Monopoly — Portage moderne 0.1.0
Portage moderne C/C++23 du Monopoly de 1999
Loading...
Searching...
No Matches
PieceMovePlan.cpp
Go to the documentation of this file.
1#include "PieceMovePlan.hpp"
2
3#include "RuleTypes.hpp"
4
5#include <cstddef>
6#include <utility>
7
8namespace monopoly::pieces
9{
10 namespace
11 {
12 constexpr std::int32_t InJail = 40;
13 constexpr std::int32_t OffBoard = 41;
14
17 std::uint8_t token,
18 std::uint16_t offset = 0) noexcept
19 {
20 const auto tag = static_cast<data::DataTag>(
21 static_cast<std::uint32_t>(base) +
22 static_cast<std::uint32_t>(token) * AnimationsPerToken +
23 offset);
25 }
26
27 std::expected<void, PieceMovePlanError> append(
28 PieceMovePlan& plan, PieceMoveInstruction instruction)
29 {
30 if (plan.instructions.size() >= TokenAnimationItemLimit)
31 return std::unexpected(PieceMovePlanError::AnimationStackOverflow);
32 plan.instructions.push_back(std::move(instruction));
33 return {};
34 }
35
36 std::expected<std::int32_t, PieceMovePlanError> randomBit(
37 std::span<const std::uint8_t> choices, std::size_t& cursor)
38 {
39 if (cursor >= choices.size())
40 return std::unexpected(PieceMovePlanError::MissingRandomChoice);
41 return static_cast<std::int32_t>(choices[cursor++] & 1U);
42 }
43
44 PieceMoveInstruction movementInstruction(
45 BoardCameraView camera,
46 data::DataId sequence,
47 std::int32_t start,
48 std::int32_t landing,
49 bool corner) noexcept
50 {
51 return {false, 0, camera, sequence, start, landing, corner};
52 }
53
54 PieceMoveInstruction finalCameraInstruction(
55 BoardCameraView camera, std::int32_t square) noexcept
56 {
57 return {true, 0, camera, data::EmptyDataId,
58 square, square, false};
59 }
60
61 bool validBoardSquare(std::int32_t square) noexcept
62 {
63 return square >= 0 && square <= OffBoard;
64 }
65 }
66
67 std::expected<PieceMovePlan, PieceMovePlanError> planTokenMove(
69 std::uint8_t token,
70 std::int32_t before,
71 std::int32_t after,
72 bool animationsEnabled,
73 std::span<const std::uint8_t> randomChoices)
74 {
76 return std::unexpected(PieceMovePlanError::InvalidToken);
77 if (!validBoardSquare(before) || !validBoardSquare(after))
78 return std::unexpected(PieceMovePlanError::InvalidSquare);
82 return std::unexpected(PieceMovePlanError::UnsupportedNotification);
83
86 plan.sourceSquare = before;
87 plan.destinationSquare = after;
88 const auto finalCamera = pickCameraFor3Squares(after);
89 const auto startingCamera = pickCameraFor15Squares(before);
90 std::size_t randomCursor{};
91
93 {
94 std::uint16_t variant{};
95 if (after > 10) variant = 1;
96 if (after > 30) variant = 2;
99 instruction.sequence = animationId(
101 instruction.startSquare = after;
102 // Source leaves landing and IsCorner untouched in this branch.
103 if (const auto added = append(plan, instruction); !added)
104 return std::unexpected(added.error());
105 return plan;
106 }
107
109 {
110 if (after == InJail)
111 {
113 return plan;
114 }
115 if (after == OffBoard)
116 {
118 return plan;
119 }
120 if (before == InJail && after == 10)
121 {
123 return plan;
124 }
125 // Other jump notifications deliberately fall through to the
126 // same forward planner used by the source.
127 }
128
129 if (animationsEnabled)
130 {
131 const auto sideBefore = before < 40 ? before / 10 : 1;
132 auto sideAfter = after < 40 ? (after - 1) / 10 : 0;
133 if (after - 1 < 0) sideAfter = 3;
135 if (cornersPassed < 0) cornersPassed += 4;
136 if (cornersPassed == 0 && before > after) cornersPassed = 4;
137
138 auto square = before;
139 bool first = true;
140 while (square != after &&
141 plan.instructions.size() < TokenAnimationItemLimit - 1)
142 {
143 auto destination = square + (10 - (square % 10));
144 if (destination > after && square < after) destination = after;
145 if (destination == 40) destination = 0;
146
147 if (destination != after)
148 {
149 const bool kittyCornerLanding =
150 destination == after - 1 || destination == after - 2 ||
151 destination == after - 1 + 40 || destination == after - 2 + 40;
153 {
154 if (10 - (square % 10) > 2)
155 {
157 if (!bit) return std::unexpected(bit.error());
158 destination -= 1 + *bit;
159 }
160 else
161 {
162 destination = after;
163 }
164 }
165 else if (cornersPassed == 1)
166 {
168 if (!bit) return std::unexpected(bit.error());
169 if (10 - (square % 10) > 2)
170 destination -= 1 + *bit;
171 else
172 destination += 1 + *bit;
173 }
174 while (destination < 0) destination += 40;
175 while (destination >= 40) destination -= 40;
176 }
177
178 auto distance = destination - square;
179 if (distance < 0) distance += 40;
180 const auto camera = first ? startingCamera :
181 pickGoodCamera(square, distance);
182
183 const bool crossesCorner =
184 ((destination + 9) / 10) % 4 != ((square + 10) / 10) % 4;
185 if (crossesCorner)
186 {
187 std::uint16_t cornerOffset{};
188 if (distance == 2) cornerOffset = 1;
189 else if (distance == 4) cornerOffset = 4;
190 else if (destination % 10 == 1) cornerOffset = 3;
191 else if (destination % 10 == 2) cornerOffset = 2;
192 else return std::unexpected(
194
195 const auto added = append(plan, movementInstruction(
196 camera,
198 square, destination, true));
199 if (!added) return std::unexpected(added.error());
200 }
201 else
202 {
203 const auto added = append(plan, movementInstruction(
204 camera,
206 static_cast<std::uint16_t>(distance)),
207 square, destination, false));
208 if (!added) return std::unexpected(added.error());
209
210 if (destination % 10 == 0 && distance != 10)
211 {
212 const auto spin = append(plan, movementInstruction(
215 destination, destination, true));
216 if (!spin) return std::unexpected(spin.error());
217 }
218 }
219
220 square += distance;
221 while (square >= 40) square -= 40;
222 first = false;
223 }
224 }
225
226 if (const auto final = append(plan,
227 finalCameraInstruction(finalCamera, after)); !final)
228 return std::unexpected(final.error());
229 plan.randomChoicesUsed = randomCursor;
230 return plan;
231 }
232
233 std::expected<PieceMovePlan, PieceMovePlanError> planOffBoardMove(
234 std::uint8_t token, std::int32_t before, OffBoardOutcome outcome)
235 {
236 if (token >= rules::MaxTokens)
237 return std::unexpected(PieceMovePlanError::InvalidToken);
238 // Prison (40) is a live token position in the legacy off-board branch.
239 if (before < 0 || before >= OffBoard)
240 return std::unexpected(PieceMovePlanError::InvalidSquare);
241
243 plan.token = token;
244 plan.sourceSquare = before;
245 plan.destinationSquare = OffBoard;
247 {
249 const auto camera = pickCameraFor3Squares(before);
250 for (int i = 0; i != 2; ++i)
251 {
252 const auto added = append(plan, movementInstruction(camera,
254 before, before, false));
255 if (!added) return std::unexpected(added.error());
256 }
257 const auto blast = append(plan, movementInstruction(camera,
259 before, before, false));
260 if (!blast) return std::unexpected(blast.error());
261 const auto final = append(plan, finalCameraInstruction(
263 if (!final) return std::unexpected(final.error());
264 return plan;
265 }
266
268 auto square = before;
269 auto distanceToCorner = 10 - (square % 10);
270 if (distanceToCorner == 0) distanceToCorner = 10;
271
272 const auto first = append(plan, movementInstruction(
275 static_cast<std::uint16_t>(distanceToCorner)),
276 square, square, false));
277 if (!first) return std::unexpected(first.error());
278
279 square += distanceToCorner;
280 if (square >= 40) square = 0;
281
282 if (distanceToCorner != 10)
283 {
284 const auto spin = append(plan, movementInstruction(
287 square, square, true));
288 if (!spin) return std::unexpected(spin.error());
289 }
290
291 plan.loopBegin = plan.instructions.size();
292 for (int lap = 0; lap != 4; ++lap)
293 {
294 const auto loop = append(plan, movementInstruction(
297 square, square, false));
298 if (!loop) return std::unexpected(loop.error());
299 square += 10;
300 if (square >= 40) square = 0;
301 }
302 plan.loopEnd = plan.instructions.size();
303 return plan;
304 }
305}
decision::EconomicActionPlan plan
std::size_t offset
unsigned index
data::DataTag first
int token
std::string error
std::uint32_t DataId
Definition DataBanks.hpp:10
constexpr DataId EmptyDataId
Definition DataBanks.hpp:13
std::uint16_t DataTag
Definition DataBanks.hpp:11
constexpr DataId packDataId(std::uint16_t group, DataTag tag) noexcept
Definition DataBanks.hpp:39
BoardCameraView pickGoodCamera(std::int32_t square, std::int32_t distance) noexcept
BoardCameraView pickCameraFor3Squares(std::int32_t square) noexcept
constexpr data::DataTag AnimationsPerToken
std::expected< PieceMovePlan, PieceMovePlanError > planTokenMove(actions::Type notification, std::uint8_t token, std::int32_t before, std::int32_t after, bool animationsEnabled, std::span< const std::uint8_t > randomChoices)
std::expected< PieceMovePlan, PieceMovePlanError > planOffBoardMove(std::uint8_t token, std::int32_t before, OffBoardOutcome outcome)
constexpr data::DataTag TokenCornerAnimationBaseTag
constexpr data::DataTag TokenBackThreeAnimationBaseTag
constexpr data::DataTag TokenMoveAnimationBaseTag
constexpr std::size_t TokenAnimationItemLimit
BoardCameraView pickCameraFor15Squares(std::int32_t square) noexcept
constexpr std::size_t MaxTokens
Definition RuleTypes.hpp:30