Skip to content
GitLab
Menu
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
LO21_Pin_Noir_Boucher_Bouri_Detree
CellulutLO21
Commits
4617dc50
Commit
4617dc50
authored
Apr 21, 2021
by
Yann Boucher
Browse files
Implemented structure readers for Golly's RLE format and a custom JSON format, fixed a few tests
parent
f60086b4
Pipeline
#76856
passed with stages
in 17 seconds
Changes
9
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
include/coord.hpp
View file @
4617dc50
...
...
@@ -35,4 +35,12 @@ inline bool operator!=(const Coord& lhs, const Coord& rhs)
return
!
(
lhs
==
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
;
}
#endif // COORD_HPP
include/structure.hpp
View file @
4617dc50
...
...
@@ -14,7 +14,7 @@ Fichier contenant la classe Structure, représentant un ensemble de cellules con
#define STRUCTURE_HPP
#include
<functional>
#include
<
vector
>
#include
<
map
>
#include
<type_traits>
#include
"coord.hpp"
...
...
@@ -25,6 +25,7 @@ Fichier contenant la classe Structure, représentant un ensemble de cellules con
Cette classe permet de représenter un ensemble de cellules constituant une structure, comme un oscillateur ou un glider.
**/
// TODO : contrainte d'unicité (utiliser std::unordered_map?)
class
Structure
{
public:
...
...
@@ -44,12 +45,19 @@ public:
template
<
typename
It
>
void
load
(
It
begin
,
It
end
)
{
static_assert
(
std
::
is_same
<
typename
std
::
remove_reference
<
decltype
(
*
begin
)
>::
type
,
std
::
pair
<
Coord
,
int
>>::
value
,
"Iterator value type must be std::pair<Coord, int>"
);
static_assert
(
std
::
is_same
<
typename
std
::
remove_reference
<
decltype
(
*
begin
)
>::
type
,
std
::
pair
<
Coord
,
unsigned
>>::
value
||
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
();
// back_inserter permet de redimensionner automatiquement m_cells lors de la copie via les itérateurs begin et end
std
::
copy
(
begin
,
end
,
std
::
back_inserter
(
m_cells
));
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
;
}
}
//! \brief retourne le nombre de cellules présentes dans la structure
...
...
@@ -62,7 +70,7 @@ public:
{
friend
class
Structure
;
private:
iterator
(
std
::
vector
<
std
::
pair
<
Coord
,
int
>
>::
const_iterator
it
)
iterator
(
std
::
map
<
Coord
,
unsigned
>::
const_iterator
it
)
:
m_it
(
it
)
{}
public:
...
...
@@ -70,24 +78,26 @@ public:
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
,
int
>
operator
*
()
{
return
*
m_it
;}
std
::
pair
<
Coord
,
unsigned
>
operator
*
()
{
return
*
m_it
;}
// iterator traits
using
difference_type
=
long
;
using
value_type
=
std
::
pair
<
Coord
,
int
>
;
using
pointer
=
const
std
::
pair
<
Coord
,
int
>*
;
using
reference
=
const
std
::
pair
<
Coord
,
int
>&
;
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
;
private:
std
::
vector
<
std
::
pair
<
Coord
,
int
>
>::
const_iterator
m_it
;
std
::
map
<
Coord
,
unsigned
>::
const_iterator
m_it
;
};
//! \brief Retourne un itérateur vers le début de la liste de cellules.
//! \post Chaque élément itérable est de coordonnée unique.
iterator
begin
()
const
{
return
iterator
(
m_cells
.
begin
());
}
//! \brief Retourne un itérateur vers la fin de la liste de cellules.
//! \post Chaque élément itérable est de coordonnée unique.
iterator
end
()
const
{
return
iterator
(
m_cells
.
end
());
}
private:
std
::
vector
<
std
::
pair
<
Coord
,
int
>
>
m_cells
;
std
::
map
<
Coord
,
unsigned
>
m_cells
;
};
#endif // STRUCTURE_HPP
include/structurereader.hpp
0 → 100644
View file @
4617dc50
#ifndef STRUCTUREREADER_HPP
#define STRUCTUREREADER_HPP
#include
<string>
#include
<exception>
class
Structure
;
// TODO : documenter ce fichier
// TODO : adapter cettre classe d'Exception en la faisant hériter à notre propre classe d'Exception
class
StructureReaderException
:
public
std
::
exception
{
public:
StructureReaderException
(
const
std
::
string
&
what
)
:
m_what
(
what
)
{}
const
char
*
what
()
const
noexcept
{
return
m_what
.
c_str
();
}
private:
const
std
::
string
m_what
;
};
class
StructureReader
{
public:
StructureReader
(
const
std
::
string
&
in_data
)
:
m_idx
(
0
),
m_data
(
in_data
)
{}
virtual
Structure
read_structure
()
=
0
;
protected:
char
peek
()
const
;
char
read
();
bool
eof
()
const
;
void
expect
(
const
std
::
string
&
str
);
bool
accept
(
const
std
::
string
&
str
);
std
::
string
read_word
();
std
::
string
read_line
();
std
::
string
data_left
()
const
;
int
read_int
();
void
read_white
();
private:
size_t
m_idx
;
const
std
::
string
&
m_data
;
};
class
RLEStructureReader
:
public
StructureReader
{
public:
RLEStructureReader
(
const
std
::
string
&
in_data
)
:
StructureReader
(
in_data
)
{}
virtual
Structure
read_structure
();
private:
unsigned
read_state
();
};
class
JSONStructureReader
:
public
StructureReader
{
public:
JSONStructureReader
(
const
std
::
string
&
in_data
)
:
StructureReader
(
in_data
)
{}
virtual
Structure
read_structure
();
};
#endif // STRUCTUREREADER_HPP
src/src.pro
View file @
4617dc50
...
...
@@ -14,7 +14,8 @@ INCLUDEPATH += ../include
SOURCES
+=
\
main
.
cpp
\
propertyvisitors
.
cpp
\
neighborhood
.
cpp
neighborhood
.
cpp
\
structurereader
.
cpp
HEADERS
+=
\
..
/
include
/
coord
.
hpp
\
...
...
@@ -23,7 +24,8 @@ HEADERS += \
..
/
include
/
propertyvisitors
.
hpp
\
..
/
include
/
structure
.
hpp
\
..
/
include
/
neighborhood
.
hpp
\
..
/
include
/
factory
.
hpp
..
/
include
/
factory
.
hpp
\
..
/
include
/
structurereader
.
hpp
#
Default
rules
for
deployment
.
...
...
src/structurereader.cpp
0 → 100644
View file @
4617dc50
#include
"structurereader.hpp"
#include
<QByteArray>
#include
<QJsonDocument>
#include
<QJsonObject>
#include
<QJsonArray>
#include
"structure.hpp"
char
StructureReader
::
peek
()
const
{
if
(
m_idx
>=
m_data
.
size
())
throw
StructureReaderException
(
"Fichier invalide, la lecture a dépassé la fin du fichier."
);
return
m_data
[
m_idx
];
}
char
StructureReader
::
read
()
{
char
val
=
peek
();
++
m_idx
;
return
val
;
}
bool
StructureReader
::
eof
()
const
{
return
m_idx
>=
m_data
.
size
();
}
void
StructureReader
::
expect
(
const
std
::
string
&
str
)
{
read_white
();
if
(
m_data
.
substr
(
m_idx
,
str
.
size
())
!=
str
)
throw
StructureReaderException
(
"Valeur attendue : "
+
str
);
m_idx
+=
str
.
size
();
read_white
();
}
bool
StructureReader
::
accept
(
const
std
::
string
&
str
)
{
read_white
();
if
(
m_data
.
substr
(
m_idx
,
str
.
size
())
==
str
)
{
m_idx
+=
str
.
size
();
read_white
();
return
true
;
}
return
false
;
}
std
::
string
StructureReader
::
read_word
()
{
std
::
string
str
;
while
(
m_idx
<
m_data
.
size
()
&&
!
isspace
(
m_data
[
m_idx
]))
str
+=
m_data
[
m_idx
++
];
return
str
;
}
std
::
string
StructureReader
::
read_line
()
{
std
::
string
str
;
while
(
m_idx
<
m_data
.
size
()
&&
m_data
[
m_idx
]
!=
'\n'
)
str
+=
m_data
[
m_idx
++
];
read_white
();
return
str
;
}
std
::
string
StructureReader
::
data_left
()
const
{
return
m_data
.
substr
(
m_idx
);
}
int
StructureReader
::
read_int
()
{
read_white
();
size_t
cars_read
;
int
val
;
try
{
val
=
std
::
stoi
(
&
m_data
.
data
()[
m_idx
],
&
cars_read
,
10
);
}
catch
(
std
::
exception
&
e
)
{
// TODO
throw
StructureReaderException
(
"La valeur lue n'est pas un entier !"
);
}
m_idx
+=
cars_read
;
read_white
();
return
val
;
}
void
StructureReader
::
read_white
()
{
while
(
m_idx
<
m_data
.
size
()
&&
isspace
(
peek
()))
++
m_idx
;
}
// ref : http://golly.sourceforge.net/Help/formats.html#rle
unsigned
RLEStructureReader
::
read_state
()
{
char
char_1
=
read
();
if
(
char_1
==
'b'
||
char_1
==
'B'
||
char_1
==
'.'
)
return
0
;
else
if
(
char_1
==
'o'
)
return
1
;
else
if
(
char_1
>=
'A'
&&
char_1
<=
'X'
)
return
char_1
-
'A'
+
1
;
else
if
(
islower
(
char_1
)
&&
isalpha
(
char_1
)
&&
char_1
>=
'p'
&&
char_1
<=
'q'
)
{
char
char_2
=
read
();
unsigned
offset
=
(
char_1
-
'p'
)
*
24
+
49
;
return
offset
+
(
char_2
-
'A'
);
}
// unknown chars are zero
return
0
;
}
// ref : http://golly.sourceforge.net/Help/formats.html#rle
Structure
RLEStructureReader
::
read_structure
()
{
// ignore first comment lines
while
(
accept
(
"#"
))
read_line
();
// First Line
{
expect
(
"x"
);
expect
(
"="
);
int
x
=
read_int
();
expect
(
","
);
expect
(
"y"
);
expect
(
"="
);
int
y
=
read_int
();
std
::
string
rule
=
""
;
if
(
accept
(
","
)
&&
accept
(
"rule"
))
{
expect
(
"="
);
rule
=
read_word
();
}
(
void
)
x
;
(
void
)
y
;
// ignore warnings
}
std
::
vector
<
std
::
pair
<
Coord
,
unsigned
>>
data
;
int
x
=
0
,
y
=
0
;
while
(
!
eof
())
{
// comment line
if
(
accept
(
"#"
))
{
//char tag = read();
std
::
string
line
=
read_line
();
}
// end of structure line
else
if
(
accept
(
"$"
))
{
x
=
0
;
++
y
;
}
// end of data
else
if
(
accept
(
"!"
))
{
break
;
}
// structure
else
{
unsigned
state
=
0
;
unsigned
run_count
=
1
;
// <run_count> is > 1
if
(
isdigit
(
peek
()))
run_count
=
read_int
();
state
=
read_state
();
for
(
unsigned
i
=
0
;
i
<
run_count
;
++
i
)
{
data
.
push_back
({{
x
,
y
},
state
});
++
x
;
}
}
}
return
Structure
(
data
.
begin
(),
data
.
end
());
}
Structure
JSONStructureReader
::
read_structure
()
{
std
::
vector
<
std
::
pair
<
Coord
,
unsigned
>>
data
;
QByteArray
byteArray
=
QByteArray
::
fromStdString
(
data_left
());
QJsonParseError
parseError
;
QJsonDocument
jsonDoc
=
QJsonDocument
::
fromJson
(
byteArray
,
&
parseError
);
if
(
parseError
.
error
!=
QJsonParseError
::
NoError
)
{
throw
StructureReaderException
(
"Erreur de parsing JSON à "
+
std
::
to_string
(
parseError
.
offset
)
+
":"
+
parseError
.
errorString
().
toStdString
());
}
QJsonObject
root
=
jsonDoc
.
object
();
if
(
!
root
.
contains
(
"cells"
))
throw
StructureReaderException
(
"Pas de champ 'cells' !"
);
if
(
!
root
[
"cells"
].
isArray
())
throw
StructureReaderException
(
"'cells' doit être un array."
);
for
(
const
auto
&
entry
:
root
[
"cells"
].
toArray
())
{
const
char
*
msg
=
"Chaque entrée de 'cells' doit être un objet contenant des entiers x, y, et state."
;
if
(
!
entry
.
isObject
())
throw
StructureReaderException
(
msg
);
QJsonObject
val
=
entry
.
toObject
();
if
(
!
val
.
contains
(
"x"
)
||
!
val
.
contains
(
"y"
)
||
!
val
.
contains
(
"state"
))
throw
StructureReaderException
(
msg
);
if
(
!
val
[
"x"
].
isDouble
()
||
!
val
[
"y"
].
isDouble
()
||
!
val
[
"state"
].
isDouble
())
throw
StructureReaderException
(
msg
);
data
.
push_back
({{
val
[
"x"
].
toInt
(),
val
[
"y"
].
toInt
()},
val
[
"state"
].
toInt
()});
}
return
Structure
(
data
.
begin
(),
data
.
end
());
}
tests/cellulut_tests.hpp
View file @
4617dc50
...
...
@@ -12,6 +12,9 @@ private slots:
void
test_structure
();
void
test_factory
();
void
test_coord
();
void
test_rle_structurereader
();
void
test_json_structurereader
();
};
#endif // CELLULUT_TESTS_HPP
tests/structure_test.cpp
View file @
4617dc50
...
...
@@ -8,9 +8,9 @@
void
CellulutTests
::
test_structure
()
{
std
::
vector
<
std
::
pair
<
Coord
,
int
>>
coords
;
std
::
vector
<
std
::
pair
<
Coord
,
int
>>
empty
;
coords
.
push_back
({{
1
,
2
},
0
});
std
::
vector
<
std
::
pair
<
Coord
,
unsigned
>>
coords
;
std
::
vector
<
std
::
pair
<
Coord
,
unsigned
>>
empty
;
coords
.
push_back
({{
1
,
2
},
3
});
coords
.
push_back
({{
-
1
,
2
},
1
});
{
...
...
@@ -19,18 +19,23 @@ void CellulutTests::test_structure()
s2
.
load
(
coords
.
begin
(),
coords
.
end
());
QCOMPARE
(
s1
.
size
(),
coords
.
size
());
QCOMPARE
(
s2
.
size
(),
coords
.
size
());
QVERIFY
(
std
::
equal
(
coords
.
begin
(),
coords
.
end
(),
s1
.
begin
()));
QVERIFY
(
std
::
equal
(
coords
.
begin
(),
coords
.
end
(),
s2
.
begin
()));
QVERIFY
(
std
::
is_permutation
(
coords
.
begin
(),
coords
.
end
(),
s1
.
begin
()));
QVERIFY
(
std
::
is_permutation
(
coords
.
begin
(),
coords
.
end
(),
s2
.
begin
()));
s1
.
load
(
empty
.
begin
(),
empty
.
end
());
QVERIFY
(
s1
.
size
()
==
0
);
}
int
cnt
=
0
;
for
(
auto
val
:
s2
)
{
QVERIFY
(
val
==
coords
[
cnt
]);
++
cnt
;
}
std
::
vector
<
std
::
pair
<
Coord
,
unsigned
>>
coords_with_zero
;
coords_with_zero
.
push_back
({{
1
,
2
},
3
});
coords_with_zero
.
push_back
({{
-
1
,
2
},
1
});
coords_with_zero
.
push_back
({{
-
1
,
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
()));
}
}
tests/structurereader_tests.cpp
0 → 100644
View file @
4617dc50
#include
<QtTest/QtTest>
#include
<algorithm>
#include
"cellulut_tests.hpp"
#include
"structure.hpp"
#include
"structurereader.hpp"
const
char
*
rle_glider
=
"#C This is a glider.
\n
"
"x = 3, y = 3
\n
"
"bo$2bo$3o!
\n
"
;
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
"
" x = 36, y = 9, rule = B3/S23
\n
"
"24bo$22bobo$12b2o6b2o12b2o$11bo3bo4b2o12b2o$2o8bo5bo3b2o$2o8bo3bob2o4b
\n
"
"obo$10bo5bo7bo$11bo3bo$12b2o!"
;
const
char
*
rle_wireworld
=
"x = 6, y = 7, rule = WireWorld
\n
"
".A$.C$4C$C2.3C$4C$.C$.A!"
;
const
char
*
json_test_1
=
"{
\n
"
"
\"
cells
\"
: [
\n
"
" {
\"
x
\"
: 1,
\"
y
\"
: 2,
\"
state
\"
: 1},
\n
"
" {
\"
x
\"
: 1,
\"
y
\"
: 3,
\"
state
\"
: 3}
\n
"
" ]
\n
"
"}"
;
void
CellulutTests
::
test_rle_structurereader
()
{
{
RLEStructureReader
rle
(
rle_glider
);
try
{
Structure
stru
=
rle
.
read_structure
();
QCOMPARE
(
stru
.
size
(),
5
);
QVERIFY
(
std
::
count
(
stru
.
begin
(),
stru
.
end
(),
std
::
make_pair
<
Coord
,
unsigned
>
(
Coord
{
1
,
0
},
1
))
==
1
);
}
catch
(
const
std
::
exception
&
ex
)
{
fprintf
(
stderr
,
"Error is %s
\n
"
,
ex
.
what
());
throw
;
}
}
{
RLEStructureReader
rle
(
rle_glider_gun
);
try
{
Structure
stru
=
rle
.
read_structure
();
QCOMPARE
(
stru
.
size
(),
36
);
}
catch
(
const
std
::
exception
&
ex
)
{
fprintf
(
stderr
,
"Error is %s
\n
"
,
ex
.
what
());
throw
;
}
}
{
RLEStructureReader
rle
(
rle_wireworld
);
try
{
Structure
stru
=
rle
.
read_structure
();
QCOMPARE
(
stru
.
size
(),
16
);
std
::
map
<
int
,
int
>
state_count
;
for
(
auto
pair
:
stru
)
{
state_count
[
pair
.
second
]
++
;
}
QCOMPARE
(
state_count
[
1
],
2
);
QCOMPARE
(
state_count
[
2
],
0
);
QCOMPARE
(
state_count
[
3
],
14
);
}
catch
(
const
std
::
exception
&
ex
)
{
fprintf
(
stderr
,
"Error is %s
\n
"
,
ex
.
what
());
throw
;
}
}
}
void
CellulutTests
::
test_json_structurereader
()
{
{
JSONStructureReader
rle
(
json_test_1
);
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
);
}
catch
(
const
std
::
exception
&
ex
)
{
fprintf
(
stderr
,
"Error is %s
\n
"
,
ex
.
what
());
throw
;
}
}
}
tests/tests.pro
View file @
4617dc50
...
...
@@ -15,12 +15,14 @@ INCLUDEPATH += ../include
SOURCES
+=
\
..
/
src
/
propertyvisitors
.
cpp
\
..
/
src
/
structurereader
.
cpp
\
coord_tests
.
cpp
\
factory_tests
.
cpp
\
property_test
.
cpp
\
propertyvisitors_test
.
cpp
\
cellulut_tests
.
cpp
\
structure_test
.
cpp
structure_test
.
cpp
\
structurereader_tests
.
cpp
HEADERS
+=
\
cellulut_tests
.
hpp
...
...
Yann Boucher
@yboucher
mentioned in issue
#14 (closed)
·
Apr 21, 2021
mentioned in issue
#14 (closed)
mentioned in issue #14
Toggle commit list
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment