EZTIME2025 commited on
Commit
548394f
·
1 Parent(s): 4dd77af

improve log system

Browse files
.github/instructions/BUILD_PLAN.md CHANGED
@@ -61,7 +61,7 @@
61
 
62
  ### משימה 3.3: End-to-End Testing
63
  **סטטוס:** 🟢 בעבודה פעילה!
64
- **עדכון אחרון:** 15 דצמבר 2025
65
 
66
  - [x] הרצת משחק מלא עם HumanUser
67
  - [x] בדיקת כל סוגי הפעולות (בנייה, גלגול קוביות)
@@ -81,15 +81,23 @@
81
  - מימוש מלא של `_execute_build_city` ב-`game_manager.py`
82
  - המרת הודעות שגיאה למספרי נקודות (point 44) במקום קואורדינטות
83
  - הצגת הודעות שגיאה בקונסול הראשי של המשתמש
 
 
 
 
 
 
 
84
 
85
  **מה שעובד כרגע:**
86
  - ✨ משחק מלא עם 3 שחקנים
87
  - ✨ בנייה: יישובים, כבישים, **ערים** ✅ (תוקן 15 דצמבר!)
88
- - ✨ גלגול קוביות וחלוקת משאבים
89
  - ✨ חוק ה-7: השלכת קלפים, העברת רובר, גניבת קלף
90
  - ✨ מסחר עם הבנק
91
  - ✨ קניית קלפי פיתוח
92
- - ✨ ויזואליזציה בזמן אמת (Console + Web)
 
93
 
94
  ---
95
 
@@ -136,23 +144,37 @@
136
  **מטרה:** טיפול במסחר ואינטראקציות מורכבות
137
 
138
  ### משימה 5.1: Trading System
139
- **סטטוס:** לא התחיל
 
140
  **זמן משוער:** 4-5 שעות
141
 
142
- - [ ] הצעות מסחר בין שחקנים
143
- - [ ] מנגנון אישור/דחייה
144
  - [ ] counter-offers
145
  - [ ] timeout למסחר
146
- - [ ] mסחר עם הבנק (4:1, harbors)
 
 
 
 
 
 
147
 
148
  ### משימה 5.2: Robber Interactions
149
- **סטטוס:** לא התחיל
 
150
  **זמן משוער:** 2-3 שעות
151
 
152
- - [ ] בחירת מיקום רובר
153
- - [ ] בחירת שחקן לגניבה
154
- - [ ] Knight card interactions
155
- - [ ] ויזואליזציה של רובר
 
 
 
 
 
 
156
 
157
  ### משימה 5.3: Development Cards & Special Actions
158
  **סטטוס:** 🟢 בעבודה פעילה!
 
61
 
62
  ### משימה 3.3: End-to-End Testing
63
  **סטטוס:** 🟢 בעבודה פעילה!
64
+ **עדכון אחרון:** 20 דצמבר 2025
65
 
66
  - [x] הרצת משחק מלא עם HumanUser
67
  - [x] בדיקת כל סוגי הפעולות (בנייה, גלגול קוביות)
 
81
  - מימוש מלא של `_execute_build_city` ב-`game_manager.py`
82
  - המרת הודעות שגיאה למספרי נקודות (point 44) במקום קואורדינטות
83
  - הצגת הודעות שגיאה בקונסול הראשי של המשתמש
84
+ - ✅ **מערכת לוגים מובנית מלאה (20 דצמבר 2025)**:
85
+ - יצירת `log_events.py` עם EventType enum, LogEntry dataclass
86
+ - עדכון `web_visualization.py` לתמוך בלוגים מפורטים עם כל הנתונים
87
+ - עדכון `console_visualization.py` לתמוך בלוגים מפורטים
88
+ - עדכון `game_manager.py` להוסיף פרטים מלאים לכל action (point numbers, dice values, costs)
89
+ - תיקון הצגת קוביות - עכשיו מוצגים הערכים האמיתיים במקום [0,0]
90
+ - כל אירוע עכשיו כולל: timestamp, turn number, player info, detailed data, status, errors
91
 
92
  **מה שעובד כרגע:**
93
  - ✨ משחק מלא עם 3 שחקנים
94
  - ✨ בנייה: יישובים, כבישים, **ערים** ✅ (תוקן 15 דצמבר!)
95
+ - ✨ גלגול קוביות וחלוקת משאבים ✅ (לוגים מפורטים 20 דצמבר!)
96
  - ✨ חוק ה-7: השלכת קלפים, העברת רובר, גניבת קלף
97
  - ✨ מסחר עם הבנק
98
  - ✨ קניית קלפי פיתוח
99
+ - ✨ ויזואליזציה בזמן אמת (Console + Web) עם לוגים מובנים
100
+ - ✨ מערכת לוגים parsable לניתוח מידע
101
 
102
  ---
103
 
 
144
  **מטרה:** טיפול במסחר ואינטראקציות מורכבות
145
 
146
  ### משימה 5.1: Trading System
147
+ **סטטוס:** 🟢 הושלם חלקית!
148
+ **תאריך עדכון:** 20 דצמבר 2025
149
  **זמן משוער:** 4-5 שעות
150
 
151
+ - [x] הצעות מסחר בין שחקנים
152
+ - [x] מנגנון אישור/דחייה
153
  - [ ] counter-offers
154
  - [ ] timeout למסחר
155
+ - [x] מסחר עם הבנק (4:1, harbors)
156
+
157
+ **מימוש:**
158
+ - `_execute_trade_propose()` - הצעת מסחר בין שחקנים
159
+ - `_execute_trade_bank()` - מסחר עם הבנק
160
+ - תמיכה בפורמט: `trade player <name> offer <cards> for <cards>`
161
+ - מנגנון אישור/דחייה: `y`/`n` או `yes`/`no`
162
 
163
  ### משימה 5.2: Robber Interactions
164
+ **סטטוס:** הושלם!
165
+ **תאריך השלמה:** נובמבר 2025
166
  **זמן משוער:** 2-3 שעות
167
 
168
+ - [x] בחירת מיקום רובר
169
+ - [x] בחירת שחקן לגניבה
170
+ - [x] Knight card interactions
171
+ - [x] ויזואליזציה של רובר
172
+
173
+ **מימוש:**
174
+ - `_handle_robber_move()` - העברת רובר
175
+ - `_handle_steal_card()` - גניבת קלף
176
+ - תמיכה בפורמט: `robber <tile_id>`
177
+ - אינטגרציה עם Knight card ו-Rule of 7
178
 
179
  ### משימה 5.3: Development Cards & Special Actions
180
  **סטטוס:** 🟢 בעבודה פעילה!
game_viz.log CHANGED
@@ -22,7 +22,7 @@ Current Player: ► a
22
  Resources: None
23
  Buildings: Settlements: 0, Cities: 0, Roads: 0
24
 
25
- s
26
  Victory Points: 0
27
  Resources: None
28
  Buildings: Settlements: 0, Cities: 0, Roads: 0
@@ -53,7 +53,7 @@ Current Player: ► a
53
  Resources: None
54
  Buildings: Settlements: 0, Cities: 0, Roads: 0
55
 
56
- s
57
  Victory Points: 0
58
  Resources: None
59
  Buildings: Settlements: 0, Cities: 0, Roads: 0
@@ -86,7 +86,7 @@ Current Player: ► b
86
  Resources: None
87
  Buildings: Settlements: 1, Cities: 0, Roads: 0
88
 
89
- s
90
  Victory Points: 0
91
  Resources: None
92
  Buildings: Settlements: 0, Cities: 0, Roads: 0
@@ -117,7 +117,7 @@ Current Player: ► b
117
  Resources: None
118
  Buildings: Settlements: 1, Cities: 0, Roads: 1
119
 
120
- s
121
  Victory Points: 0
122
  Resources: None
123
  Buildings: Settlements: 0, Cities: 0, Roads: 0
@@ -127,15 +127,15 @@ Current Player: ► b
127
  Board Tiles: 19 tiles configured
128
 
129
 
130
- >>> Turn 2: s's turn
131
- ✓ s built a settlement
132
 
133
  ==================================================
134
   GAME STATE 
135
  ==================================================
136
 
137
  Turn: 2
138
- Current Player: ► s
139
 
140
  PLAYERS
141
  -------
@@ -150,7 +150,7 @@ Current Player: ► s
150
  Resources: None
151
  Buildings: Settlements: 1, Cities: 0, Roads: 1
152
 
153
- ► s
154
  Victory Points: 1
155
  Resources: None
156
  Buildings: Settlements: 1, Cities: 0, Roads: 0
@@ -159,14 +159,14 @@ Current Player: ► s
159
  -----
160
  Board Tiles: 19 tiles configured
161
 
162
- ✓ s built a road
163
 
164
  ==================================================
165
   GAME STATE 
166
  ==================================================
167
 
168
  Turn: 2
169
- Current Player: ► s
170
 
171
  PLAYERS
172
  -------
@@ -181,7 +181,7 @@ Current Player: ► s
181
  Resources: None
182
  Buildings: Settlements: 1, Cities: 0, Roads: 1
183
 
184
- ► s
185
  Victory Points: 1
186
  Resources: None
187
  Buildings: Settlements: 1, Cities: 0, Roads: 1
