Commit feb51b5a authored by Yann Boucher's avatar Yann Boucher
Browse files

Implémentation de RLEStructureWriter; Fixed #37

parent f5212a32
Pipeline #77944 passed with stages
in 17 seconds
......@@ -38,9 +38,9 @@ inline bool operator!=(const Coord& lhs, const Coord& rhs)
//! \brief Comparaison lexicographique de deux coordonnées (pour std::map et compagnie)
inline bool operator<(const Coord& lhs, const Coord& rhs)
{
if (lhs.x == rhs.x)
return lhs.y < rhs.y;
return lhs.x < rhs.x;
if (lhs.y == rhs.y)
return lhs.x < rhs.x;
return lhs.y < rhs.y;
}
//! \brief Addition de coordonnées
......
......@@ -16,7 +16,7 @@ Fichier contenant la classe Structure, représentant un ensemble de cellules con
#include <functional>
#include <map>
#include <type_traits>
#include <algorithm>
#include <limits>
#include "coord.hpp"
......@@ -49,23 +49,34 @@ public:
std::is_same<typename std::remove_reference<decltype(*begin)>::type, std::pair<const Coord, unsigned>>::value,
"Iterator value type must be std::pair<Coord, unsigned>");
m_cells.clear();
int min_x, min_y, max_x, max_y;
min_x = min_y = std::numeric_limits<int>::max();
max_x = max_y = std::numeric_limits<int>::min();
// Première passe pour calculer les positions minimales et maximales
for (auto it = begin; it != end; ++it)
{
// on ignore les entrées avec des cellules zéro, elles sont implicites (cela permet d'économiser pas mal de mémoire)
if (it->second == 0)
continue;
else
m_cells[it->first] = it->second;
if (it->first.x > max_x) max_x = it->first.x;
if (it->first.x < min_x) min_x = it->first.x;
if (it->first.y > max_y) max_y = it->first.y;
if (it->first.y < min_y) min_y = it->first.y;
}
auto minmax_width = std::minmax_element(m_cells.begin(), m_cells.end(), [](const auto& a, const auto& b)
{ return a.first.x < b.first.x; });
m_width = minmax_width.second->first.x - minmax_width.first->first.x + 1;
m_width = max_x - min_x + 1;
m_height = max_y - min_y + 1;
auto minmax_height = std::minmax_element(m_cells.begin(), m_cells.end(), [](const auto& a, const auto& b)
{ return a.first.y < b.first.y; });
m_height = minmax_height.second->first.y - minmax_width.first->first.y + 1;
m_cells.clear();
// Seconde passe, on ajoute les éléments avec une position normalisée (élément le plus à gauche <=> x=0, élément le plus en haut <=> y=0)
for (auto it = begin; it != end; ++it)
{
if (it->second == 0)
continue;
else
m_cells[it->first - Coord{min_x, min_y}] = it->second;
}
}
//! \brief Fonction vidant la structure, la laissant vide.
void clear()
......@@ -95,17 +106,19 @@ public:
: m_it(it) {}
public:
// iterator traits
using difference_type = long;
using value_type = std::pair<const Coord, unsigned>;
using pointer = const std::pair<const Coord, unsigned>*;
using reference = const std::pair<const Coord, unsigned>&;
using iterator_category = std::forward_iterator_tag;
iterator& operator++() { ++m_it; return *this;}
iterator operator++(int) {iterator retval = *this; ++(*this); return retval;}
bool operator==(iterator other) const {return m_it == other.m_it;}
bool operator!=(iterator other) const {return !(*this == other);}
std::pair<Coord, unsigned> operator*() {return *m_it;}
// iterator traits
using difference_type = long;
using value_type = std::pair<Coord, unsigned>;
using pointer = const std::pair<Coord, unsigned>*;
using reference = const std::pair<Coord, unsigned>&;
using iterator_category = std::forward_iterator_tag;
std::pair<Coord, unsigned> operator*() const {return *m_it;}
pointer operator->() const { return &*m_it;}
private:
std::map<Coord, unsigned>::const_iterator m_it;
......
......@@ -15,7 +15,7 @@ Fichier contenant la classe StructureWrite et ses filles, permettant de sauvegar
#include <string>
class Structure;
#include "structure.hpp"
//! \class StructureWrite
//! Classe abstraite permettant de sauvegarder une Structure sous différents formats de fichier.
......@@ -37,7 +37,13 @@ public:
virtual std::string save_structure(const Structure& s) const;
private:
std::string state_to_str(unsigned state);
std::string state_to_str(unsigned state) const;
std::string output_line(Structure::iterator& it, Structure::iterator end) const;
std::string output_rle_state(Structure::iterator& it, Structure::iterator end) const;
std::string rle(const std::string& val, unsigned count) const;
private:
mutable unsigned m_x, m_y;
};
//! \class JSONStructureWrite
......
......@@ -42,11 +42,18 @@ std::string RLEStructureWriter::save_structure(const Structure& s) const
result += "x = " + std::to_string(s.width()) + ", ";
result += "y = " + std::to_string(s.height()) + "\n";
// TODO : implement
return "<unimplemented>";
m_x = m_y = 0;
auto it = s.begin();
auto end = s.end();
while (it != end)
result += output_line(it, end);
result += "!";
return result;
}
std::string RLEStructureWriter::state_to_str(unsigned state)
std::string RLEStructureWriter::state_to_str(unsigned state) const
{
if (state == 0)
return "b";
......@@ -62,3 +69,49 @@ std::string RLEStructureWriter::state_to_str(unsigned state)
return std::string(1, page_char) + std::string(1, offset_char);
}
}
std::string RLEStructureWriter::output_line(Structure::iterator& it, Structure::iterator end) const
{
std::string output;
unsigned line_difference = it->first.y - m_y;
m_x = 0;
m_y = it->first.y;
output += rle("$", line_difference);
while (it != end && it->first.y == (int)m_y)
output += output_rle_state(it, end);
return output;
}
std::string RLEStructureWriter::output_rle_state(Structure::iterator& it, Structure::iterator end) const
{
unsigned zero_pad = it->first.x - m_x;
unsigned state = it->second;
unsigned len = 1;
while (std::next(it) != end &&
std::next(it)->second == state &&
std::next(it)->first.x == it->first.x + 1 &&
std::next(it)->first.y == it->first.y)
{
std::advance(it, 1);
++len;
}
m_x = it->first.x + 1;
std::advance(it, 1);
// pad until first non-null state
return rle(state_to_str(0), zero_pad) + rle(state_to_str(state), len);
}
std::string RLEStructureWriter::rle(const std::string &val, unsigned count) const
{
if (count == 0)
return "";
else if (count == 1)
return val;
else
return std::to_string(count) + val;
}
......@@ -9,9 +9,10 @@
void CellulutTests::test_structure()
{
std::vector<std::pair<Coord, unsigned>> coords;
std::vector<std::pair<Coord, unsigned>> normalized_coords;
std::vector<std::pair<Coord, unsigned>> empty;
coords.push_back({{1, 3}, 3});
coords.push_back({{-1, 2}, 1});
coords.push_back({{1, 3}, 3}); normalized_coords.push_back({{2, 1}, 3});
coords.push_back({{-1, 2}, 1}); normalized_coords.push_back({{0, 0}, 1});
{
Structure s1(coords.begin(), coords.end());
......@@ -20,8 +21,8 @@ void CellulutTests::test_structure()
QCOMPARE(s1.size(), coords.size());
QCOMPARE(s2.size(), coords.size());
QVERIFY(std::is_permutation(coords.begin(), coords.end(), s1.begin()));
QVERIFY(std::is_permutation(coords.begin(), coords.end(), s2.begin()));
QVERIFY(std::is_permutation(normalized_coords.begin(), normalized_coords.end(), s1.begin()));
QVERIFY(std::is_permutation(normalized_coords.begin(), normalized_coords.end(), s2.begin()));
QCOMPARE(s1.width(), 3);
QCOMPARE(s1.height(), 2);
......@@ -31,14 +32,13 @@ void CellulutTests::test_structure()
}
std::vector<std::pair<Coord, unsigned>> coords_with_zero;
coords_with_zero.push_back({{1, 3}, 3});
coords_with_zero.push_back({{-1, 2}, 1});
coords_with_zero = coords;
coords_with_zero.push_back({{-2, 2}, 0});
{
Structure s1(coords_with_zero.begin(), coords_with_zero.end());
QCOMPARE(s1.size(), 2); // 2 cellules non nulles, on ignore les cellules à zéro qui sont toujours implicites
QVERIFY(std::is_permutation(coords.begin(), coords.end(), s1.begin()));
QCOMPARE(s1.size(), coords.size()); // 2 cellules non nulles, on ignore les cellules à zéro qui sont toujours implicites
QVERIFY(std::is_permutation(normalized_coords.begin(), normalized_coords.end(), s1.begin()));
QCOMPARE(s1.width(), 3);
QCOMPARE(s1.height(), 2);
......
......@@ -7,12 +7,12 @@
#include "structure.hpp"
#include "structurereader.hpp"
const char* rle_glider =
static const char* rle_glider =
"#C This is a glider.\n"
"x = 3, y = 3\n"
"bo$2bo$3o!\n";
const char* rle_glider_gun =
static const char* rle_glider_gun =
"#N Gosper glider gun\n"
"#C This was the first gun discovered.\n"
"#C As its name suggests, it was discovered by Bill Gosper.\n"
......@@ -20,15 +20,15 @@ const char* rle_glider_gun =
"24bo$22bobo$12b2o6b2o12b2o$11bo3bo4b2o12b2o$2o8bo5bo3b2o$2o8bo3bob2o4b\n"
"obo$10bo5bo7bo$11bo3bo$12b2o!";
const char* rle_wireworld =
static const char* rle_wireworld =
"x = 6, y = 7, rule = WireWorld\n"
".A$.C$4C$C2.3C$4C$.C$.A!";
const char* json_test_1 =
static const char* json_test_1 =
"{\n"
" \"cells\" : [\n"
" { \"x\" : 1, \"y\" : 2, \"state\" : 1},\n"
" { \"x\" : 1, \"y\" : 3, \"state\" : 3}\n"
" { \"x\" : 0, \"y\" : 0, \"state\" : 1},\n"
" { \"x\" : 2, \"y\" : 3, \"state\" : 3}\n"
" ]\n"
"}";
......@@ -97,8 +97,8 @@ void CellulutTests::test_json_structurereader()
try {
Structure stru = rle.read_structure();
QCOMPARE(stru.size(), 2);
QVERIFY(std::count(stru.begin(), stru.end(), std::make_pair<Coord, unsigned>(Coord{1, 2}, 1)) == 1);
QVERIFY(std::count(stru.begin(), stru.end(), std::make_pair<Coord, unsigned>(Coord{1, 3}, 3)) == 1);
QVERIFY(std::count(stru.begin(), stru.end(), std::make_pair<Coord, unsigned>(Coord{0, 0}, 1)) == 1);
QVERIFY(std::count(stru.begin(), stru.end(), std::make_pair<Coord, unsigned>(Coord{2, 3}, 3)) == 1);
}
catch (const std::exception& ex)
{
......
......@@ -8,16 +8,60 @@
#include "structurewriter.hpp"
#include "structurereader.hpp"
static const char* rle_glider =
"x = 3, y = 3\n"
"bo$2bo$3o!";
static const char* rle_glider_gun =
"x = 36, y = 9\n"
"24bo$22bobo$12b2o6b2o12b2o$11bo3bo4b2o12b2o$2o8bo5bo3b2o$2o8bo3bob2o4b"
"obo$10bo5bo7bo$11bo3bo$12b2o!";
void CellulutTests::test_rle_structurewriter()
{
// TODO : écrire ces tests
try
{
Structure stru;
RLEStructureReader reader(rle_glider);
stru = reader.read_structure();
RLEStructureWriter writer;
auto out = writer.save_structure(stru);
QCOMPARE(out, std::string(rle_glider));
}
catch (const std::exception& ex)
{
fprintf(stderr, "Error is %s\n", ex.what());
QFAIL("Exception thrown");
}
try
{
Structure stru;
RLEStructureReader reader(rle_glider_gun);
stru = reader.read_structure();
RLEStructureWriter writer;
auto out = writer.save_structure(stru);
QCOMPARE(out, std::string(rle_glider_gun));
}
catch (const std::exception& ex)
{
fprintf(stderr, "Error is %s\n", ex.what());
QFAIL("Exception thrown");
}
}
void CellulutTests::test_json_structurewriter()
{
std::vector<std::pair<Coord, unsigned>> coords;
coords.push_back({{1, 2}, 3});
coords.push_back({{-1, 2}, 1});
coords.push_back({{0, 0}, 3});
coords.push_back({{1, 2}, 1});
coords.push_back({{5, 8}, 5});
try
{
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment