22
33#include " input_event.hpp"
44#include " random.hpp"
5+ #include " tetrion.hpp"
6+ #include " tetrion_snapshot.hpp"
57#include " types.hpp"
68#include " utils.hpp"
79#include < filesystem>
1416struct RecordingError : public std ::exception { };
1517
1618struct Recording {
19+ protected:
20+ enum class MagicByte : u8 {
21+ Record = 42 ,
22+ Snapshot = 43 ,
23+ };
24+
1725public:
1826 struct TetrionHeader final {
1927 Random::Seed seed;
@@ -34,6 +42,10 @@ struct Recording {
3442 explicit Recording (std::vector<TetrionHeader> tetrion_headers) : m_tetrion_headers{ std::move (tetrion_headers) } { }
3543
3644public:
45+ Recording (const Recording&) = delete ;
46+ Recording (Recording&&) = delete ;
47+ Recording& operator =(const Recording&) = delete ;
48+ Recording& operator =(Recording&&) = delete ;
3749 virtual ~Recording () = default ;
3850
3951 [[nodiscard]] const std::vector<TetrionHeader>& tetrion_headers () const {
@@ -43,6 +55,7 @@ struct Recording {
4355
4456struct RecordingReader : public Recording {
4557 std::vector<Record> m_records;
58+ std::vector<TetrionSnapshot> m_snapshots;
4659
4760public:
4861 explicit RecordingReader (const std::filesystem::path& path) {
@@ -68,16 +81,33 @@ struct RecordingReader : public Recording {
6881 }
6982
7083 while (true ) {
71- const auto record = read_record_from_file (file);
72- if (not record.has_value ()) {
73- if (record.error () == ReadError::EndOfFile) {
74- // finished reading
75- break ;
84+ const auto magic_byte = read_integral_from_file<std::underlying_type_t <MagicByte>>(file);
85+ if (not magic_byte.has_value ()) {
86+ if (magic_byte.error () == ReadError::InvalidStream) {
87+ spdlog::error (" unable to read magic byte" );
88+ throw RecordingError{};
89+ }
90+ break ;
91+ }
92+ if (*magic_byte == std::to_underlying (MagicByte::Record)) {
93+ const auto record = read_record_from_file (file);
94+ if (not record.has_value ()) {
95+ if (record.error () == ReadError::EndOfFile) {
96+ // finished reading
97+ break ;
98+ }
99+ spdlog::error (" invalid record while reading recorded game" );
100+ throw RecordingError{};
76101 }
77- spdlog::error (" invalid record while reading recorded game" );
102+ m_records.push_back (*record);
103+ } else if (*magic_byte == std::to_underlying (MagicByte::Snapshot)) {
104+ auto snapshot = TetrionSnapshot{ file }; // todo: handle exception
105+ m_snapshots.push_back (std::move (snapshot));
106+ spdlog::info (" read snapshot" );
107+ } else {
108+ spdlog::error (" invalid magic byte: {}" , static_cast <int >(*magic_byte));
78109 throw RecordingError{};
79110 }
80- m_records.push_back (*record);
81111 }
82112 }
83113
@@ -97,6 +127,10 @@ struct RecordingReader : public Recording {
97127 return m_records.cend ();
98128 }
99129
130+ [[nodiscard]] const std::vector<TetrionSnapshot>& snapshots () const {
131+ return m_snapshots;
132+ }
133+
100134private:
101135 enum class ReadError {
102136 EndOfFile,
@@ -192,11 +226,20 @@ struct RecordingWriter : public Recording {
192226
193227 void add_event (const u8 tetrion_index, const u64 simulation_step_index, const InputEvent event) {
194228 assert (tetrion_index < m_tetrion_headers.size ());
229+ write (std::to_underlying (MagicByte::Record));
195230 write (tetrion_index);
196231 write (simulation_step_index);
197232 write (static_cast <u8 >(event));
198233 }
199234
235+ void add_snapshot (const u8 tetrion_index, const u64 simulation_step_index, const Tetrion& tetrion) {
236+ write (std::to_underlying (MagicByte::Snapshot));
237+ const auto snapshot = TetrionSnapshot{ tetrion_index, tetrion.level (), tetrion.score (),
238+ tetrion.lines_cleared (), simulation_step_index, tetrion.mino_stack () };
239+ const auto bytes = snapshot.to_bytes ();
240+ m_output_file.write (bytes.data (), static_cast <std::streamsize>(bytes.size ()));
241+ }
242+
200243private:
201244 static void write_integral_to_file (std::ofstream& file, const std::integral auto data) {
202245 if (not file) {
0 commit comments