@@ -191,18 +191,18 @@ Current Player: ► s
191
  Board Tiles: 19 tiles configured
192
 
193
 
194
- >>> Turn 3: s's turn
195
 
196
  📦 Resources distributed:
197
- s: Ore, Wheat, Wheat
198
- ✓ s built a settlement
199
 
200
  ==================================================
201
   GAME STATE 
202
  ==================================================
203
 
204
  Turn: 3
205
- Current Player: ► s
206
 
207
  PLAYERS
208
  -------
@@ -217,7 +217,7 @@ Current Player: ► s
217
  Resources: None
218
  Buildings: Settlements: 1, Cities: 0, Roads: 1
219
 
220
- ► s
221
  Victory Points: 2
222
  Resources: None
223
  Buildings: Settlements: 2, Cities: 0, Roads: 1
@@ -226,14 +226,14 @@ Current Player: ► s
226
  -----
227
  Board Tiles: 19 tiles configured
228
 
229
- ✓ s built a road
230
 
231
  ==================================================
232
   GAME STATE 
233
  ==================================================
234
 
235
  Turn: 3
236
- Current Player: ► s
237
 
238
  PLAYERS
239
  -------
@@ -248,7 +248,7 @@ Current Player: ► s
248
  Resources: None
249
  Buildings: Settlements: 1, Cities: 0, Roads: 1
250
 
251
- ► s
252
  Victory Points: 2
253
  Resources: None
254
  Buildings: Settlements: 2, Cities: 0, Roads: 2
@@ -284,7 +284,7 @@ Current Player: ► b
284
  Resources: None
285
  Buildings: Settlements: 2, Cities: 0, Roads: 1
286
 
287
- s
288
  Victory Points: 2
289
  Resources: None
290
  Buildings: Settlements: 2, Cities: 0, Roads: 2
@@ -315,7 +315,7 @@ Current Player: ► b
315
  Resources: None
316
  Buildings: Settlements: 2, Cities: 0, Roads: 2
317
 
318
- s
319
  Victory Points: 2
320
  Resources: None
321
  Buildings: Settlements: 2, Cities: 0, Roads: 2
@@ -351,7 +351,7 @@ Current Player: ► a
351
  Resources: None
352
  Buildings: Settlements: 2, Cities: 0, Roads: 2
353
 
354
- s
355
  Victory Points: 2
356
  Resources: None
357
  Buildings: Settlements: 2, Cities: 0, Roads: 2
@@ -382,7 +382,7 @@ Current Player: ► a
382
  Resources: None
383
  Buildings: Settlements: 2, Cities: 0, Roads: 2
384
 
385
- s
386
  Victory Points: 2
387
  Resources: None
388
  Buildings: Settlements: 2, Cities: 0, Roads: 2
@@ -393,12 +393,6 @@ Board Tiles: 19 tiles configured
393
 
394
 
395
  >>> Turn 6: a's turn
396
-
397
- 🎲 a rolled: 5 + 3 = 8
398
-
399
- 📦 Resources distributed:
400
- Player 1: Sheep
401
- Player 2: Wheat
402
  ✓ a rolled the dice
403
 
404
  ==================================================
@@ -421,38 +415,7 @@ Current Player: ► a
421
  Resources: None
422
  Buildings: Settlements: 2, Cities: 0, Roads: 2
423
 
424
- s
425
- Victory Points: 2
426
- Resources: None
427
- Buildings: Settlements: 2, Cities: 0, Roads: 2
428
-
429
- BOARD
430
- -----
431
- Board Tiles: 19 tiles configured
432
-
433
- ✓ a ended their turn
434
-
435
- ==================================================
436
-  GAME STATE 
437
- ==================================================
438
-
439
- Turn: 6
440
- Current Player: ► a
441
-
442
- PLAYERS
443
- -------
444
-
445
- ► a
446
- Victory Points: 2
447
- Resources: None
448
- Buildings: Settlements: 2, Cities: 0, Roads: 2
449
-
450
- b
451
- Victory Points: 2
452
- Resources: None
453
- Buildings: Settlements: 2, Cities: 0, Roads: 2
454
-
455
- s
456
  Victory Points: 2
457
  Resources: None
458
  Buildings: Settlements: 2, Cities: 0, Roads: 2
@@ -463,8 +426,6 @@ Board Tiles: 19 tiles configured
463
 
464
 
465
  >>> Turn 7: b's turn
466
-
467
- 🎲 b rolled: 6 + 1 = 7
468
  ✓ b rolled the dice
469
 
470
  ==================================================
@@ -487,7 +448,7 @@ Current Player: ► b
487
  Resources: None
488
  Buildings: Settlements: 2, Cities: 0, Roads: 2
489
 
490
- s
491
  Victory Points: 2
492
  Resources: None
493
  Buildings: Settlements: 2, Cities: 0, Roads: 2
@@ -518,7 +479,7 @@ Current Player: ► b
518
  Resources: None
519
  Buildings: Settlements: 2, Cities: 0, Roads: 2
520
 
521
- s
522
  Victory Points: 2
523
  Resources: None
524
  Buildings: Settlements: 2, Cities: 0, Roads: 2
@@ -527,84 +488,16 @@ Current Player: ► b
527
  -----
528
  Board Tiles: 19 tiles configured
529
 
530
- ✓ b ended their turn
531
-
532
- ==================================================
533
-  GAME STATE 
534
- ==================================================
535
-
536
- Turn: 7
537
- Current Player: ► b
538
-
539
- PLAYERS
540
- -------
541
-
542
- a
543
- Victory Points: 2
544
- Resources: None
545
- Buildings: Settlements: 2, Cities: 0, Roads: 2
546
-
547
- ► b
548
- Victory Points: 2
549
- Resources: None
550
- Buildings: Settlements: 2, Cities: 0, Roads: 2
551
-
552
- s
553
- Victory Points: 2
554
- Resources: None
555
- Buildings: Settlements: 2, Cities: 0, Roads: 2
556
-
557
- BOARD
558
- -----
559
- Board Tiles: 19 tiles configured
560
-
561
-
562
- >>> Turn 8: s's turn
563
-
564
- 🎲 s rolled: 3 + 5 = 8
565
-
566
- 📦 Resources distributed:
567
- Player 1: Sheep
568
- Player 2: Wheat
569
- ✓ s rolled the dice
570
-
571
- ==================================================
572
-  GAME STATE 
573
- ==================================================
574
-
575
- Turn: 8
576
- Current Player: ► s
577
-
578
- PLAYERS
579
- -------
580
-
581
- a
582
- Victory Points: 2
583
- Resources: None
584
- Buildings: Settlements: 2, Cities: 0, Roads: 2
585
-
586
- b
587
- Victory Points: 2
588
- Resources: None
589
- Buildings: Settlements: 2, Cities: 0, Roads: 2
590
-
591
- ► s
592
- Victory Points: 2
593
- Resources: None
594
- Buildings: Settlements: 2, Cities: 0, Roads: 2
595
-
596
- BOARD
597
- -----
598
- Board Tiles: 19 tiles configured
599
 
600
- ✓ s ended their turn
 
601
 
602
  ==================================================
603
   GAME STATE 
604
  ==================================================
605
 
606
  Turn: 8
607
- Current Player: ► s
608
 
609
  PLAYERS
610
  -------
@@ -619,7 +512,7 @@ Current Player: ► s
619
  Resources: None
620
  Buildings: Settlements: 2, Cities: 0, Roads: 2
621
 
622
- ► s
623
  Victory Points: 2
624
  Resources: None
625
  Buildings: Settlements: 2, Cities: 0, Roads: 2
@@ -630,8 +523,6 @@ Board Tiles: 19 tiles configured
630
 
631
 
632
  >>> Turn 9: a's turn
633
-
634
- 🎲 a rolled: 2 + 5 = 7
635
  ✓ a rolled the dice
636
 
637
  ==================================================
@@ -654,7 +545,7 @@ Current Player: ► a
654
  Resources: None
655
  Buildings: Settlements: 2, Cities: 0, Roads: 2
656
 
657
- s
658
  Victory Points: 2
659
  Resources: None
660
  Buildings: Settlements: 2, Cities: 0, Roads: 2
@@ -685,38 +576,7 @@ Current Player: ► a
685
  Resources: None
686
  Buildings: Settlements: 2, Cities: 0, Roads: 2
687
 
688
- s
689
- Victory Points: 2
690
- Resources: None
691
- Buildings: Settlements: 2, Cities: 0, Roads: 2
692
-
693
- BOARD
694
- -----
695
- Board Tiles: 19 tiles configured
696
-
697
- ✓ a ended their turn
698
-
699
- ==================================================
700
-  GAME STATE 
701
- ==================================================
702
-
703
- Turn: 9
704
- Current Player: ► a
705
-
706
- PLAYERS
707
- -------
708
-
709
- ► a
710
- Victory Points: 2
711
- Resources: None
712
- Buildings: Settlements: 2, Cities: 0, Roads: 2
713
-
714
- b
715
- Victory Points: 2
716
- Resources: None
717
- Buildings: Settlements: 2, Cities: 0, Roads: 2
718
-
719
- s
720
  Victory Points: 2
721
  Resources: None
722
  Buildings: Settlements: 2, Cities: 0, Roads: 2
@@ -727,11 +587,6 @@ Board Tiles: 19 tiles configured
727
 
728
 
729
  >>> Turn 10: b's turn
730
-
731
- 🎲 b rolled: 4 + 1 = 5
732
-
733
- 📦 Resources distributed:
734
- Player 1: Ore
735
  ✓ b rolled the dice
736
 
737
  ==================================================
@@ -754,7 +609,7 @@ Current Player: ► b
754
  Resources: None
755
  Buildings: Settlements: 2, Cities: 0, Roads: 2
756
 
757
- s
758
  Victory Points: 2
759
  Resources: None
760
  Buildings: Settlements: 2, Cities: 0, Roads: 2
@@ -786,7 +641,7 @@ Current Player: ► b
786
  Dev Cards: 1
787
  Buildings: Settlements: 2, Cities: 0, Roads: 2
788
 
789
- s
790
  Victory Points: 2
791
  Resources: None
792
  Buildings: Settlements: 2, Cities: 0, Roads: 2
@@ -817,7 +672,7 @@ Current Player: ► b
817
  Resources: None
818
  Buildings: Settlements: 2, Cities: 0, Roads: 4
819
 
820
- s
821
  Victory Points: 2
822
  Resources: None
823
  Buildings: Settlements: 2, Cities: 0, Roads: 2
@@ -826,52 +681,16 @@ Current Player: ► b
826
  -----
827
  Board Tiles: 19 tiles configured
828
 
829
- ✓ b ended their turn
830
 
831
- ==================================================
832
-  GAME STATE 
833
- ==================================================
834
-
835
- Turn: 10
836
- Current Player: ► b
837
-
838
- PLAYERS
839
- -------
840
-
841
- a
842
- Victory Points: 2
843
- Resources: None
844
- Buildings: Settlements: 2, Cities: 0, Roads: 2
845
-
846
- ► b
847
- Victory Points: 2
848
- Resources: None
849
- Buildings: Settlements: 2, Cities: 0, Roads: 4
850
-
851
- s
852
- Victory Points: 2
853
- Resources: None
854
- Buildings: Settlements: 2, Cities: 0, Roads: 2
855
-
856
- BOARD
857
- -----
858
- Board Tiles: 19 tiles configured
859
-
860
-
861
- >>> Turn 11: s's turn
862
-
863
- 🎲 s rolled: 5 + 4 = 9
864
-
865
- 📦 Resources distributed:
866
- Player 3: Wheat, Wheat
867
- ✓ s rolled the dice
868
 
869
  ==================================================
870
   GAME STATE 
871
  ==================================================
872
 
873
  Turn: 11
874
- Current Player: ► s
875
 
876
  PLAYERS
877
  -------
@@ -886,7 +705,7 @@ Current Player: ► s
886
  Resources: None
887
  Buildings: Settlements: 2, Cities: 0, Roads: 4
888
 
889
- ► s
890
  Victory Points: 2
891
  Resources: None
892
  Buildings: Settlements: 2, Cities: 0, Roads: 2
@@ -895,14 +714,14 @@ Current Player: ► s
895
  -----
896
  Board Tiles: 19 tiles configured
897
 
898
- ✓ s proposed a trade
899
 
900
  ==================================================
901
   GAME STATE 
902
  ==================================================
903
 
904
  Turn: 11
905
- Current Player: ► s
906
 
907
  PLAYERS
908
  -------
@@ -917,7 +736,7 @@ Current Player: ► s
917
  Resources: None
918
  Buildings: Settlements: 2, Cities: 0, Roads: 4
919
 
920
- ► s
921
  Victory Points: 2
922
  Resources: None
923
  Buildings: Settlements: 2, Cities: 0, Roads: 2
@@ -926,7 +745,7 @@ Current Player: ► s
926
  -----
927
  Board Tiles: 19 tiles configured
928
 
929
- ✗ s built a city
930
  Error: You don't own the settlement at point 40
931
 
932
  ==================================================
@@ -934,7 +753,7 @@ Board Tiles: 19 tiles configured
934
  ==================================================
935
 
936
  Turn: 11
937
- Current Player: ► s
938
 
939
  PLAYERS
940
  -------
@@ -949,54 +768,23 @@ Current Player: ► s
949
  Resources: None
950
  Buildings: Settlements: 2, Cities: 0, Roads: 4
951
 
952
- ► s
953
- Victory Points: 2
954
- Resources: None
955
- Buildings: Settlements: 2, Cities: 0, Roads: 2
956
-
957
- BOARD
958
- -----
959
- Board Tiles: 19 tiles configured
960
-
961
- ✓ s built a city
962
-
963
- ==================================================
964
-  GAME STATE 
965
- ==================================================
966
-
967
- Turn: 11
968
- Current Player: ► s
969
-
970
- PLAYERS
971
- -------
972
-
973
- a
974
  Victory Points: 2
975
  Resources: None
976
  Buildings: Settlements: 2, Cities: 0, Roads: 2
977
 
978
- b
979
- Victory Points: 2
980
- Resources: None
981
- Buildings: Settlements: 2, Cities: 0, Roads: 4
982
-
983
- ► s
984
- Victory Points: 3
985
- Resources: None
986
- Buildings: Settlements: 1, Cities: 1, Roads: 2
987
-
988
  BOARD
989
  -----
990
  Board Tiles: 19 tiles configured
991
 
992
- ✓ s ended their turn
993
 
994
  ==================================================
995
   GAME STATE 
996
  ==================================================
997
 
998
  Turn: 11
999
- Current Player: ► s
1000
 
1001
  PLAYERS
1002
  -------
@@ -1011,7 +799,7 @@ Current Player: ► s
1011
  Resources: None
1012
  Buildings: Settlements: 2, Cities: 0, Roads: 4
1013
 
1014
- ► s
1015
  Victory Points: 3
1016
  Resources: None
1017
  Buildings: Settlements: 1, Cities: 1, Roads: 2
 
22
  Resources: None
23
  Buildings: Settlements: 0, Cities: 0, Roads: 0
24
 
25
+ c
26
  Victory Points: 0
27
  Resources: None
28
  Buildings: Settlements: 0, Cities: 0, Roads: 0
 
53
  Resources: None
54
  Buildings: Settlements: 0, Cities: 0, Roads: 0
55
 
56
+ c
57
  Victory Points: 0
58
  Resources: None
59
  Buildings: Settlements: 0, Cities: 0, Roads: 0
 
86
  Resources: None
87
  Buildings: Settlements: 1, Cities: 0, Roads: 0
88
 
89
+ c
90
  Victory Points: 0
91
  Resources: None
92
  Buildings: Settlements: 0, Cities: 0, Roads: 0
 
117
  Resources: None
118
  Buildings: Settlements: 1, Cities: 0, Roads: 1
119
 
120
+ c
121
  Victory Points: 0
122
  Resources: None
123
  Buildings: Settlements: 0, Cities: 0, Roads: 0
 
127
  Board Tiles: 19 tiles configured
128
 
129
 
130
+ >>> Turn 2: c's turn
131
+ ✓ c built a settlement
132
 
133
  ==================================================
134
   GAME STATE 
135
  ==================================================
136
 
137
  Turn: 2
138
+ Current Player: ► c
139
 
140
  PLAYERS
141
  -------
 
150
  Resources: None
151
  Buildings: Settlements: 1, Cities: 0, Roads: 1
152
 
153
+ ► c
154
  Victory Points: 1
155
  Resources: None
156
  Buildings: Settlements: 1, Cities: 0, Roads: 0
 
159
  -----
160
  Board Tiles: 19 tiles configured
161
 
162
+ ✓ c built a road
163
 
164
  ==================================================
165
   GAME STATE 
166
  ==================================================
167
 
168
  Turn: 2
169
+ Current Player: ► c
170
 
171
  PLAYERS
172
  -------
 
181
  Resources: None
182
  Buildings: Settlements: 1, Cities: 0, Roads: 1
183
 
184
+ ► c
185
  Victory Points: 1
186
  Resources: None
187
  Buildings: Settlements: 1, Cities: 0, Roads: 1
 
191
  Board Tiles: 19 tiles configured
192
 
193
 
194
+ >>> Turn 3: c's turn
195
 
196
  📦 Resources distributed:
197
+ c: Ore, Wheat, Wheat
198
+ ✓ c built a settlement
199
 
200
  ==================================================
201
   GAME STATE 
202
  ==================================================
203
 
204
  Turn: 3
205
+ Current Player: ► c
206
 
207
  PLAYERS
208
  -------
 
217
  Resources: None
218
  Buildings: Settlements: 1, Cities: 0, Roads: 1
219
 
220
+ ► c
221
  Victory Points: 2
222
  Resources: None
223
  Buildings: Settlements: 2, Cities: 0, Roads: 1
 
226
  -----
227
  Board Tiles: 19 tiles configured
228
 
229
+ ✓ c built a road
230
 
231
  ==================================================
232
   GAME STATE 
233
  ==================================================
234
 
235
  Turn: 3
236
+ Current Player: ► c
237
 
238
  PLAYERS
239
  -------
 
248
  Resources: None
249
  Buildings: Settlements: 1, Cities: 0, Roads: 1
250
 
251
+ ► c
252
  Victory Points: 2
253
  Resources: None
254
  Buildings: Settlements: 2, Cities: 0, Roads: 2
 
284
  Resources: None
285
  Buildings: Settlements: 2, Cities: 0, Roads: 1
286
 
287
+ c
288
  Victory Points: 2
289
  Resources: None
290
  Buildings: Settlements: 2, Cities: 0, Roads: 2
 
315
  Resources: None
316
  Buildings: Settlements: 2, Cities: 0, Roads: 2
317
 
318
+ c
319
  Victory Points: 2
320
  Resources: None
321
  Buildings: Settlements: 2, Cities: 0, Roads: 2
 
351
  Resources: None
352
  Buildings: Settlements: 2, Cities: 0, Roads: 2
353
 
354
+ c
355
  Victory Points: 2
356
  Resources: None
357
  Buildings: Settlements: 2, Cities: 0, Roads: 2
 
382
  Resources: None
383
  Buildings: Settlements: 2, Cities: 0, Roads: 2
384
 
385
+ c
386
  Victory Points: 2
387
  Resources: None
388
  Buildings: Settlements: 2, Cities: 0, Roads: 2
 
393
 
394
 
395
  >>> Turn 6: a's turn
 
 
 
 
 
 
396
  ✓ a rolled the dice
397
 
398
  ==================================================
 
415
  Resources: None
416
  Buildings: Settlements: 2, Cities: 0, Roads: 2
417
 
418
+ c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
419
  Victory Points: 2
420
  Resources: None
421
  Buildings: Settlements: 2, Cities: 0, Roads: 2
 
426
 
427
 
428
  >>> Turn 7: b's turn
 
 
429
  ✓ b rolled the dice
430
 
431
  ==================================================
 
448
  Resources: None
449
  Buildings: Settlements: 2, Cities: 0, Roads: 2
450
 
451
+ c
452
  Victory Points: 2
453
  Resources: None
454
  Buildings: Settlements: 2, Cities: 0, Roads: 2
 
479
  Resources: None
480
  Buildings: Settlements: 2, Cities: 0, Roads: 2
481
 
482
+ c
483
  Victory Points: 2
484
  Resources: None
485
  Buildings: Settlements: 2, Cities: 0, Roads: 2
 
488
  -----
489
  Board Tiles: 19 tiles configured
490
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
491
 
492
+ >>> Turn 8: c's turn
493
+ ✓ c rolled the dice
494
 
495
  ==================================================
496
   GAME STATE 
497
  ==================================================
498
 
499
  Turn: 8
500
+ Current Player: ► c
501
 
502
  PLAYERS
503
  -------
 
512
  Resources: None
513
  Buildings: Settlements: 2, Cities: 0, Roads: 2
514
 
515
+ ► c
516
  Victory Points: 2
517
  Resources: None
518
  Buildings: Settlements: 2, Cities: 0, Roads: 2
 
523
 
524
 
525
  >>> Turn 9: a's turn
 
 
526
  ✓ a rolled the dice
527
 
528
  ==================================================
 
545
  Resources: None
546
  Buildings: Settlements: 2, Cities: 0, Roads: 2
547
 
548
+ c
549
  Victory Points: 2
550
  Resources: None
551
  Buildings: Settlements: 2, Cities: 0, Roads: 2
 
576
  Resources: None
577
  Buildings: Settlements: 2, Cities: 0, Roads: 2
578
 
579
+ c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
580
  Victory Points: 2
581
  Resources: None
582
  Buildings: Settlements: 2, Cities: 0, Roads: 2
 
587
 
588
 
589
  >>> Turn 10: b's turn
 
 
 
 
 
590
  ✓ b rolled the dice
591
 
592
  ==================================================
 
609
  Resources: None
610
  Buildings: Settlements: 2, Cities: 0, Roads: 2
611
 
612
+ c
613
  Victory Points: 2
614
  Resources: None
615
  Buildings: Settlements: 2, Cities: 0, Roads: 2
 
641
  Dev Cards: 1
642
  Buildings: Settlements: 2, Cities: 0, Roads: 2
643
 
644
+ c
645
  Victory Points: 2
646
  Resources: None
647
  Buildings: Settlements: 2, Cities: 0, Roads: 2
 
672
  Resources: None
673
  Buildings: Settlements: 2, Cities: 0, Roads: 4
674
 
675
+ c
676
  Victory Points: 2
677
  Resources: None
678
  Buildings: Settlements: 2, Cities: 0, Roads: 2
 
681
  -----
682
  Board Tiles: 19 tiles configured
683
 
 
684
 
685
+ >>> Turn 11: c's turn
686
+ ✓ c rolled the dice
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
687
 
688
  ==================================================
689
   GAME STATE 
690
  ==================================================
691
 
692
  Turn: 11
693
+ Current Player: ► c
694
 
695
  PLAYERS
696
  -------
 
705
  Resources: None
706
  Buildings: Settlements: 2, Cities: 0, Roads: 4
707
 
708
+ ► c
709
  Victory Points: 2
710
  Resources: None
711
  Buildings: Settlements: 2, Cities: 0, Roads: 2
 
714
  -----
715
  Board Tiles: 19 tiles configured
716
 
717
+ ✓ c proposed a trade
718
 
719
  ==================================================
720
   GAME STATE 
721
  ==================================================
722
 
723
  Turn: 11
724
+ Current Player: ► c
725
 
726
  PLAYERS
727
  -------
 
736
  Resources: None
737
  Buildings: Settlements: 2, Cities: 0, Roads: 4
738
 
739
+ ► c
740
  Victory Points: 2
741
  Resources: None
742
  Buildings: Settlements: 2, Cities: 0, Roads: 2
 
745
  -----
746
  Board Tiles: 19 tiles configured
747
 
748
+ ✗ c built a city
749
  Error: You don't own the settlement at point 40
750
 
751
  ==================================================
 
753
  ==================================================
754
 
755
  Turn: 11
756
+ Current Player: ► c
757
 
758
  PLAYERS
759
  -------
 
768
  Resources: None
769
  Buildings: Settlements: 2, Cities: 0, Roads: 4
770
 
771
+ ► c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
772
  Victory Points: 2
773
  Resources: None
774
  Buildings: Settlements: 2, Cities: 0, Roads: 2
775
 
 
 
 
 
 
 
 
 
 
 
776
  BOARD
777
  -----
778
  Board Tiles: 19 tiles configured
779
 
780
+ ✓ c built a city
781
 
782
  ==================================================
783
   GAME STATE 
784
  ==================================================
785
 
786
  Turn: 11
787
+ Current Player: ► c
788
 
789
  PLAYERS
790
  -------
 
799
  Resources: None
800
  Buildings: Settlements: 2, Cities: 0, Roads: 4
801
 
802
+ ► c
803
  Victory Points: 3
804
  Resources: None
805
  Buildings: Settlements: 1, Cities: 1, Roads: 2
pycatan/__init__.py CHANGED
@@ -15,6 +15,7 @@ from pycatan.actions import (
15
  GamePhase, TurnPhase, create_build_settlement_action, create_build_road_action,
16
  create_trade_action
17
  )
 
18
  from pycatan.user import User, UserInputError, validate_user_list, create_test_user
19
  from pycatan.human_user import HumanUser
20
  from pycatan.game_manager import GameManager
 
15
  GamePhase, TurnPhase, create_build_settlement_action, create_build_road_action,
16
  create_trade_action
17
  )
18
+ from pycatan.log_events import EventType, LogEntry, create_log_entry
19
  from pycatan.user import User, UserInputError, validate_user_list, create_test_user
20
  from pycatan.human_user import HumanUser
21
  from pycatan.game_manager import GameManager
pycatan/console_visualization.py CHANGED
@@ -10,6 +10,7 @@ from typing import Dict, Any, List, Optional
10
  from .visualization import Visualization
11
  from .actions import Action, ActionResult, ActionType, GameState
12
  from .card import ResCard, DevCard
 
13
 
14
 
15
  class ConsoleVisualization(Visualization):
@@ -642,4 +643,43 @@ class ConsoleVisualization(Visualization):
642
  print(" 'board' - Show detailed board layout")
643
  print(" 'robber' - Show robber information")
644
  print(" 'help' - Show all available commands")
645
- print()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  from .visualization import Visualization
11
  from .actions import Action, ActionResult, ActionType, GameState
12
  from .card import ResCard, DevCard
13
+ from .log_events import EventType, LogEntry, create_log_entry
14
 
15
 
16
  class ConsoleVisualization(Visualization):
 
643
  print(" 'board' - Show detailed board layout")
644
  print(" 'robber' - Show robber information")
645
  print(" 'help' - Show all available commands")
