Monopoly — Portage moderne 0.1.0
Portage moderne C/C++23 du Monopoly de 1999
Loading...
Searching...
No Matches
AISaveState.cpp
Go to the documentation of this file.
1#include "AISaveState.hpp"
2
3#include <algorithm>
4#include <bit>
5#include <limits>
6#include <utility>
7
9{
10 namespace
11 {
12 constexpr std::array<std::uint8_t, 4> Magic{'A', 'I', 'S', 'V'};
13 constexpr std::uint32_t Version = 1;
14
15 struct Writer
16 {
17 std::vector<std::uint8_t> data{};
18
19 void u8(std::uint8_t value)
20 {
21 data.push_back(value);
22 }
23
24 void u32(std::uint32_t value)
25 {
26 for (int shift = 0; shift < 32; shift += 8)
27 data.push_back(static_cast<std::uint8_t>(value >> shift));
28 }
29
30 void u64(std::uint64_t value)
31 {
32 for (int shift = 0; shift < 64; shift += 8)
33 data.push_back(static_cast<std::uint8_t>(value >> shift));
34 }
35
36 void i32(std::int32_t value)
37 {
38 u32(std::bit_cast<std::uint32_t>(value));
39 }
40
41 void i64(std::int64_t value)
42 {
43 u64(std::bit_cast<std::uint64_t>(value));
44 }
45
46 void f64(double value)
47 {
48 u64(std::bit_cast<std::uint64_t>(value));
49 }
50 };
51
52 struct Reader
53 {
54 std::span<const std::uint8_t> data{};
55 std::size_t offset{};
56
57 bool u8(std::uint8_t& value) noexcept
58 {
59 if (offset >= data.size())
60 return false;
61 value = data[offset++];
62 return true;
63 }
64
65 bool u32(std::uint32_t& value) noexcept
66 {
67 if (data.size() - offset < 4)
68 return false;
69 value = 0;
70 for (int shift = 0; shift < 32; shift += 8)
71 value |= static_cast<std::uint32_t>(data[offset++]) << shift;
72 return true;
73 }
74
75 bool u64(std::uint64_t& value) noexcept
76 {
77 if (data.size() - offset < 8)
78 return false;
79 value = 0;
80 for (int shift = 0; shift < 64; shift += 8)
81 value |= static_cast<std::uint64_t>(data[offset++]) << shift;
82 return true;
83 }
84
85 bool i32(std::int32_t& value) noexcept
86 {
87 std::uint32_t raw{};
88 if (!u32(raw))
89 return false;
90 value = std::bit_cast<std::int32_t>(raw);
91 return true;
92 }
93
94 bool i64(std::int64_t& value) noexcept
95 {
96 std::uint64_t raw{};
97 if (!u64(raw))
98 return false;
99 value = std::bit_cast<std::int64_t>(raw);
100 return true;
101 }
102
103 bool f64(double& value) noexcept
104 {
105 std::uint64_t raw{};
106 if (!u64(raw))
107 return false;
108 value = std::bit_cast<double>(raw);
109 return true;
110 }
111
113 {
114 return offset == data.size();
115 }
116 };
117
118 template <std::size_t N>
119 void writeDoubles(
120 Writer& writer,
121 const std::array<double, N>& values)
122 {
123 for (double value : values)
125 }
126
127 template <std::size_t N>
128 bool readDoubles(
129 Reader& reader,
130 std::array<double, N>& values) noexcept
131 {
132 for (double& value : values)
133 {
134 if (!reader.f64(value))
135 return false;
136 }
137 return true;
138 }
139
140 [[nodiscard]] bool validClassification(std::uint8_t value) noexcept
141 {
142 return value <= 6 || value == 10 || value == 11;
143 }
144
145 bool writeProfile(Writer& writer, const profile::Profile& value)
146 {
147 if (value.maxTrades > std::numeric_limits<std::uint32_t>::max())
148 return false;
149
150 writeDoubles(writer, value.playerAttitude);
151 writer.f64(value.attitudeLostForRejectedTrade);
152 writer.f64(value.attitudeChangeTradeObserving);
153 writer.f64(value.attitudeTickChange);
154 writer.f64(value.neutralAttitude);
155 writer.i64(value.timeForgetTrade);
156 writer.u32(static_cast<std::uint32_t>(value.maxTrades));
157 writer.i32(value.tradeCounterLimit);
158 writer.f64(value.tradeCounterProbability);
159 writer.i64(value.minCashOnHand);
160 writer.u8(static_cast<std::uint8_t>(value.cashStrategy));
161 writer.u8(value.housingPurchaseStrategy);
162 writer.f64(value.buyingStageCashMultiplier);
163 writer.f64(value.noMonopolyStageCashMultiplier);
164 writer.f64(value.cashLiquidAssetsDependence);
165 writer.f64(value.monopolyNotOwnedStageCashMultiplier);
166 writer.f64(value.monopolyOwnedStageCashMultiplier);
167 writer.f64(value.chancesFactor);
168 writer.f64(value.chancesThreshold);
169 writer.f64(value.cashFactor);
170 writeDoubles(writer, value.worthFactors.property);
171 writeDoubles(writer, value.worthFactors.cashCow);
172 writer.f64(value.tradeImportanceFactor);
173 writer.f64(value.minEvaluationThreshold);
174 writer.f64(value.minGiveMonopolyEvaluation);
175
176 const auto& importance = value.propertyImportance;
177 writer.f64(importance.monopolyReceivedImportance);
178 writer.f64(importance.givingMonopolyImportance);
179 writer.f64(importance.negativePropertyImportanceChangeMultiplier);
180 writer.f64(importance.propertyAllowTradeImportance);
181 writer.f64(importance.propertyAllowMoreTradeImportance);
182 writer.f64(importance.propertyOneUnownedImportance);
183 writer.f64(importance.propertyTwoUnownedImportance);
184 writeDoubles(writer, importance.railroadImportance);
185 writeDoubles(writer, importance.utilityImportance);
186 writeDoubles(writer, importance.monopolyVetoImportance);
187 writer.f64(value.balticReceivedImportance);
188 writer.f64(value.proposeTradeProbability);
189 writer.f64(value.monopolyTradeProbability);
190 writer.f64(value.proposeMonopolyGiveAwayProbability);
191 writer.i32(value.maxHousesPerSquareForGiveAway);
192 writer.i32(value.minActionWait);
193 writer.i32(value.maxActionWait);
194 writer.i32(value.waitForTradeConsider);
195 writer.u64(value.patience);
196 writer.i32(value.numberTimesAllowPropertyTrade);
197 writer.i32(value.turnsToForgetPropertyTrade);
198 writer.f64(value.minEvaluationIfFedUp);
199 writer.f64(value.monopolySuicideFactor);
200 writer.f64(value.lowestPropertyImportanceForCounter);
201 writer.f64(value.minimumNonmonopolyTradeAttitude);
202
203 for (const auto& trade : value.whatToTrade)
204 {
205 writer.u8(static_cast<std::uint8_t>(trade.giveMonopoly));
206 writer.u8(trade.giveGroupTrades);
207 writer.u8(trade.giveCashCows);
208 writer.u8(trade.giveJunk);
209 writer.f64(trade.cashMultiplier);
210 }
211
212 return true;
213 }
214
215 bool readProfile(Reader& reader, profile::Profile& value) noexcept
216 {
217 std::uint32_t maxTrades{};
218 std::int32_t intValue{};
219 std::uint8_t enumValue{};
220
221 if (!readDoubles(reader, value.playerAttitude) ||
222 !reader.f64(value.attitudeLostForRejectedTrade) ||
223 !reader.f64(value.attitudeChangeTradeObserving) ||
224 !reader.f64(value.attitudeTickChange) ||
225 !reader.f64(value.neutralAttitude) ||
226 !reader.i64(value.timeForgetTrade) ||
227 !reader.u32(maxTrades) ||
228 !reader.i32(intValue))
229 return false;
230
231 value.maxTrades = maxTrades;
232 value.tradeCounterLimit = intValue;
233
234 if (!reader.f64(value.tradeCounterProbability) ||
235 !reader.i64(value.minCashOnHand) ||
236 !reader.u8(enumValue) ||
237 enumValue >= static_cast<std::uint8_t>(decision::CashStrategy::Count))
238 return false;
239 value.cashStrategy = static_cast<decision::CashStrategy>(enumValue);
240
241 if (!reader.u8(value.housingPurchaseStrategy) ||
242 !reader.f64(value.buyingStageCashMultiplier) ||
243 !reader.f64(value.noMonopolyStageCashMultiplier) ||
244 !reader.f64(value.cashLiquidAssetsDependence) ||
245 !reader.f64(value.monopolyNotOwnedStageCashMultiplier) ||
246 !reader.f64(value.monopolyOwnedStageCashMultiplier) ||
247 !reader.f64(value.chancesFactor) ||
248 !reader.f64(value.chancesThreshold) ||
249 !reader.f64(value.cashFactor) ||
250 !readDoubles(reader, value.worthFactors.property) ||
251 !readDoubles(reader, value.worthFactors.cashCow) ||
252 !reader.f64(value.tradeImportanceFactor) ||
253 !reader.f64(value.minEvaluationThreshold) ||
254 !reader.f64(value.minGiveMonopolyEvaluation))
255 return false;
256
257 auto& importance = value.propertyImportance;
258 if (!reader.f64(importance.monopolyReceivedImportance) ||
259 !reader.f64(importance.givingMonopolyImportance) ||
260 !reader.f64(importance.negativePropertyImportanceChangeMultiplier) ||
261 !reader.f64(importance.propertyAllowTradeImportance) ||
262 !reader.f64(importance.propertyAllowMoreTradeImportance) ||
263 !reader.f64(importance.propertyOneUnownedImportance) ||
264 !reader.f64(importance.propertyTwoUnownedImportance) ||
265 !readDoubles(reader, importance.railroadImportance) ||
266 !readDoubles(reader, importance.utilityImportance) ||
267 !readDoubles(reader, importance.monopolyVetoImportance) ||
268 !reader.f64(value.balticReceivedImportance) ||
269 !reader.f64(value.proposeTradeProbability) ||
270 !reader.f64(value.monopolyTradeProbability) ||
271 !reader.f64(value.proposeMonopolyGiveAwayProbability))
272 return false;
273
274 if (!reader.i32(intValue))
275 return false;
276 value.maxHousesPerSquareForGiveAway = intValue;
277 if (!reader.i32(intValue))
278 return false;
279 value.minActionWait = intValue;
280 if (!reader.i32(intValue))
281 return false;
282 value.maxActionWait = intValue;
283
284 if (!reader.i32(intValue))
285 return false;
286 value.waitForTradeConsider = intValue;
287 if (!reader.u64(value.patience))
288 return false;
289 if (!reader.i32(intValue))
290 return false;
291 value.numberTimesAllowPropertyTrade = intValue;
292 if (!reader.i32(intValue))
293 return false;
294 value.turnsToForgetPropertyTrade = intValue;
295
296 if (!reader.f64(value.minEvaluationIfFedUp) ||
297 !reader.f64(value.monopolySuicideFactor) ||
298 !reader.f64(value.lowestPropertyImportanceForCounter) ||
299 !reader.f64(value.minimumNonmonopolyTradeAttitude))
300 return false;
301
302 for (auto& trade : value.whatToTrade)
303 {
304 if (!reader.u8(enumValue) || !validClassification(enumValue))
305 return false;
306 trade.giveMonopoly =
308 if (!reader.u8(trade.giveGroupTrades) ||
309 !reader.u8(trade.giveCashCows) ||
310 !reader.u8(trade.giveJunk) ||
311 !reader.f64(trade.cashMultiplier))
312 return false;
313 }
314
315 return true;
316 }
317 }
318
319 bool encode(
320 const State& state,
321 std::vector<std::uint8_t>& result) noexcept
322 {
323 try
324 {
325 Writer payload{};
326 payload.u32(Version);
327 if (!writeProfile(payload, state.profile))
328 return false;
329 for (std::int64_t timer : state.timeLastTrade)
330 payload.i64(timer);
331
332 if (payload.data.size() >
333 std::numeric_limits<std::uint32_t>::max())
334 return false;
335
336 Writer chunk{};
337 for (std::uint8_t byte : Magic)
338 chunk.u8(byte);
339 chunk.u32(static_cast<std::uint32_t>(payload.data.size()));
340 chunk.data.insert(
341 chunk.data.end(), payload.data.begin(), payload.data.end());
342 result = std::move(chunk.data);
343 return true;
344 }
345 catch (...)
346 {
347 result.clear();
348 return false;
349 }
350 }
351
352 bool decode(
353 std::span<const std::uint8_t> data,
354 State& result) noexcept
355 {
356 if (data.size() < 12 ||
357 !std::equal(Magic.begin(), Magic.end(), data.begin()))
358 return false;
359
360 Reader header{data.subspan(4, 4)};
361 std::uint32_t payloadSize{};
362 if (!header.u32(payloadSize) ||
363 payloadSize != data.size() - 8)
364 return false;
365
366 Reader reader{data.subspan(8)};
367 std::uint32_t version{};
368 if (!reader.u32(version) || version != Version)
369 return false;
370
371 State decoded{};
372 if (!readProfile(reader, decoded.profile))
373 return false;
374 for (std::int64_t& timer : decoded.timeLastTrade)
375 {
376 if (!reader.i64(timer))
377 return false;
378 }
379 if (!reader.finished())
380 return false;
381
382 result = std::move(decoded);
383 return true;
384 }
385}
std::vector< std::uint8_t > data
std::size_t offset
const char * header
unsigned state
unsigned index
std::atomic< bool > finished
std::uint16_t value
Definition Timers.cpp:16
bool encode(const State &state, std::vector< std::uint8_t > &result) noexcept
bool decode(std::span< const std::uint8_t > data, State &result) noexcept