Monopoly — Portage moderne 0.1.0
Portage moderne C/C++23 du Monopoly de 1999
Loading...
Searching...
No Matches
RuleLifecycle.cpp
Go to the documentation of this file.
1#include "RuleLifecycle.hpp"
2
3#include "BoardRules.hpp"
4#include "LegacyTextIds.hpp"
5#include "Messaging.hpp"
6#include "PhaseStack.hpp"
7#include "RuleRejections.hpp"
8
9#include <algorithm>
10#include <cstddef>
11#include <cstdint>
12#include <cwctype>
13#include <string>
14#include <string_view>
15
17{
18 namespace
19 {
20 constexpr std::uint8_t OffBoardSquare = 41;
21
22 constexpr std::uint8_t MaxAILevels = 3;
23
24
26 const GameState& state)
27 {
28 int count = 0;
29
30 for (PlayerNumber player = 0;
31 player < state.numberOfPlayers;
32 ++player)
33 {
34 if (
35 state.players[player]
36 .currentSquare >=
37 OffBoardSquare)
38 {
39 ++count;
40 }
41 }
42
43 return count;
44 }
45
46
48 std::wstring_view lhs,
49 std::wstring_view rhs)
50 {
51 if (lhs.size() != rhs.size())
52 {
53 return false;
54 }
55
56 for (std::size_t i = 0;
57 i < lhs.size();
58 ++i)
59 {
60 if (
61 std::towlower(lhs[i]) !=
62 std::towlower(rhs[i]))
63 {
64 return false;
65 }
66 }
67
68 return true;
69 }
70
71
72 void actionCompleted(
73 const actions::Message& message,
74 bool success)
75 {
80 static_cast<std::int64_t>(
81 message.action
82 ),
83 success ? 1 : 0,
84 message.fromPlayer,
85 0
86 );
87 }
88
89
90 void wrongPlayer(
91 const GameState& state,
92 const actions::Message& message)
93 {
95 }
96
97
98 void wrongPhase(
99 const GameState& state,
100 const actions::Message& message)
101 {
103 }
104
105
106 std::int64_t playerWealth(
107 const GameState& state,
108 PlayerNumber player)
109 {
110 // GameOverDeclareWinner() original :
111 //
112 // cash
113 // + purchase price si non hypothéqué
114 // + mortgage value si hypothéqué
115 // + full construction cost des bâtiments.
116
117 std::int64_t wealth =
118 state.players[player].cash;
119
120
121 for (std::size_t squareNo = 0;
123 ++squareNo)
124 {
125 const SquareState& square =
127
128 if (square.owner != player)
129 {
130 continue;
131 }
132
133
136 static_cast<board::SquareType>(
138 )
139 );
140
141
142 wealth +=
143 square.mortgaged
144 ? predefined.mortgageCost
145 : predefined.purchaseCost;
146
147
148 wealth +=
149 static_cast<std::int64_t>(
150 square.houses
151 ) *
152 predefined.housePurchaseCost;
153 }
154
155
156 return wealth;
157 }
158 }
159
160
162 const GameState& state)
163 {
164 const int bankrupt =
166
167 return
169 bankrupt >=
170 static_cast<int>(
172 ) - 1;
173 }
174
175
177 const GameState& state)
178 {
179 const int bankrupt =
181
182
183 // ActionEndTurn() original.
184
185 if (
187 bankrupt >=
189 {
190 return true;
191 }
192
193
194 if (
197 static_cast<std::uint64_t>(
199 ))
200 {
201 return true;
202 }
203
204
205 if (
207 bankrupt >=
208 static_cast<int>(
210 ) - 1)
211 {
212 return true;
213 }
214
215
216 return false;
217 }
218
219
222 {
223 // ====================================================
224 // GameOverDeclareWinner() original.
225 // ====================================================
226
229
230
232
233 std::int64_t highestWealth = -1;
234
237
238
239 for (PlayerNumber player = 0;
240 player < state.numberOfPlayers;
241 ++player)
242 {
243 if (
244 state.players[player]
245 .currentSquare >=
246 OffBoardSquare)
247 {
249 continue;
250 }
251
252
253 const std::int64_t wealth =
255 state,
256 player
257 );
258
259
260 // Strictement > dans Rule.cpp.
261 // En cas d'égalité, le premier reste gagnant.
262 if (wealth > highestWealth)
263 {
265 wealth;
266
268 player;
269 }
270 }
271
272
273 // PushPhase(
274 // GF_GAME_FINISHED,
275 // RichestPlayer,
276 // RULE_NOBODY_PLAYER,
277 // HighestWealth
278 // );
279
281 state,
286 );
287
288
293 );
294
295
296 // ----------------------------------------------------
297 // Retirer tous les survivants du plateau.
298 // ----------------------------------------------------
299
300 for (PlayerNumber player = 0;
301 player < state.numberOfPlayers;
302 ++player)
303 {
304 if (
305 state.players[player]
306 .currentSquare >=
307 OffBoardSquare)
308 {
309 continue;
310 }
311
312
317 OffBoardSquare,
318 state.players[player]
319 .currentSquare,
320 player,
321 0
322 );
323
324
325 state.players[player]
326 .currentSquare =
327 OffBoardSquare;
328 }
329
330
331 // ----------------------------------------------------
332 // Diagnostic exact de la raison.
333 // ----------------------------------------------------
334
335 if (
339 {
348 );
349
350 return;
351 }
352
353
354 if (
357 static_cast<std::uint64_t>(
359 ))
360 {
369 );
370
371 return;
372 }
373
374
382 0
383 );
384
385
386 // SendGameStatusToLobby() et
387 // MESS_UpdateLobbyGameFinished()
388 // appartiennent au transport réseau/lobby et ne sont
389 // pas simulés dans le runtime local.
390 }
391
392
395 const actions::Message& message)
396 {
397 // ====================================================
398 // ActionPauseGame() original.
399 // ====================================================
400
401 const PlayerNumber player =
402 message.fromPlayer;
403
404
405 if (player >=
407 {
408 wrongPlayer(
409 state,
410 message
411 );
412
413 return;
414 }
415
416
417 // Le source envoie ACTION_COMPLETED(TRUE)
418 // avant même de vérifier MOVING_TOKEN / WAIT_END_TURN.
420 message,
421 true
422 );
423
424
425 if (message.numberA != 0)
426 {
427 // Pause.
428
429 if (
430 phases::current(state).phase !=
432 {
433 // Ces deux phases ne sont pas restartables
434 // proprement après une pause.
435
436 if (
437 phases::current(state).phase ==
439 phases::current(state).phase ==
441 {
442 // Oui : l'original peut produire ici
443 // ACTION_COMPLETED(TRUE), puis FALSE via
444 // ErrorWrongPhase().
445 wrongPhase(
446 state,
447 message
448 );
449 return;
450 }
451 else
452 {
458 player,
459 0,
460 0
461 );
462
463
465 state,
467 0,
468 0,
469 0
470 );
471 }
472 }
473 }
474 else
475 {
476 // Reprise.
477
478 if (
479 phases::current(state).phase ==
481 {
487 player,
488 0,
489 0
490 );
491
492
494 }
495 }
496
497
498 // ActionPauseGame() finit toujours par un restart.
503 );
504 }
505
506
509 const actions::Message& message)
510 {
511 // ====================================================
512 // ActionDisconnectedPlayer() original.
513 //
514 // Connection owners replace the original NetworkAddress records.
515 // ====================================================
516
517 if (
518 message.numberA < 0 ||
519 message.numberA >=
521 (message.sourceId != 0 && message.fromPlayer != message.numberA))
522 {
523 wrongPlayer(
524 state,
525 message
526 );
527
528 return;
529 }
530
531
532 const PlayerNumber player =
533 static_cast<PlayerNumber>(
534 message.numberA
535 );
536
537
538 messaging::setPlayerOwner(player, 0);
539
541 message,
542 true
543 );
544
545
551 0,
552 player,
553 0
554 );
555
556
557 // ----------------------------------------------------
558 // IA de remplacement.
559 //
560 // Rule.cpp :
561 // level fourni si 1..AI_MAX_LEVELS,
562 // sinon AI_MAX_LEVELS = 3.
563 // ----------------------------------------------------
564
565 std::int64_t aiLevel =
566 message.numberB;
567
568
569 if (
570 aiLevel < 1 ||
571 aiLevel > MaxAILevels)
572 {
573 aiLevel =
575 }
576
577
578 state.players[player]
579 .aiPlayerLevel =
580 static_cast<std::uint8_t>(
581 aiLevel
582 );
583
584
585 // ----------------------------------------------------
586 // Garantir un nom unique.
587 //
588 // Le source conserve le nom existant et ajoute "?"
589 // tant qu'il y a collision.
590 // ----------------------------------------------------
591
592 std::wstring& name =
593 state.players[player].name;
594
595
596 while (true)
597 {
598 bool uniqueNameFound = true;
599
600
601 for (PlayerNumber other = 0;
602 other <
604 ++other)
605 {
606 if (other == player)
607 {
608 continue;
609 }
610
611
612 if (
614 state.players[other].name,
615 name
616 ))
617 {
618 uniqueNameFound = false;
619 break;
620 }
621 }
622
623
624 if (uniqueNameFound)
625 {
626 break;
627 }
628
629
630 if (
631 name.size() >=
633 {
634 name = L"?";
635 }
636 else
637 {
638 name += L"?";
639 }
640 }
641
642
643 // ----------------------------------------------------
644 // NOTIFY_ADD_LOCAL_PLAYER
645 // ----------------------------------------------------
646
651 player,
652 0,
653 0,
654 0,
655 name
656 );
657
658
659 // ----------------------------------------------------
660 // NOTIFY_NAME_PLAYER
661 // ----------------------------------------------------
662
667 player,
668 state.players[player].token,
669 state.players[player].colour,
670 state.players[player]
671 .aiPlayerLevel,
672 name
673 );
674
675
676 // L'IA qui vient de prendre le slot doit savoir
677 // immédiatement où le jeu en est.
682 );
683 }
684
685
688 PlayerNumber player)
689 {
690 // RULE_ProcessRules() original :
691 //
692 // if (PlayerNo < RULE_MAX_PLAYERS)
693 //
694 // et non NumberOfPlayers.
695
696 if (player >= MaxPlayers)
697 {
698 return;
699 }
700
701
702 state.players[player]
703 .timeOfLastActivity =
704 static_cast<std::uint32_t>(
706 );
707
708
709 state.players[player]
710 .inactivityCount = 0;
711 }
712
713
716 {
717 // ====================================================
718 // Partie inactivity de ActionTick().
719 // ====================================================
720
721 if (
723 {
724 return;
725 }
726
727
728 if (
730 {
731 return;
732 }
733
734
735 const GamePhase phase =
737
738
739 if (
741 phase == GamePhase::Configuration ||
743 phase == GamePhase::GameFinished ||
744 phase ==
745 GamePhase::
747 {
748 return;
749 }
750
751
752 for (PlayerNumber player = 0;
753 player < state.numberOfPlayers;
754 ++player)
755 {
756 PlayerState& info =
757 state.players[player];
758
759
760 const std::uint64_t lastActivity =
762
763
764 if (
767 {
768 continue;
769 }
770
771
772 if (
775 static_cast<std::uint64_t>(
778 ))
779 {
780 continue;
781 }
782
783
784 ++info.inactivityCount;
785
786
787 info.timeOfLastActivity =
788 static_cast<std::uint32_t>(
790 );
791
792
793 if (info.inactivityCount < 6)
794 {
795 // Les deux premiers avertissements ne sont
796 // volontairement pas affichés.
797
798 if (info.inactivityCount >= 3)
799 {
802 NotifyErrorMessage,
807
808 info.inactivityCount *
811
812 player,
813
814 (
815 6 -
816 info.inactivityCount
817 ) *
820 );
821 }
822
823
824 continue;
825 }
826
827
828 // ------------------------------------------------
829 // Sixième expiration :
830 // remplacement automatique par IA.
831 // ------------------------------------------------
832
833 info.inactivityCount = 0;
834
835
836 info.timeOfLastActivity =
837 static_cast<std::uint32_t>(
839 );
840
841
844 DisconnectedPlayer,
847 player,
848 0,
849 0,
850 0
851 );
852 }
853 }
854
855
858 {
859 switch (
860 phases::current(state).phase)
861 {
863 {
864 // GF_GAME_FINISHED original.
865
871 .fromPlayer,
873 .amount,
874 0,
875 0
876 );
877
878
879 return true;
880 }
881
882
884 {
885 // GF_PAUSED original.
886
891 0,
892 0,
893 0,
894 0
895 );
896
897
898 return true;
899 }
900
901
902 default:
903 return false;
904 }
905 }
906}
std::size_t count
const char * name
unsigned index
CardTag other
constexpr std::int64_t ErrorGameWonNormal
constexpr std::int64_t ErrorPlayerInactiveWarning
constexpr std::int64_t ErrorGameWonShort
constexpr std::int64_t ErrorGameWonTime
constexpr std::int64_t GamePaused
constexpr std::int64_t ErrorPlayerDisconnected
constexpr std::int64_t GameUnpaused
bool sendAction(const actions::Message &message)
void setPlayerOwner(rules::PlayerNumber player, std::uint32_t source)
Definition Messaging.cpp:76
const SquareDefinition & definition(SquareType square)
void declareWinner(GameState &state)
bool shouldEndGame(const GameState &state)
bool onlyOneSurvivor(const GameState &state)
bool restartLifecyclePhase(GameState &state)
void onIdleTick(GameState &state)
void actionPauseGame(GameState &state, const actions::Message &message)
void recordPlayerActivity(GameState &state, PlayerNumber player)
void actionDisconnectedPlayer(GameState &state, const actions::Message &message)
bool pop(GameState &state)
const PendingPhase & current(const GameState &state)
bool push(GameState &state, GamePhase phase, PlayerNumber fromPlayer, PlayerNumber toPlayer, std::int64_t amount)
void wrongPlayer(const GameState &state, const actions::Message &message)
void wrongPhase(const GameState &state, const actions::Message &message)
constexpr PlayerNumber BankPlayer
Definition RuleTypes.hpp:15
std::uint8_t PlayerNumber
Definition RuleTypes.hpp:12
constexpr std::size_t MaxPlayerNameLength
Definition RuleTypes.hpp:26
constexpr std::size_t SquareCount
Definition RuleTypes.hpp:24
const GameState & state()
constexpr PlayerNumber MaxPlayers
Definition RuleTypes.hpp:14
constexpr PlayerNumber NobodyPlayer
Definition RuleTypes.hpp:16
constexpr PlayerNumber AllPlayers
Definition RuleTypes.hpp:17
rules::PlayerNumber fromPlayer
Definition Actions.hpp:128
std::array< SquareState, SquareCount > squares
std::uint64_t gameDurationInSeconds
std::uint8_t numberOfPendingPhases
std::array< PlayerState, MaxPlayers > players