646
+ print()
647
+
648
+ def log_event(self, log_entry: LogEntry):
649
+ """
650
+ Log a structured event directly to console.
651
+ This allows GameManager to send detailed log entries.
652
+ """
653
+ if not self.enabled:
654
+ return
655
+
656
+ # Display human-readable version
657
+ message = log_entry.to_human_string()
658
+
659
+ # Color based on status
660
+ if log_entry.status == "SUCCESS":
661
+ color = 'green'
662
+ symbol = '✓'
663
+ elif log_entry.status == "FAIL":
664
+ color = 'red'
665
+ symbol = '✗'
666
+ else:
667
+ color = 'yellow'
668
+ symbol = '⏳'
669
+
670
+ self._print(f"{self.colors[color]}{symbol}{self.colors['reset']} {message}")
671
+
672
+ # If there's an error, display it
673
+ if log_entry.error:
674
+ self._print(f" {self.colors['red']}└─ Error: {log_entry.error}{self.colors['reset']}")
675
+
676
+ def log_structured(self, log_entry: LogEntry):
677
+ """
678
+ Log a structured event in machine-readable format.
679
+ Useful for debugging and data analysis.
680
+ """
681
+ if not self.enabled:
682
+ return
683
+
684
+ # Display structured log string
685
+ self._print(log_entry.to_log_string())
pycatan/game_manager.py CHANGED
@@ -1153,6 +1153,109 @@ class GameManager:
1153
  "ACTION_PROCESSING_ERROR"
1154
  )
1155
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1156
  def _update_all_systems(self, action: Action, result: ActionResult) -> None:
1157
  """
1158
  Updates all systems after an action has been executed.
@@ -1199,6 +1302,9 @@ class GameManager:
1199
  player_name = self.users[action.player_id].name if hasattr(self.users[action.player_id], 'name') else f"Player {action.player_id}"
1200
  action.parameters['player_name'] = player_name
1201
 
 
 
 
1202
  # Display the action result (success or failure)
1203
  self.visualization_manager.display_action(action, result)
1204
 
@@ -1429,28 +1535,26 @@ class GameManager:
1429
  die2 = random.randint(1, 6)
1430
  total = die1 + die2
1431
 
 
 
 
 
 
 
1432
  # Update state
1433
  self._current_game_state.dice_rolled = (die1, die2)
1434
 
1435
- # Notify visualization about dice roll
1436
- if self.visualization_manager:
1437
- player_name = self.users[action.player_id].name if hasattr(self.users[action.player_id], 'name') else f"Player {action.player_id + 1}"
1438
- self.visualization_manager.display_dice_roll(player_name, [die1, die2], total)
1439
-
1440
  # Distribute resources or handle robber
1441
  if total != 7:
1442
  distribution = self.game.add_yield_for_roll(total)
1443
 
1444
- # Notify visualization about resources (even if empty)
1445
- if self.visualization_manager:
1446
- if distribution:
1447
- self.visualization_manager.display_resource_distribution(distribution)
1448
- message = f"Rolled {total} ({die1}+{die2}). Resources distributed."
1449
- else:
1450
- # No resources were distributed (no settlements on this number)
1451
- message = f"Rolled {total} ({die1}+{die2}). No settlements on this number - no resources distributed."
1452
- else:
1453
  message = f"Rolled {total} ({die1}+{die2}). Resources distributed."
 
 
1454
  else:
1455
  # Rolled 7! Handle robber sequence
1456
  message = f"Rolled 7 ({die1}+{die2})! 🏴‍☠️ Robber activated!"
 
1153
  "ACTION_PROCESSING_ERROR"
1154
  )
1155
 
1156
+ def _enrich_action_parameters(self, action: Action, result: ActionResult) -> None:
1157
+ """
1158
+ Enrich action parameters with detailed information for visualization and logging.
1159
+ Adds all relevant details like point numbers, costs, card types, etc.
1160
+ """
1161
+ from .card import ResCard, DevCard
1162
+ from .board_definition import board_definition
1163
+
1164
+ # Ensure parameters dict exists
1165
+ if not hasattr(action, 'parameters') or action.parameters is None:
1166
+ action.parameters = {}
1167
+
1168
+ params = action.parameters
1169
+
1170
+ # Add turn number
1171
+ params['turn_number'] = self._current_game_state.turn_number
1172
+
1173
+ # Building actions - add point number and costs
1174
+ if action.action_type in [ActionType.BUILD_SETTLEMENT, ActionType.PLACE_STARTING_SETTLEMENT]:
1175
+ if 'point_coords' in params:
1176
+ coords = params['point_coords']
1177
+ try:
1178
+ point_id = board_definition.game_coords_to_point_id(coords[0], coords[1])
1179
+ params['point'] = point_id if point_id else f"[{coords[0]},{coords[1]}]"
1180
+ except:
1181
+ params['point'] = f"[{coords[0]},{coords[1]}]"
1182
+
1183
+ if result.success and action.action_type == ActionType.BUILD_SETTLEMENT:
1184
+ params['cost'] = ['WOOD', 'BRICK', 'WHEAT', 'SHEEP']
1185
+
1186
+ elif action.action_type == ActionType.BUILD_CITY:
1187
+ if 'point_coords' in params:
1188
+ coords = params['point_coords']
1189
+ try:
1190
+ point_id = board_definition.game_coords_to_point_id(coords[0], coords[1])
1191
+ params['point'] = point_id if point_id else f"[{coords[0]},{coords[1]}]"
1192
+ except:
1193
+ params['point'] = f"[{coords[0]},{coords[1]}]"
1194
+
1195
+ if result.success:
1196
+ params['cost'] = ['ORE', 'ORE', 'ORE', 'WHEAT', 'WHEAT']
1197
+
1198
+ elif action.action_type in [ActionType.BUILD_ROAD, ActionType.PLACE_STARTING_ROAD]:
1199
+ if 'start_coords' in params and 'end_coords' in params:
1200
+ start_coords = params['start_coords']
1201
+ end_coords = params['end_coords']
1202
+ try:
1203
+ start_id = board_definition.game_coords_to_point_id(start_coords[0], start_coords[1])
1204
+ end_id = board_definition.game_coords_to_point_id(end_coords[0], end_coords[1])
1205
+ params['points'] = [start_id, end_id] if (start_id and end_id) else [start_coords, end_coords]
1206
+ except:
1207
+ params['points'] = [start_coords, end_coords]
1208
+
1209
+ if result.success and action.action_type == ActionType.BUILD_ROAD:
1210
+ params['cost'] = ['WOOD', 'BRICK']
1211
+
1212
+ # Dev card actions
1213
+ elif action.action_type == ActionType.BUY_DEV_CARD:
1214
+ if result.success:
1215
+ # Try to get the actual card that was drawn
1216
+ player = self.game.players[action.player_id]
1217
+ if player.dev_cards:
1218
+ last_card = player.dev_cards[-1]
1219
+ params['card'] = last_card.name if hasattr(last_card, 'name') else str(last_card)
1220
+ params['cost'] = ['ORE', 'SHEEP', 'WHEAT']
1221
+
1222
+ elif action.action_type == ActionType.USE_DEV_CARD:
1223
+ if 'card_type' in params:
1224
+ card_type = params['card_type']
1225
+ params['card'] = card_type.name if hasattr(card_type, 'name') else str(card_type)
1226
+
1227
+ # Dice roll
1228
+ elif action.action_type == ActionType.ROLL_DICE:
1229
+ if 'dice' in params:
1230
+ params['total'] = sum(params['dice'])
1231
+
1232
+ # Robber actions
1233
+ elif action.action_type == ActionType.ROBBER_MOVE:
1234
+ if 'tile_coords' in params:
1235
+ params['tile'] = str(params['tile_coords'])
1236
+
1237
+ # Trading
1238
+ elif action.action_type == ActionType.TRADE_BANK:
1239
+ # params should already have 'give' and 'receive'
1240
+ pass
1241
+
1242
+ elif action.action_type == ActionType.TRADE_PROPOSE:
1243
+ if 'target_player' in params:
1244
+ target_id = params['target_player']
1245
+ target_name = self.users[target_id].name if hasattr(self.users[target_id], 'name') else f"Player {target_id}"
1246
+ params['to_player'] = target_name
1247
+
1248
+ # End turn - add player state
1249
+ elif action.action_type == ActionType.END_TURN:
1250
+ player = self.game.players[action.player_id]
1251
+ params['player_state'] = {
1252
+ 'victory_points': player.victory_points,
1253
+ 'card_count': len(player.cards),
1254
+ 'roads': len(player.roads),
1255
+ 'settlements': len(player.settlements),
1256
+ 'cities': len(player.cities)
1257
+ }
1258
+
1259
  def _update_all_systems(self, action: Action, result: ActionResult) -> None:
1260
  """
1261
  Updates all systems after an action has been executed.
 
1302
  player_name = self.users[action.player_id].name if hasattr(self.users[action.player_id], 'name') else f"Player {action.player_id}"
1303
  action.parameters['player_name'] = player_name
1304
 
1305
+ # Enrich action parameters with detailed information for logging
1306
+ self._enrich_action_parameters(action, result)
1307
+
1308
  # Display the action result (success or failure)
1309
  self.visualization_manager.display_action(action, result)
1310
 
 
1535
  die2 = random.randint(1, 6)
1536
  total = die1 + die2
1537
 
1538
+ # Update action parameters for logging/visualization
1539
+ if not hasattr(action, 'parameters') or action.parameters is None:
1540
+ action.parameters = {}
1541
+ action.parameters['dice'] = [die1, die2]
1542
+ action.parameters['total'] = total
1543
+
1544
  # Update state
1545
  self._current_game_state.dice_rolled = (die1, die2)
1546
 
 
 
 
 
 
1547
  # Distribute resources or handle robber
1548
  if total != 7:
1549
  distribution = self.game.add_yield_for_roll(total)
1550
 
1551
+ # Add distribution to action parameters for logging
1552
+ action.parameters['distribution'] = distribution
1553
+
1554
+ if distribution:
 
 
 
 
 
1555
  message = f"Rolled {total} ({die1}+{die2}). Resources distributed."
1556
+ else:
1557
+ message = f"Rolled {total} ({die1}+{die2}). No settlements on this number."
1558
  else:
1559
  # Rolled 7! Handle robber sequence
1560
  message = f"Rolled 7 ({die1}+{die2})! 🏴‍☠️ Robber activated!"
pycatan/log_events.py ADDED
@@ -0,0 +1,228 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Log Events - Structured logging for game actions
3
+ Provides EventType enum and LogEntry dataclass for detailed game logging
4
+ """
5
+
6
+ from enum import Enum
7
+ from dataclasses import dataclass, field
8
+ from datetime import datetime
9
+ from typing import Dict, Any, Optional, List
10
+
11
+
12
+ class EventType(Enum):
13
+ """All possible game events for structured logging"""
14
+ # Game flow
15
+ GAME_START = "GAME_START"
16
+ GAME_END = "GAME_END"
17
+ TURN_START = "TURN_START"
18
+ TURN_END = "TURN_END"
19
+ PHASE_CHANGE = "PHASE_CHANGE"
20
+
21
+ # Dice and resources
22
+ DICE_ROLL = "DICE_ROLL"
23
+ RESOURCE_DIST = "RESOURCE_DIST"
24
+ RESOURCE_LOSS = "RESOURCE_LOSS"
25
+
26
+ # Building actions
27
+ BUILD_SETTLEMENT = "BUILD_SETTLEMENT"
28
+ BUILD_CITY = "BUILD_CITY"
29
+ BUILD_ROAD = "BUILD_ROAD"
30
+
31
+ # Development cards
32
+ BUY_DEV_CARD = "BUY_DEV_CARD"
33
+ USE_DEV_CARD = "USE_DEV_CARD"
34
+
35
+ # Trading
36
+ TRADE_PROPOSE = "TRADE_PROPOSE"
37
+ TRADE_RESPONSE = "TRADE_RESPONSE"
38
+ TRADE_EXECUTE = "TRADE_EXECUTE"
39
+ TRADE_BANK = "TRADE_BANK"
40
+
41
+ # Robber
42
+ ROBBER_MOVE = "ROBBER_MOVE"
43
+ ROBBER_STEAL = "ROBBER_STEAL"
44
+ DISCARD_CARDS = "DISCARD_CARDS"
45
+
46
+ # Special
47
+ LONGEST_ROAD = "LONGEST_ROAD"
48
+ LARGEST_ARMY = "LARGEST_ARMY"
49
+ VICTORY = "VICTORY"
50
+
51
+ # Errors
52
+ ACTION_FAILED = "ACTION_FAILED"
53
+
54
+
55
+ @dataclass
56
+ class LogEntry:
57
+ """Structured log entry for a game event"""
58
+ timestamp: datetime
59
+ event_type: EventType
60
+ turn: int
61
+ player_id: Optional[int] = None
62
+ player_name: Optional[str] = None
63
+ data: Dict[str, Any] = field(default_factory=dict)
64
+ status: str = "SUCCESS" # SUCCESS, FAIL, WAITING, PENDING
65
+ error: Optional[str] = None
66
+
67
+ def to_dict(self) -> Dict[str, Any]:
68
+ """Convert to dictionary for JSON serialization"""
69
+ return {
70
+ 'timestamp': self.timestamp.isoformat(),
71
+ 'event_type': self.event_type.value,
72
+ 'turn': self.turn,
73
+ 'player_id': self.player_id,
74
+ 'player_name': self.player_name,
75
+ 'data': self.data,
76
+ 'status': self.status,
77
+ 'error': self.error
78
+ }
79
+
80
+ def to_log_string(self) -> str:
81
+ """
82
+ Convert to structured log string format:
83
+ [timestamp] EVENT_TYPE | key=value | key=value | ...
84
+ """
85
+ parts = [
86
+ f"[{self.timestamp.strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]}]",
87
+ self.event_type.value
88
+ ]
89
+
90
+ # Add turn
91
+ parts.append(f"turn={self.turn}")
92
+
93
+ # Add player info
94
+ if self.player_id is not None and self.player_name:
95
+ parts.append(f"player={self.player_id}({self.player_name})")
96
+ elif self.player_id is not None:
97
+ parts.append(f"player={self.player_id}")
98
+
99
+ # Add all data fields
100
+ for key, value in self.data.items():
101
+ if isinstance(value, list):
102
+ value_str = f"[{','.join(str(v) for v in value)}]"
103
+ elif isinstance(value, dict):
104
+ items = [f"{k}:{v}" for k, v in value.items()]
105
+ value_str = f"{{{','.join(items)}}}"
106
+ else:
107
+ value_str = str(value)
108
+ parts.append(f"{key}={value_str}")
109
+
110
+ # Add status if not SUCCESS
111
+ if self.status != "SUCCESS":
112
+ parts.append(f"status={self.status}")
113
+
114
+ # Add error if present
115
+ if self.error:
116
+ parts.append(f"error={self.error}")
117
+
118
+ return " | ".join(parts)
119
+
120
+ def to_human_string(self) -> str:
121
+ """Convert to human-readable string for display"""
122
+ # Player identifier
123
+ if self.player_name:
124
+ player_str = self.player_name
125
+ elif self.player_id is not None:
126
+ player_str = f"Player {self.player_id}"
127
+ else:
128
+ player_str = "System"
129
+
130
+ # Build message based on event type
131
+ if self.event_type == EventType.DICE_ROLL:
132
+ dice = self.data.get('dice', [])
133
+ total = self.data.get('total', sum(dice))
134
+ return f"🎲 {player_str} rolled {dice} = {total}"
135
+
136
+ elif self.event_type == EventType.RESOURCE_DIST:
137
+ resource = self.data.get('resource', '?')
138
+ recipients = self.data.get('recipients', [])
139
+ amounts = self.data.get('amounts', [])
140
+ distrib = ', '.join([f"Player {r}: {a}×{resource}" for r, a in zip(recipients, amounts)])
141
+ return f"📦 {distrib}"
142
+
143
+ elif self.event_type == EventType.BUILD_SETTLEMENT:
144
+ point = self.data.get('point', '?')
145
+ if self.status == "SUCCESS":
146
+ return f"🏠 {player_str} built settlement at point {point}"
147
+ else:
148
+ return f"❌ {player_str} failed to build settlement at point {point}: {self.error}"
149
+
150
+ elif self.event_type == EventType.BUILD_CITY:
151
+ point = self.data.get('point', '?')
152
+ if self.status == "SUCCESS":
153
+ return f"🏛️ {player_str} built city at point {point}"
154
+ else:
155
+ return f"❌ {player_str} failed to build city at point {point}: {self.error}"
156
+
157
+ elif self.event_type == EventType.BUILD_ROAD:
158
+ points = self.data.get('points', [])
159
+ if self.status == "SUCCESS":
160
+ return f"🛤️ {player_str} built road {points[0]}→{points[1]}"
161
+ else:
162
+ return f"❌ {player_str} failed to build road {points}: {self.error}"
163
+
164
+ elif self.event_type == EventType.BUY_DEV_CARD:
165
+ card = self.data.get('card', '?')
166
+ return f"🎴 {player_str} bought development card: {card}"
167
+
168
+ elif self.event_type == EventType.USE_DEV_CARD:
169
+ card = self.data.get('card', '?')
170
+ return f"✨ {player_str} used {card}"
171
+
172
+ elif self.event_type == EventType.TRADE_PROPOSE:
173
+ to_player = self.data.get('to_player', '?')
174
+ offer = self.data.get('offer', {})
175
+ request = self.data.get('request', {})
176
+ offer_str = ', '.join([f"{v}×{k}" for k, v in offer.items()])
177
+ request_str = ', '.join([f"{v}×{k}" for k, v in request.items()])
178
+ return f"💱 {player_str} proposed trade to {to_player}: [{offer_str}] for [{request_str}]"
179
+
180
+ elif self.event_type == EventType.TRADE_RESPONSE:
181
+ response = self.data.get('response', '?')
182
+ return f"💬 {player_str} {response} trade"
183
+
184
+ elif self.event_type == EventType.TRADE_EXECUTE:
185
+ details = self.data.get('details', '')
186
+ return f"✅ Trade executed: {details}"
187
+
188
+ elif self.event_type == EventType.ROBBER_MOVE:
189
+ tile = self.data.get('tile', '?')
190
+ return f"🦹 {player_str} moved robber to tile {tile}"
191
+
192
+ elif self.event_type == EventType.ROBBER_STEAL:
193
+ victim = self.data.get('victim', '?')
194
+ card = self.data.get('card', 'a card')
195
+ return f"🦹 {player_str} stole {card} from {victim}"
196
+
197
+ elif self.event_type == EventType.TURN_START:
198
+ phase = self.data.get('phase', 'MAIN')
199
+ return f"➤ Turn {self.turn}: {player_str}'s turn begins ({phase})"
200
+
201
+ elif self.event_type == EventType.TURN_END:
202
+ vp = self.data.get('vp', '?')
203
+ cards = self.data.get('cards', '?')
204
+ return f"⏹️ {player_str} ended turn [VP: {vp}, Cards: {cards}]"
205
+
206
+ elif self.event_type == EventType.VICTORY:
207
+ vp = self.data.get('vp', 10)
208
+ return f"🏆 {player_str} WON with {vp} victory points!"
209
+
210
+ else:
211
+ # Generic fallback
212
+ return f"• {player_str} performed {self.event_type.value}"
213
+
214
+
215
+ def create_log_entry(event_type: EventType, turn: int, player_id: Optional[int] = None,
216
+ player_name: Optional[str] = None, data: Optional[Dict[str, Any]] = None,
217
+ status: str = "SUCCESS", error: Optional[str] = None) -> LogEntry:
218
+ """Helper function to create a log entry"""
219
+ return LogEntry(
220
+ timestamp=datetime.now(),
221
+ event_type=event_type,
222
+ turn=turn,
223
+ player_id=player_id,
224
+ player_name=player_name,
225
+ data=data or {},
226
+ status=status,
227
+ error=error
228
+ )
pycatan/web_visualization.py CHANGED
@@ -22,6 +22,7 @@ except ImportError:
22
  from .visualization import Visualization
23
  from .actions import Action, ActionResult, GameState
24
  from .board_definition import board_definition
 
25
 
26
 
27
  class WebVisualization(Visualization):
@@ -65,6 +66,7 @@ class WebVisualization(Visualization):
65
  self.current_game_state = None
66
  self.action_history: List[Dict[str, Any]] = []
67
  self.event_history: List[Dict[str, Any]] = [] # Track all events (turn starts, dice rolls, etc.)
 
68
 
69
  # SSE (Server-Sent Events) for real-time updates
70
  self.sse_clients: List[Queue] = []
@@ -539,48 +541,141 @@ class WebVisualization(Visualization):
539
  if client in self.sse_clients:
540
  self.sse_clients.remove(client)
541
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
542
  def notify_action(self, action: Action, result: ActionResult):
543
  """Notify web clients of action execution."""
544
- timestamp = datetime.now().strftime("%H:%M:%S")
545
-
546
  # Get player name from action parameters (added by GameManager)
547
  player_name = action.parameters.get('player_name', f'Player {action.player_id + 1}') if hasattr(action, 'parameters') and action.parameters else f'Player {action.player_id + 1}'
548
 
549
- # Generate a better message for the web log
550
- message = result.error_message
551
- if result.success:
552
- action_name = action.action_type.name
553
- if action_name == 'BUILD_SETTLEMENT':
554
- message = f"{player_name} built a settlement"
555
- elif action_name == 'BUILD_CITY':
556
- message = f"{player_name} built a city"
557
- elif action_name == 'BUILD_ROAD':
558
- message = f"{player_name} built a road"
559
- elif action_name == 'BUY_DEV_CARD':
560
- message = f"{player_name} bought a development card"
561
- elif action_name == 'ROLL_DICE':
562
- message = f"{player_name} rolled dice"
563
- elif action_name == 'END_TURN':
564
- message = f"{player_name} ended turn"
565
- elif action_name == 'TRADE_BANK':
566
- message = f"{player_name} traded with bank"
567
- elif action_name == 'TRADE_PLAYER':
568
- message = f"{player_name} traded with player"
569
- else:
570
- message = f"{player_name} performed {action_name}"
571
-
572
  action_data = {
573
- 'timestamp': timestamp,
574
  'action_type': action.action_type.name,
 
575
  'player': action.player_id,
 
576
  'success': result.success,
577
- 'message': message
 
 
578
  }
579
 
580
  # Add to history
581
  self.action_history.append(action_data)
582
-
583
- # Keep only last 100 actions
584
  if len(self.action_history) > 100:
585
  self.action_history = self.action_history[-100:]
586
 
@@ -603,6 +698,42 @@ class WebVisualization(Visualization):
603
  'type': 'game_update',
604
  'payload': web_state
605
  })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
606
 
607
  # ===== ConsoleVisualization Interface Compatibility =====
608
  # Adding methods to match ConsoleVisualization interface
@@ -632,62 +763,55 @@ class WebVisualization(Visualization):
632
 
633
  def display_turn_start(self, player_name: str, turn_number: int) -> None:
634
  """Display turn start notification (ConsoleVisualization interface)."""
635
- timestamp = datetime.now().strftime("%H:%M:%S")
636
-
637
- message_data = {
638
- 'timestamp': timestamp,
639
- 'type': 'turn_start',
640
- 'player_name': player_name,
641
- 'turn_number': turn_number,
642
- 'message': f"Turn {turn_number}: {player_name}'s turn begins"
643
- }
644
 
645
- # Add to event history
646
- self.event_history.append(message_data)
647
- # Keep only last 100 events
648
- if len(self.event_history) > 100:
649
- self.event_history = self.event_history[-100:]
650
-
651
- # Broadcast to web clients
652
- self._broadcast_to_clients({
653
- 'type': 'turn_start',
654
- 'payload': message_data
655
- })
656
 
657
  def display_dice_roll(self, player_name: str, dice_values: List[int], total: int) -> None:
658
  """Display dice roll results (ConsoleVisualization interface)."""
659
- timestamp = datetime.now().strftime("%H:%M:%S")
660
-
661
- dice_data = {
662
- 'timestamp': timestamp,
663
- 'player_name': player_name,
664
- 'dice_values': dice_values,
665
- 'total': total,
666
- 'message': f"{player_name} rolled {dice_values} = {total}"
667
- }
668
-
669
- # Add to event history
670
- self.event_history.append(dice_data)
671
- if len(self.event_history) > 100:
672
- self.event_history = self.event_history[-100:]
673
 
674
- # Broadcast to web clients
675
- self._broadcast_to_clients({
676
- 'type': 'dice_roll',
677
- 'payload': dice_data
678
- })
679
 
680
  def display_resource_distribution(self, distributions: Dict[str, List[str]]) -> None:
681
  """Display resource distribution (ConsoleVisualization interface)."""
682
- timestamp = datetime.now().strftime("%H:%M:%S")
 
 
 
 
 
 
 
 
 
 
 
683
 
 
 
684
  distribution_data = {
685
  'timestamp': timestamp,
686
  'distributions': distributions,
687
  'message': "Resources distributed to players"
688
  }
689
-
690
- # Add to event history
691
  self.event_history.append(distribution_data)
692
  if len(self.event_history) > 100:
693
  self.event_history = self.event_history[-100:]
 
22
  from .visualization import Visualization
23
  from .actions import Action, ActionResult, GameState
24
  from .board_definition import board_definition
25
+ from .log_events import EventType, LogEntry, create_log_entry
26
 
27
 
28
  class WebVisualization(Visualization):
 
66
  self.current_game_state = None
67
  self.action_history: List[Dict[str, Any]] = []
68
  self.event_history: List[Dict[str, Any]] = [] # Track all events (turn starts, dice rolls, etc.)
69
+ self.log_entries: List[LogEntry] = [] # Structured log entries
70
 
71
  # SSE (Server-Sent Events) for real-time updates
72
  self.sse_clients: List[Queue] = []
 
541
  if client in self.sse_clients:
542
  self.sse_clients.remove(client)
543
 
544
+ def _map_action_to_event(self, action: Action, result: ActionResult) -> tuple:
545
+ """
546
+ Map ActionType to EventType and extract relevant data.
547
+ Returns: (EventType, data_dict)
548
+ """
549
+ from .actions import ActionType as AT
550
+
551
+ event_data = {}
552
+
553
+ # Extract common parameters
554
+ params = action.parameters if hasattr(action, 'parameters') else {}
555
+
556
+ if action.action_type == AT.BUILD_SETTLEMENT or action.action_type == AT.PLACE_STARTING_SETTLEMENT:
557
+ event_type = EventType.BUILD_SETTLEMENT
558
+ event_data['point'] = params.get('point', '?')
559
+ event_data['is_starting'] = action.action_type == AT.PLACE_STARTING_SETTLEMENT
560
+ if result.success and 'cost' in params:
561
+ event_data['cost'] = params['cost']
562
+
563
+ elif action.action_type == AT.BUILD_CITY:
564
+ event_type = EventType.BUILD_CITY
565
+ event_data['point'] = params.get('point', '?')
566
+ if result.success and 'cost' in params:
567
+ event_data['cost'] = params['cost']
568
+
569
+ elif action.action_type == AT.BUILD_ROAD or action.action_type == AT.PLACE_STARTING_ROAD:
570
+ event_type = EventType.BUILD_ROAD
571
+ event_data['points'] = params.get('points', ['?', '?'])
572
+ event_data['is_starting'] = action.action_type == AT.PLACE_STARTING_ROAD
573
+ if result.success and 'cost' in params:
574
+ event_data['cost'] = params['cost']
575
+
576
+ elif action.action_type == AT.BUY_DEV_CARD:
577
+ event_type = EventType.BUY_DEV_CARD
578
+ event_data['card'] = params.get('card', 'Unknown')
579
+ if result.success:
580
+ event_data['cost'] = ['ORE', 'SHEEP', 'WHEAT']
581
+
582
+ elif action.action_type == AT.USE_DEV_CARD:
583
+ event_type = EventType.USE_DEV_CARD
584
+ event_data['card'] = params.get('card_type', 'Unknown')
585
+ if 'details' in params:
586
+ event_data['details'] = params['details']
587
+
588
+ elif action.action_type == AT.ROLL_DICE:
589
+ event_type = EventType.DICE_ROLL
590
+ event_data['dice'] = params.get('dice', [0, 0])
591
+ event_data['total'] = sum(event_data['dice'])
592
+
593
+ elif action.action_type == AT.ROBBER_MOVE:
594
+ event_type = EventType.ROBBER_MOVE
595
+ event_data['tile'] = params.get('tile', '?')
596
+ if 'victim' in params:
597
+ event_data['victim'] = params['victim']
598
+
599
+ elif action.action_type == AT.TRADE_BANK:
600
+ event_type = EventType.TRADE_BANK
601
+ event_data['give'] = params.get('give', {})
602
+ event_data['receive'] = params.get('receive', {})
603
+
604
+ elif action.action_type == AT.TRADE_PROPOSE:
605
+ event_type = EventType.TRADE_PROPOSE
606
+ event_data['to_player'] = params.get('to_player', '?')
607
+ event_data['offer'] = params.get('offer', {})
608
+ event_data['request'] = params.get('request', {})
609
+
610
+ elif action.action_type == AT.TRADE_RESPOND:
611
+ event_type = EventType.TRADE_RESPONSE
612
+ event_data['response'] = params.get('response', 'UNKNOWN')
613
+
614
+ elif action.action_type == AT.DISCARD_CARDS:
615
+ event_type = EventType.DISCARD_CARDS
616
+ event_data['cards'] = params.get('cards', [])
617
+ event_data['count'] = len(event_data['cards'])
618
+
619
+ elif action.action_type == AT.END_TURN:
620
+ event_type = EventType.TURN_END
621
+ # Add player state at end of turn
622
+ if 'player_state' in params:
623
+ ps = params['player_state']
624
+ event_data['vp'] = ps.get('victory_points', 0)
625
+ event_data['cards'] = ps.get('card_count', 0)
626
+ event_data['roads'] = ps.get('roads', 0)
627
+ event_data['settlements'] = ps.get('settlements', 0)
628
+ event_data['cities'] = ps.get('cities', 0)
629
+
630
+ else:
631
+ # Generic action
632
+ event_type = EventType.ACTION_FAILED if not result.success else EventType.TURN_START
633
+ event_data['action'] = action.action_type.name
634
+
635
+ return event_type, event_data
636
+
637
  def notify_action(self, action: Action, result: ActionResult):
638
  """Notify web clients of action execution."""
 
 
639
  # Get player name from action parameters (added by GameManager)
640
  player_name = action.parameters.get('player_name', f'Player {action.player_id + 1}') if hasattr(action, 'parameters') and action.parameters else f'Player {action.player_id + 1}'
641
 
642
+ # Map ActionType to EventType and extract relevant data
643
+ event_type, event_data = self._map_action_to_event(action, result)
644
+
645
+ # Get turn number from action parameters (added by GameManager)
646
+ turn_number = action.parameters.get('turn_number', 0) if hasattr(action, 'parameters') and action.parameters else 0
647
+
648
+ # Create structured log entry
649
+ log_entry = create_log_entry(
650
+ event_type=event_type,
651
+ turn=turn_number,
652
+ player_id=action.player_id,
653
+ player_name=player_name,
654
+ data=event_data,
655
+ status="SUCCESS" if result.success else "FAIL",
656
+ error=result.error_message if not result.success else None
657
+ )
658
+
659
+ # Store log entry
660
+ self.log_entries.append(log_entry)
661
+ if len(self.log_entries) > 100:
662
+ self.log_entries = self.log_entries[-100:]
663
+
664
+ # Create display data from log entry
665
  action_data = {
666
+ 'timestamp': log_entry.timestamp.strftime("%H:%M:%S.%f")[:-3],
667
  'action_type': action.action_type.name,
668
+ 'event_type': event_type.value,
669
  'player': action.player_id,
670
+ 'player_name': player_name,
671
  'success': result.success,
672
+ 'message': log_entry.to_human_string(),
673
+ 'structured': log_entry.to_log_string(),
674
+ 'data': event_data
675
  }
676
 
677
  # Add to history
678
  self.action_history.append(action_data)
 
 
679
  if len(self.action_history) > 100:
680
  self.action_history = self.action_history[-100:]
681
 
 
698
  'type': 'game_update',
699
  'payload': web_state
700
  })
701
+
702
+ def log_event(self, log_entry: LogEntry):
703
+ """
704
+ Log a structured event directly.
705
+ This allows GameManager to send detailed log entries.
706
+ """
707
+ # Store log entry
708
+ self.log_entries.append(log_entry)
709
+ if len(self.log_entries) > 100:
710
+ self.log_entries = self.log_entries[-100:]
711
+
712
+ # Create display data
713
+ event_data = {
714
+ 'timestamp': log_entry.timestamp.strftime("%H:%M:%S.%f")[:-3],
715
+ 'event_type': log_entry.event_type.value,
716
+ 'turn': log_entry.turn,
717
+ 'player_id': log_entry.player_id,
718
+ 'player_name': log_entry.player_name,
719
+ 'success': log_entry.status == "SUCCESS",
720
+ 'status': log_entry.status,
721
+ 'message': log_entry.to_human_string(),
722
+ 'structured': log_entry.to_log_string(),
723
+ 'data': log_entry.data,
724
+ 'error': log_entry.error
725
+ }
726
+
727
+ # Add to history
728
+ self.event_history.append(event_data)
729
+ if len(self.event_history) > 100:
730
+ self.event_history = self.event_history[-100:]
731
+
732
+ # Broadcast to web clients
733
+ self._broadcast_to_clients({
734
+ 'type': 'log_event',
735
+ 'payload': event_data
736
+ })
737
 
738
  # ===== ConsoleVisualization Interface Compatibility =====
739
  # Adding methods to match ConsoleVisualization interface
 
763
 
764
  def display_turn_start(self, player_name: str, turn_number: int) -> None:
765
  """Display turn start notification (ConsoleVisualization interface)."""
766
+ # Create structured log entry
767
+ log_entry = create_log_entry(
768
+ event_type=EventType.TURN_START,
769
+ turn=turn_number,
770
+ player_name=player_name,
771
+ data={'phase': 'MAIN'}
772
+ )
 
 
773
 
774
+ # Use the log_event method
775
+ self.log_event(log_entry)
 
 
 
 
 
 
 
 
 
776
 
777
  def display_dice_roll(self, player_name: str, dice_values: List[int], total: int) -> None:
778
  """Display dice roll results (ConsoleVisualization interface)."""
779
+ # Create structured log entry
780
+ log_entry = create_log_entry(
781
+ event_type=EventType.DICE_ROLL,
782
+ turn=0, # Turn number will be set by GameManager
783
+ player_name=player_name,
784
+ data={
785
+ 'dice': dice_values,
786
+ 'total': total
787
+ }
788
+ )
 
 
 
 
789
 
790
+ # Use the log_event method
791
+ self.log_event(log_entry)
 
 
 
792
 
793
  def display_resource_distribution(self, distributions: Dict[str, List[str]]) -> None:
794
  """Display resource distribution (ConsoleVisualization interface)."""
795
+ # Create structured log entry for each resource distribution
796
+ for resource, players in distributions.items():
797
+ log_entry = create_log_entry(
798
+ event_type=EventType.RESOURCE_DIST,
799
+ turn=0, # Turn number will be set by GameManager
800
+ data={
801
+ 'resource': resource,
802
+ 'recipients': list(range(len(players))), # Player IDs
803
+ 'amounts': [1] * len(players) # Assuming 1 card each
804
+ }
805
+ )
806
+ self.log_event(log_entry)
807
 
808
+ # Keep compatibility with old event history format
809
+ timestamp = datetime.now().strftime("%H:%M:%S")
810
  distribution_data = {
811
  'timestamp': timestamp,
812
  'distributions': distributions,
813
  'message': "Resources distributed to players"
814
  }
 
 
815
  self.event_history.append(distribution_data)
816
  if len(self.event_history) > 100:
817
  self.event_history = self.event_history[-100:]
test_dice_log.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Quick test to check dice logging"""
2
+
3
+ from pycatan import Game, GameManager, HumanUser, WebVisualization, ConsoleVisualization
4
+ from pycatan.actions import ActionType, Action
5
+
6
+ # Create a simple game
7
+ users = [HumanUser("Alice"), HumanUser("Bob"), HumanUser("Charlie")]
8
+ viz = ConsoleVisualization(use_colors=False)
9
+ manager = GameManager(users, [viz])
10
+
11
+ # Start game
12
+ manager.start_game()
13
+
14
+ # Create a roll dice action
15
+ action = Action(
16
+ action_type=ActionType.ROLL_DICE,
17
+ player_id=0,
18
+ parameters={}
19
+ )
20
+
21
+ # Execute it
22
+ result = manager.execute_action(action)
23
+
24
+ print("\n=== Action Parameters After Execution ===")
25
+ print(f"Parameters: {action.parameters}")
26
+ print(f"Dice: {action.parameters.get('dice', 'NOT FOUND')}")
27
+ print(f"Total: {action.parameters.get('total', 'NOT FOUND')}")