Spaces:
Configuration error
Configuration error
EZTIME2025 commited on
Commit ·
548394f
1
Parent(s): 4dd77af
improve log system
Browse files- .github/instructions/BUILD_PLAN.md +34 -12
- game_viz.log +48 -260
- pycatan/__init__.py +1 -0
- pycatan/console_visualization.py +41 -1
- pycatan/game_manager.py +118 -14
- pycatan/log_events.py +228 -0
- pycatan/web_visualization.py +195 -71
- test_dice_log.py +27 -0
.github/instructions/BUILD_PLAN.md
CHANGED
|
@@ -61,7 +61,7 @@
|
|
| 61 |
|
| 62 |
### משימה 3.3: End-to-End Testing
|
| 63 |
**סטטוס:** 🟢 בעבודה פעילה!
|
| 64 |
-
**עדכון אחרון:**
|
| 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 |
-
- [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
|
| 148 |
### משימה 5.2: Robber Interactions
|
| 149 |
-
**סטטוס:**
|
|
|
|
| 150 |
**זמן משוער:** 2-3 שעות
|
| 151 |
|
| 152 |
-
- [
|
| 153 |
-
- [
|
| 154 |
-
- [
|
| 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: [1m[92m► a[0m
|
|
| 22 |
Resources: None
|
| 23 |
Buildings: Settlements: [91m0[0m, Cities: [91m0[0m, Roads: [91m0[0m
|
| 24 |
|
| 25 |
-
[
|
| 26 |
Victory Points: [97m0[0m
|
| 27 |
Resources: None
|
| 28 |
Buildings: Settlements: [91m0[0m, Cities: [91m0[0m, Roads: [91m0[0m
|
|
@@ -53,7 +53,7 @@ Current Player: [1m[92m► a[0m
|
|
| 53 |
Resources: None
|
| 54 |
Buildings: Settlements: [91m0[0m, Cities: [91m0[0m, Roads: [91m0[0m
|
| 55 |
|
| 56 |
-
[
|
| 57 |
Victory Points: [97m0[0m
|
| 58 |
Resources: None
|
| 59 |
Buildings: Settlements: [91m0[0m, Cities: [91m0[0m, Roads: [91m0[0m
|
|
@@ -86,7 +86,7 @@ Current Player: [1m[92m► b[0m
|
|
| 86 |
Resources: None
|
| 87 |
Buildings: Settlements: [92m1[0m, Cities: [91m0[0m, Roads: [91m0[0m
|
| 88 |
|
| 89 |
-
[
|
| 90 |
Victory Points: [97m0[0m
|
| 91 |
Resources: None
|
| 92 |
Buildings: Settlements: [91m0[0m, Cities: [91m0[0m, Roads: [91m0[0m
|
|
@@ -117,7 +117,7 @@ Current Player: [1m[92m► b[0m
|
|
| 117 |
Resources: None
|
| 118 |
Buildings: Settlements: [92m1[0m, Cities: [91m0[0m, Roads: [92m1[0m
|
| 119 |
|
| 120 |
-
[
|
| 121 |
Victory Points: [97m0[0m
|
| 122 |
Resources: None
|
| 123 |
Buildings: Settlements: [91m0[0m, Cities: [91m0[0m, Roads: [91m0[0m
|
|
@@ -127,15 +127,15 @@ Current Player: [1m[92m► b[0m
|
|
| 127 |
Board Tiles: 19 tiles configured
|
| 128 |
|
| 129 |
|
| 130 |
-
[1m[94m>>> Turn 2:
|
| 131 |
-
[92m✓[0m
|
| 132 |
|
| 133 |
[1m[96m==================================================[0m
|
| 134 |
[1m[96m GAME STATE [0m
|
| 135 |
[1m[96m==================================================[0m
|
| 136 |
|
| 137 |
Turn: [1m2[0m
|
| 138 |
-
Current Player: [1m[92m►
|
| 139 |
|
| 140 |
[1m[93mPLAYERS[0m
|
| 141 |
[93m-------[0m
|
|
@@ -150,7 +150,7 @@ Current Player: [1m[92m► s[0m
|
|
| 150 |
Resources: None
|
| 151 |
Buildings: Settlements: [92m1[0m, Cities: [91m0[0m, Roads: [92m1[0m
|
| 152 |
|
| 153 |
-
[1m[92m►
|
| 154 |
Victory Points: [97m1[0m
|
| 155 |
Resources: None
|
| 156 |
Buildings: Settlements: [92m1[0m, Cities: [91m0[0m, Roads: [91m0[0m
|
|
@@ -159,14 +159,14 @@ Current Player: [1m[92m► s[0m
|
|
| 159 |
[93m-----[0m
|
| 160 |
Board Tiles: 19 tiles configured
|
| 161 |
|
| 162 |
-
[92m✓[0m
|
| 163 |
|
| 164 |
[1m[96m==================================================[0m
|
| 165 |
[1m[96m GAME STATE [0m
|
| 166 |
[1m[96m==================================================[0m
|
| 167 |
|
| 168 |
Turn: [1m2[0m
|
| 169 |
-
Current Player: [1m[92m►
|
| 170 |
|
| 171 |
[1m[93mPLAYERS[0m
|
| 172 |
[93m-------[0m
|
|
@@ -181,7 +181,7 @@ Current Player: [1m[92m► s[0m
|
|
| 181 |
Resources: None
|
| 182 |
Buildings: Settlements: [92m1[0m, Cities: [91m0[0m, Roads: [92m1[0m
|
| 183 |
|
| 184 |
-
[1m[92m►
|
| 185 |
Victory Points: [97m1[0m
|
| 186 |
Resources: None
|
| 187 |
Buildings: Settlements: [92m1[0m, Cities: [91m0[0m, Roads: [92m1[0m
|
|
@@ -191,18 +191,18 @@ Current Player: [1m[92m► s[0m
|
|
| 191 |
Board Tiles: 19 tiles configured
|
| 192 |
|
| 193 |
|
| 194 |
-
[1m[94m>>> Turn 3:
|
| 195 |
|
| 196 |
📦 Resources distributed:
|
| 197 |
-
|
| 198 |
-
[92m✓[0m
|
| 199 |
|
| 200 |
[1m[96m==================================================[0m
|
| 201 |
[1m[96m GAME STATE [0m
|
| 202 |
[1m[96m==================================================[0m
|
| 203 |
|
| 204 |
Turn: [1m3[0m
|
| 205 |
-
Current Player: [1m[92m►
|
| 206 |
|
| 207 |
[1m[93mPLAYERS[0m
|
| 208 |
[93m-------[0m
|
|
@@ -217,7 +217,7 @@ Current Player: [1m[92m► s[0m
|
|
| 217 |
Resources: None
|
| 218 |
Buildings: Settlements: [92m1[0m, Cities: [91m0[0m, Roads: [92m1[0m
|
| 219 |
|
| 220 |
-
[1m[92m►
|
| 221 |
Victory Points: [97m2[0m
|
| 222 |
Resources: None
|
| 223 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m1[0m
|
|
@@ -226,14 +226,14 @@ Current Player: [1m[92m► s[0m
|
|
| 226 |
[93m-----[0m
|
| 227 |
Board Tiles: 19 tiles configured
|
| 228 |
|
| 229 |
-
[92m✓[0m
|
| 230 |
|
| 231 |
[1m[96m==================================================[0m
|
| 232 |
[1m[96m GAME STATE [0m
|
| 233 |
[1m[96m==================================================[0m
|
| 234 |
|
| 235 |
Turn: [1m3[0m
|
| 236 |
-
Current Player: [1m[92m►
|
| 237 |
|
| 238 |
[1m[93mPLAYERS[0m
|
| 239 |
[93m-------[0m
|
|
@@ -248,7 +248,7 @@ Current Player: [1m[92m► s[0m
|
|
| 248 |
Resources: None
|
| 249 |
Buildings: Settlements: [92m1[0m, Cities: [91m0[0m, Roads: [92m1[0m
|
| 250 |
|
| 251 |
-
[1m[92m►
|
| 252 |
Victory Points: [97m2[0m
|
| 253 |
Resources: None
|
| 254 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
|
@@ -284,7 +284,7 @@ Current Player: [1m[92m► b[0m
|
|
| 284 |
Resources: None
|
| 285 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m1[0m
|
| 286 |
|
| 287 |
-
[
|
| 288 |
Victory Points: [97m2[0m
|
| 289 |
Resources: None
|
| 290 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
|
@@ -315,7 +315,7 @@ Current Player: [1m[92m► b[0m
|
|
| 315 |
Resources: None
|
| 316 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 317 |
|
| 318 |
-
[
|
| 319 |
Victory Points: [97m2[0m
|
| 320 |
Resources: None
|
| 321 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
|
@@ -351,7 +351,7 @@ Current Player: [1m[92m► a[0m
|
|
| 351 |
Resources: None
|
| 352 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 353 |
|
| 354 |
-
[
|
| 355 |
Victory Points: [97m2[0m
|
| 356 |
Resources: None
|
| 357 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
|
@@ -382,7 +382,7 @@ Current Player: [1m[92m► a[0m
|
|
| 382 |
Resources: None
|
| 383 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 384 |
|
| 385 |
-
[
|
| 386 |
Victory Points: [97m2[0m
|
| 387 |
Resources: None
|
| 388 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
|
@@ -393,12 +393,6 @@ Board Tiles: 19 tiles configured
|
|
| 393 |
|
| 394 |
|
| 395 |
[1m[94m>>> Turn 6: a's turn[0m
|
| 396 |
-
|
| 397 |
-
[1m🎲 a rolled: 5 + 3 = [97m8[0m
|
| 398 |
-
|
| 399 |
-
📦 Resources distributed:
|
| 400 |
-
Player 1: [92mSheep[0m
|
| 401 |
-
Player 2: [92mWheat[0m
|
| 402 |
[92m✓[0m a rolled the dice
|
| 403 |
|
| 404 |
[1m[96m==================================================[0m
|
|
@@ -421,38 +415,7 @@ Current Player: [1m[92m► a[0m
|
|
| 421 |
Resources: None
|
| 422 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 423 |
|
| 424 |
-
[
|
| 425 |
-
Victory Points: [97m2[0m
|
| 426 |
-
Resources: None
|
| 427 |
-
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 428 |
-
|
| 429 |
-
[1m[93mBOARD[0m
|
| 430 |
-
[93m-----[0m
|
| 431 |
-
Board Tiles: 19 tiles configured
|
| 432 |
-
|
| 433 |
-
[92m✓[0m a ended their turn
|
| 434 |
-
|
| 435 |
-
[1m[96m==================================================[0m
|
| 436 |
-
[1m[96m GAME STATE [0m
|
| 437 |
-
[1m[96m==================================================[0m
|
| 438 |
-
|
| 439 |
-
Turn: [1m6[0m
|
| 440 |
-
Current Player: [1m[92m► a[0m
|
| 441 |
-
|
| 442 |
-
[1m[93mPLAYERS[0m
|
| 443 |
-
[93m-------[0m
|
| 444 |
-
|
| 445 |
-
[1m[92m► a[0m
|
| 446 |
-
Victory Points: [97m2[0m
|
| 447 |
-
Resources: None
|
| 448 |
-
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 449 |
-
|
| 450 |
-
[97mb[0m
|
| 451 |
-
Victory Points: [97m2[0m
|
| 452 |
-
Resources: None
|
| 453 |
-
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 454 |
-
|
| 455 |
-
[97ms[0m
|
| 456 |
Victory Points: [97m2[0m
|
| 457 |
Resources: None
|
| 458 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
|
@@ -463,8 +426,6 @@ Board Tiles: 19 tiles configured
|
|
| 463 |
|
| 464 |
|
| 465 |
[1m[94m>>> Turn 7: b's turn[0m
|
| 466 |
-
|
| 467 |
-
[1m🎲 b rolled: 6 + 1 = [91m7[0m
|
| 468 |
[92m✓[0m b rolled the dice
|
| 469 |
|
| 470 |
[1m[96m==================================================[0m
|
|
@@ -487,7 +448,7 @@ Current Player: [1m[92m► b[0m
|
|
| 487 |
Resources: None
|
| 488 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 489 |
|
| 490 |
-
[
|
| 491 |
Victory Points: [97m2[0m
|
| 492 |
Resources: None
|
| 493 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
|
@@ -518,7 +479,7 @@ Current Player: [1m[92m► b[0m
|
|
| 518 |
Resources: None
|
| 519 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 520 |
|
| 521 |
-
[
|
| 522 |
Victory Points: [97m2[0m
|
| 523 |
Resources: None
|
| 524 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
|
@@ -527,84 +488,16 @@ Current Player: [1m[92m► b[0m
|
|
| 527 |
[93m-----[0m
|
| 528 |
Board Tiles: 19 tiles configured
|
| 529 |
|
| 530 |
-
[92m✓[0m b ended their turn
|
| 531 |
-
|
| 532 |
-
[1m[96m==================================================[0m
|
| 533 |
-
[1m[96m GAME STATE [0m
|
| 534 |
-
[1m[96m==================================================[0m
|
| 535 |
-
|
| 536 |
-
Turn: [1m7[0m
|
| 537 |
-
Current Player: [1m[92m► b[0m
|
| 538 |
-
|
| 539 |
-
[1m[93mPLAYERS[0m
|
| 540 |
-
[93m-------[0m
|
| 541 |
-
|
| 542 |
-
[97ma[0m
|
| 543 |
-
Victory Points: [97m2[0m
|
| 544 |
-
Resources: None
|
| 545 |
-
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 546 |
-
|
| 547 |
-
[1m[92m► b[0m
|
| 548 |
-
Victory Points: [97m2[0m
|
| 549 |
-
Resources: None
|
| 550 |
-
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 551 |
-
|
| 552 |
-
[97ms[0m
|
| 553 |
-
Victory Points: [97m2[0m
|
| 554 |
-
Resources: None
|
| 555 |
-
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 556 |
-
|
| 557 |
-
[1m[93mBOARD[0m
|
| 558 |
-
[93m-----[0m
|
| 559 |
-
Board Tiles: 19 tiles configured
|
| 560 |
-
|
| 561 |
-
|
| 562 |
-
[1m[94m>>> Turn 8: s's turn[0m
|
| 563 |
-
|
| 564 |
-
[1m🎲 s rolled: 3 + 5 = [97m8[0m
|
| 565 |
-
|
| 566 |
-
📦 Resources distributed:
|
| 567 |
-
Player 1: [92mSheep[0m
|
| 568 |
-
Player 2: [92mWheat[0m
|
| 569 |
-
[92m✓[0m s rolled the dice
|
| 570 |
-
|
| 571 |
-
[1m[96m==================================================[0m
|
| 572 |
-
[1m[96m GAME STATE [0m
|
| 573 |
-
[1m[96m==================================================[0m
|
| 574 |
-
|
| 575 |
-
Turn: [1m8[0m
|
| 576 |
-
Current Player: [1m[92m► s[0m
|
| 577 |
-
|
| 578 |
-
[1m[93mPLAYERS[0m
|
| 579 |
-
[93m-------[0m
|
| 580 |
-
|
| 581 |
-
[97ma[0m
|
| 582 |
-
Victory Points: [97m2[0m
|
| 583 |
-
Resources: None
|
| 584 |
-
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 585 |
-
|
| 586 |
-
[97mb[0m
|
| 587 |
-
Victory Points: [97m2[0m
|
| 588 |
-
Resources: None
|
| 589 |
-
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 590 |
-
|
| 591 |
-
[1m[92m► s[0m
|
| 592 |
-
Victory Points: [97m2[0m
|
| 593 |
-
Resources: None
|
| 594 |
-
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 595 |
-
|
| 596 |
-
[1m[93mBOARD[0m
|
| 597 |
-
[93m-----[0m
|
| 598 |
-
Board Tiles: 19 tiles configured
|
| 599 |
|
| 600 |
-
[
|
|
|
|
| 601 |
|
| 602 |
[1m[96m==================================================[0m
|
| 603 |
[1m[96m GAME STATE [0m
|
| 604 |
[1m[96m==================================================[0m
|
| 605 |
|
| 606 |
Turn: [1m8[0m
|
| 607 |
-
Current Player: [1m[92m►
|
| 608 |
|
| 609 |
[1m[93mPLAYERS[0m
|
| 610 |
[93m-------[0m
|
|
@@ -619,7 +512,7 @@ Current Player: [1m[92m► s[0m
|
|
| 619 |
Resources: None
|
| 620 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 621 |
|
| 622 |
-
[1m[92m►
|
| 623 |
Victory Points: [97m2[0m
|
| 624 |
Resources: None
|
| 625 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
|
@@ -630,8 +523,6 @@ Board Tiles: 19 tiles configured
|
|
| 630 |
|
| 631 |
|
| 632 |
[1m[94m>>> Turn 9: a's turn[0m
|
| 633 |
-
|
| 634 |
-
[1m🎲 a rolled: 2 + 5 = [91m7[0m
|
| 635 |
[92m✓[0m a rolled the dice
|
| 636 |
|
| 637 |
[1m[96m==================================================[0m
|
|
@@ -654,7 +545,7 @@ Current Player: [1m[92m► a[0m
|
|
| 654 |
Resources: None
|
| 655 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 656 |
|
| 657 |
-
[
|
| 658 |
Victory Points: [97m2[0m
|
| 659 |
Resources: None
|
| 660 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
|
@@ -685,38 +576,7 @@ Current Player: [1m[92m► a[0m
|
|
| 685 |
Resources: None
|
| 686 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 687 |
|
| 688 |
-
[
|
| 689 |
-
Victory Points: [97m2[0m
|
| 690 |
-
Resources: None
|
| 691 |
-
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 692 |
-
|
| 693 |
-
[1m[93mBOARD[0m
|
| 694 |
-
[93m-----[0m
|
| 695 |
-
Board Tiles: 19 tiles configured
|
| 696 |
-
|
| 697 |
-
[92m✓[0m a ended their turn
|
| 698 |
-
|
| 699 |
-
[1m[96m==================================================[0m
|
| 700 |
-
[1m[96m GAME STATE [0m
|
| 701 |
-
[1m[96m==================================================[0m
|
| 702 |
-
|
| 703 |
-
Turn: [1m9[0m
|
| 704 |
-
Current Player: [1m[92m► a[0m
|
| 705 |
-
|
| 706 |
-
[1m[93mPLAYERS[0m
|
| 707 |
-
[93m-------[0m
|
| 708 |
-
|
| 709 |
-
[1m[92m► a[0m
|
| 710 |
-
Victory Points: [97m2[0m
|
| 711 |
-
Resources: None
|
| 712 |
-
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 713 |
-
|
| 714 |
-
[97mb[0m
|
| 715 |
-
Victory Points: [97m2[0m
|
| 716 |
-
Resources: None
|
| 717 |
-
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 718 |
-
|
| 719 |
-
[97ms[0m
|
| 720 |
Victory Points: [97m2[0m
|
| 721 |
Resources: None
|
| 722 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
|
@@ -727,11 +587,6 @@ Board Tiles: 19 tiles configured
|
|
| 727 |
|
| 728 |
|
| 729 |
[1m[94m>>> Turn 10: b's turn[0m
|
| 730 |
-
|
| 731 |
-
[1m🎲 b rolled: 4 + 1 = [97m5[0m
|
| 732 |
-
|
| 733 |
-
📦 Resources distributed:
|
| 734 |
-
Player 1: [92mOre[0m
|
| 735 |
[92m✓[0m b rolled the dice
|
| 736 |
|
| 737 |
[1m[96m==================================================[0m
|
|
@@ -754,7 +609,7 @@ Current Player: [1m[92m► b[0m
|
|
| 754 |
Resources: None
|
| 755 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 756 |
|
| 757 |
-
[
|
| 758 |
Victory Points: [97m2[0m
|
| 759 |
Resources: None
|
| 760 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
|
@@ -786,7 +641,7 @@ Current Player: [1m[92m► b[0m
|
|
| 786 |
Dev Cards: 1
|
| 787 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 788 |
|
| 789 |
-
[
|
| 790 |
Victory Points: [97m2[0m
|
| 791 |
Resources: None
|
| 792 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
|
@@ -817,7 +672,7 @@ Current Player: [1m[92m► b[0m
|
|
| 817 |
Resources: None
|
| 818 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m4[0m
|
| 819 |
|
| 820 |
-
[
|
| 821 |
Victory Points: [97m2[0m
|
| 822 |
Resources: None
|
| 823 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
|
@@ -826,52 +681,16 @@ Current Player: [1m[92m► b[0m
|
|
| 826 |
[93m-----[0m
|
| 827 |
Board Tiles: 19 tiles configured
|
| 828 |
|
| 829 |
-
[92m✓[0m b ended their turn
|
| 830 |
|
| 831 |
-
[1m[
|
| 832 |
-
[
|
| 833 |
-
[1m[96m==================================================[0m
|
| 834 |
-
|
| 835 |
-
Turn: [1m10[0m
|
| 836 |
-
Current Player: [1m[92m► b[0m
|
| 837 |
-
|
| 838 |
-
[1m[93mPLAYERS[0m
|
| 839 |
-
[93m-------[0m
|
| 840 |
-
|
| 841 |
-
[97ma[0m
|
| 842 |
-
Victory Points: [97m2[0m
|
| 843 |
-
Resources: None
|
| 844 |
-
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 845 |
-
|
| 846 |
-
[1m[92m► b[0m
|
| 847 |
-
Victory Points: [97m2[0m
|
| 848 |
-
Resources: None
|
| 849 |
-
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m4[0m
|
| 850 |
-
|
| 851 |
-
[97ms[0m
|
| 852 |
-
Victory Points: [97m2[0m
|
| 853 |
-
Resources: None
|
| 854 |
-
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 855 |
-
|
| 856 |
-
[1m[93mBOARD[0m
|
| 857 |
-
[93m-----[0m
|
| 858 |
-
Board Tiles: 19 tiles configured
|
| 859 |
-
|
| 860 |
-
|
| 861 |
-
[1m[94m>>> Turn 11: s's turn[0m
|
| 862 |
-
|
| 863 |
-
[1m🎲 s rolled: 5 + 4 = [97m9[0m
|
| 864 |
-
|
| 865 |
-
📦 Resources distributed:
|
| 866 |
-
Player 3: [92mWheat, Wheat[0m
|
| 867 |
-
[92m✓[0m s rolled the dice
|
| 868 |
|
| 869 |
[1m[96m==================================================[0m
|
| 870 |
[1m[96m GAME STATE [0m
|
| 871 |
[1m[96m==================================================[0m
|
| 872 |
|
| 873 |
Turn: [1m11[0m
|
| 874 |
-
Current Player: [1m[92m►
|
| 875 |
|
| 876 |
[1m[93mPLAYERS[0m
|
| 877 |
[93m-------[0m
|
|
@@ -886,7 +705,7 @@ Current Player: [1m[92m► s[0m
|
|
| 886 |
Resources: None
|
| 887 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m4[0m
|
| 888 |
|
| 889 |
-
[1m[92m►
|
| 890 |
Victory Points: [97m2[0m
|
| 891 |
Resources: None
|
| 892 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
|
@@ -895,14 +714,14 @@ Current Player: [1m[92m► s[0m
|
|
| 895 |
[93m-----[0m
|
| 896 |
Board Tiles: 19 tiles configured
|
| 897 |
|
| 898 |
-
[92m✓[0m
|
| 899 |
|
| 900 |
[1m[96m==================================================[0m
|
| 901 |
[1m[96m GAME STATE [0m
|
| 902 |
[1m[96m==================================================[0m
|
| 903 |
|
| 904 |
Turn: [1m11[0m
|
| 905 |
-
Current Player: [1m[92m►
|
| 906 |
|
| 907 |
[1m[93mPLAYERS[0m
|
| 908 |
[93m-------[0m
|
|
@@ -917,7 +736,7 @@ Current Player: [1m[92m► s[0m
|
|
| 917 |
Resources: None
|
| 918 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m4[0m
|
| 919 |
|
| 920 |
-
[1m[92m►
|
| 921 |
Victory Points: [97m2[0m
|
| 922 |
Resources: None
|
| 923 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
|
@@ -926,7 +745,7 @@ Current Player: [1m[92m► s[0m
|
|
| 926 |
[93m-----[0m
|
| 927 |
Board Tiles: 19 tiles configured
|
| 928 |
|
| 929 |
-
[91m✗[0m
|
| 930 |
[91mError: You don't own the settlement at point 40[0m
|
| 931 |
|
| 932 |
[1m[96m==================================================[0m
|
|
@@ -934,7 +753,7 @@ Board Tiles: 19 tiles configured
|
|
| 934 |
[1m[96m==================================================[0m
|
| 935 |
|
| 936 |
Turn: [1m11[0m
|
| 937 |
-
Current Player: [1m[92m►
|
| 938 |
|
| 939 |
[1m[93mPLAYERS[0m
|
| 940 |
[93m-------[0m
|
|
@@ -949,54 +768,23 @@ Current Player: [1m[92m► s[0m
|
|
| 949 |
Resources: None
|
| 950 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m4[0m
|
| 951 |
|
| 952 |
-
[1m[92m►
|
| 953 |
-
Victory Points: [97m2[0m
|
| 954 |
-
Resources: None
|
| 955 |
-
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 956 |
-
|
| 957 |
-
[1m[93mBOARD[0m
|
| 958 |
-
[93m-----[0m
|
| 959 |
-
Board Tiles: 19 tiles configured
|
| 960 |
-
|
| 961 |
-
[92m✓[0m s built a city
|
| 962 |
-
|
| 963 |
-
[1m[96m==================================================[0m
|
| 964 |
-
[1m[96m GAME STATE [0m
|
| 965 |
-
[1m[96m==================================================[0m
|
| 966 |
-
|
| 967 |
-
Turn: [1m11[0m
|
| 968 |
-
Current Player: [1m[92m► s[0m
|
| 969 |
-
|
| 970 |
-
[1m[93mPLAYERS[0m
|
| 971 |
-
[93m-------[0m
|
| 972 |
-
|
| 973 |
-
[97ma[0m
|
| 974 |
Victory Points: [97m2[0m
|
| 975 |
Resources: None
|
| 976 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 977 |
|
| 978 |
-
[97mb[0m
|
| 979 |
-
Victory Points: [97m2[0m
|
| 980 |
-
Resources: None
|
| 981 |
-
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m4[0m
|
| 982 |
-
|
| 983 |
-
[1m[92m► s[0m
|
| 984 |
-
Victory Points: [97m3[0m
|
| 985 |
-
Resources: None
|
| 986 |
-
Buildings: Settlements: [92m1[0m, Cities: [92m1[0m, Roads: [92m2[0m
|
| 987 |
-
|
| 988 |
[1m[93mBOARD[0m
|
| 989 |
[93m-----[0m
|
| 990 |
Board Tiles: 19 tiles configured
|
| 991 |
|
| 992 |
-
[92m✓[0m
|
| 993 |
|
| 994 |
[1m[96m==================================================[0m
|
| 995 |
[1m[96m GAME STATE [0m
|
| 996 |
[1m[96m==================================================[0m
|
| 997 |
|
| 998 |
Turn: [1m11[0m
|
| 999 |
-
Current Player: [1m[92m►
|
| 1000 |
|
| 1001 |
[1m[93mPLAYERS[0m
|
| 1002 |
[93m-------[0m
|
|
@@ -1011,7 +799,7 @@ Current Player: [1m[92m► s[0m
|
|
| 1011 |
Resources: None
|
| 1012 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m4[0m
|
| 1013 |
|
| 1014 |
-
[1m[92m►
|
| 1015 |
Victory Points: [97m3[0m
|
| 1016 |
Resources: None
|
| 1017 |
Buildings: Settlements: [92m1[0m, Cities: [92m1[0m, Roads: [92m2[0m
|
|
|
|
| 22 |
Resources: None
|
| 23 |
Buildings: Settlements: [91m0[0m, Cities: [91m0[0m, Roads: [91m0[0m
|
| 24 |
|
| 25 |
+
[97mc[0m
|
| 26 |
Victory Points: [97m0[0m
|
| 27 |
Resources: None
|
| 28 |
Buildings: Settlements: [91m0[0m, Cities: [91m0[0m, Roads: [91m0[0m
|
|
|
|
| 53 |
Resources: None
|
| 54 |
Buildings: Settlements: [91m0[0m, Cities: [91m0[0m, Roads: [91m0[0m
|
| 55 |
|
| 56 |
+
[97mc[0m
|
| 57 |
Victory Points: [97m0[0m
|
| 58 |
Resources: None
|
| 59 |
Buildings: Settlements: [91m0[0m, Cities: [91m0[0m, Roads: [91m0[0m
|
|
|
|
| 86 |
Resources: None
|
| 87 |
Buildings: Settlements: [92m1[0m, Cities: [91m0[0m, Roads: [91m0[0m
|
| 88 |
|
| 89 |
+
[97mc[0m
|
| 90 |
Victory Points: [97m0[0m
|
| 91 |
Resources: None
|
| 92 |
Buildings: Settlements: [91m0[0m, Cities: [91m0[0m, Roads: [91m0[0m
|
|
|
|
| 117 |
Resources: None
|
| 118 |
Buildings: Settlements: [92m1[0m, Cities: [91m0[0m, Roads: [92m1[0m
|
| 119 |
|
| 120 |
+
[97mc[0m
|
| 121 |
Victory Points: [97m0[0m
|
| 122 |
Resources: None
|
| 123 |
Buildings: Settlements: [91m0[0m, Cities: [91m0[0m, Roads: [91m0[0m
|
|
|
|
| 127 |
Board Tiles: 19 tiles configured
|
| 128 |
|
| 129 |
|
| 130 |
+
[1m[94m>>> Turn 2: c's turn[0m
|
| 131 |
+
[92m✓[0m c built a settlement
|
| 132 |
|
| 133 |
[1m[96m==================================================[0m
|
| 134 |
[1m[96m GAME STATE [0m
|
| 135 |
[1m[96m==================================================[0m
|
| 136 |
|
| 137 |
Turn: [1m2[0m
|
| 138 |
+
Current Player: [1m[92m► c[0m
|
| 139 |
|
| 140 |
[1m[93mPLAYERS[0m
|
| 141 |
[93m-------[0m
|
|
|
|
| 150 |
Resources: None
|
| 151 |
Buildings: Settlements: [92m1[0m, Cities: [91m0[0m, Roads: [92m1[0m
|
| 152 |
|
| 153 |
+
[1m[92m► c[0m
|
| 154 |
Victory Points: [97m1[0m
|
| 155 |
Resources: None
|
| 156 |
Buildings: Settlements: [92m1[0m, Cities: [91m0[0m, Roads: [91m0[0m
|
|
|
|
| 159 |
[93m-----[0m
|
| 160 |
Board Tiles: 19 tiles configured
|
| 161 |
|
| 162 |
+
[92m✓[0m c built a road
|
| 163 |
|
| 164 |
[1m[96m==================================================[0m
|
| 165 |
[1m[96m GAME STATE [0m
|
| 166 |
[1m[96m==================================================[0m
|
| 167 |
|
| 168 |
Turn: [1m2[0m
|
| 169 |
+
Current Player: [1m[92m► c[0m
|
| 170 |
|
| 171 |
[1m[93mPLAYERS[0m
|
| 172 |
[93m-------[0m
|
|
|
|
| 181 |
Resources: None
|
| 182 |
Buildings: Settlements: [92m1[0m, Cities: [91m0[0m, Roads: [92m1[0m
|
| 183 |
|
| 184 |
+
[1m[92m► c[0m
|
| 185 |
Victory Points: [97m1[0m
|
| 186 |
Resources: None
|
| 187 |
Buildings: Settlements: [92m1[0m, Cities: [91m0[0m, Roads: [92m1[0m
|
|
|
|
| 191 |
Board Tiles: 19 tiles configured
|
| 192 |
|
| 193 |
|
| 194 |
+
[1m[94m>>> Turn 3: c's turn[0m
|
| 195 |
|
| 196 |
📦 Resources distributed:
|
| 197 |
+
c: [92mOre, Wheat, Wheat[0m
|
| 198 |
+
[92m✓[0m c built a settlement
|
| 199 |
|
| 200 |
[1m[96m==================================================[0m
|
| 201 |
[1m[96m GAME STATE [0m
|
| 202 |
[1m[96m==================================================[0m
|
| 203 |
|
| 204 |
Turn: [1m3[0m
|
| 205 |
+
Current Player: [1m[92m► c[0m
|
| 206 |
|
| 207 |
[1m[93mPLAYERS[0m
|
| 208 |
[93m-------[0m
|
|
|
|
| 217 |
Resources: None
|
| 218 |
Buildings: Settlements: [92m1[0m, Cities: [91m0[0m, Roads: [92m1[0m
|
| 219 |
|
| 220 |
+
[1m[92m► c[0m
|
| 221 |
Victory Points: [97m2[0m
|
| 222 |
Resources: None
|
| 223 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m1[0m
|
|
|
|
| 226 |
[93m-----[0m
|
| 227 |
Board Tiles: 19 tiles configured
|
| 228 |
|
| 229 |
+
[92m✓[0m c built a road
|
| 230 |
|
| 231 |
[1m[96m==================================================[0m
|
| 232 |
[1m[96m GAME STATE [0m
|
| 233 |
[1m[96m==================================================[0m
|
| 234 |
|
| 235 |
Turn: [1m3[0m
|
| 236 |
+
Current Player: [1m[92m► c[0m
|
| 237 |
|
| 238 |
[1m[93mPLAYERS[0m
|
| 239 |
[93m-------[0m
|
|
|
|
| 248 |
Resources: None
|
| 249 |
Buildings: Settlements: [92m1[0m, Cities: [91m0[0m, Roads: [92m1[0m
|
| 250 |
|
| 251 |
+
[1m[92m► c[0m
|
| 252 |
Victory Points: [97m2[0m
|
| 253 |
Resources: None
|
| 254 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
|
|
|
| 284 |
Resources: None
|
| 285 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m1[0m
|
| 286 |
|
| 287 |
+
[97mc[0m
|
| 288 |
Victory Points: [97m2[0m
|
| 289 |
Resources: None
|
| 290 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
|
|
|
| 315 |
Resources: None
|
| 316 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 317 |
|
| 318 |
+
[97mc[0m
|
| 319 |
Victory Points: [97m2[0m
|
| 320 |
Resources: None
|
| 321 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
|
|
|
| 351 |
Resources: None
|
| 352 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 353 |
|
| 354 |
+
[97mc[0m
|
| 355 |
Victory Points: [97m2[0m
|
| 356 |
Resources: None
|
| 357 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
|
|
|
| 382 |
Resources: None
|
| 383 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 384 |
|
| 385 |
+
[97mc[0m
|
| 386 |
Victory Points: [97m2[0m
|
| 387 |
Resources: None
|
| 388 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
|
|
|
| 393 |
|
| 394 |
|
| 395 |
[1m[94m>>> Turn 6: a's turn[0m
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 396 |
[92m✓[0m a rolled the dice
|
| 397 |
|
| 398 |
[1m[96m==================================================[0m
|
|
|
|
| 415 |
Resources: None
|
| 416 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 417 |
|
| 418 |
+
[97mc[0m
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 419 |
Victory Points: [97m2[0m
|
| 420 |
Resources: None
|
| 421 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
|
|
|
| 426 |
|
| 427 |
|
| 428 |
[1m[94m>>> Turn 7: b's turn[0m
|
|
|
|
|
|
|
| 429 |
[92m✓[0m b rolled the dice
|
| 430 |
|
| 431 |
[1m[96m==================================================[0m
|
|
|
|
| 448 |
Resources: None
|
| 449 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 450 |
|
| 451 |
+
[97mc[0m
|
| 452 |
Victory Points: [97m2[0m
|
| 453 |
Resources: None
|
| 454 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
|
|
|
| 479 |
Resources: None
|
| 480 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 481 |
|
| 482 |
+
[97mc[0m
|
| 483 |
Victory Points: [97m2[0m
|
| 484 |
Resources: None
|
| 485 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
|
|
|
| 488 |
[93m-----[0m
|
| 489 |
Board Tiles: 19 tiles configured
|
| 490 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 491 |
|
| 492 |
+
[1m[94m>>> Turn 8: c's turn[0m
|
| 493 |
+
[92m✓[0m c rolled the dice
|
| 494 |
|
| 495 |
[1m[96m==================================================[0m
|
| 496 |
[1m[96m GAME STATE [0m
|
| 497 |
[1m[96m==================================================[0m
|
| 498 |
|
| 499 |
Turn: [1m8[0m
|
| 500 |
+
Current Player: [1m[92m► c[0m
|
| 501 |
|
| 502 |
[1m[93mPLAYERS[0m
|
| 503 |
[93m-------[0m
|
|
|
|
| 512 |
Resources: None
|
| 513 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 514 |
|
| 515 |
+
[1m[92m► c[0m
|
| 516 |
Victory Points: [97m2[0m
|
| 517 |
Resources: None
|
| 518 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
|
|
|
| 523 |
|
| 524 |
|
| 525 |
[1m[94m>>> Turn 9: a's turn[0m
|
|
|
|
|
|
|
| 526 |
[92m✓[0m a rolled the dice
|
| 527 |
|
| 528 |
[1m[96m==================================================[0m
|
|
|
|
| 545 |
Resources: None
|
| 546 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 547 |
|
| 548 |
+
[97mc[0m
|
| 549 |
Victory Points: [97m2[0m
|
| 550 |
Resources: None
|
| 551 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
|
|
|
| 576 |
Resources: None
|
| 577 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 578 |
|
| 579 |
+
[97mc[0m
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 580 |
Victory Points: [97m2[0m
|
| 581 |
Resources: None
|
| 582 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
|
|
|
| 587 |
|
| 588 |
|
| 589 |
[1m[94m>>> Turn 10: b's turn[0m
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 590 |
[92m✓[0m b rolled the dice
|
| 591 |
|
| 592 |
[1m[96m==================================================[0m
|
|
|
|
| 609 |
Resources: None
|
| 610 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 611 |
|
| 612 |
+
[97mc[0m
|
| 613 |
Victory Points: [97m2[0m
|
| 614 |
Resources: None
|
| 615 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
|
|
|
| 641 |
Dev Cards: 1
|
| 642 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 643 |
|
| 644 |
+
[97mc[0m
|
| 645 |
Victory Points: [97m2[0m
|
| 646 |
Resources: None
|
| 647 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
|
|
|
| 672 |
Resources: None
|
| 673 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m4[0m
|
| 674 |
|
| 675 |
+
[97mc[0m
|
| 676 |
Victory Points: [97m2[0m
|
| 677 |
Resources: None
|
| 678 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
|
|
|
| 681 |
[93m-----[0m
|
| 682 |
Board Tiles: 19 tiles configured
|
| 683 |
|
|
|
|
| 684 |
|
| 685 |
+
[1m[94m>>> Turn 11: c's turn[0m
|
| 686 |
+
[92m✓[0m c rolled the dice
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 687 |
|
| 688 |
[1m[96m==================================================[0m
|
| 689 |
[1m[96m GAME STATE [0m
|
| 690 |
[1m[96m==================================================[0m
|
| 691 |
|
| 692 |
Turn: [1m11[0m
|
| 693 |
+
Current Player: [1m[92m► c[0m
|
| 694 |
|
| 695 |
[1m[93mPLAYERS[0m
|
| 696 |
[93m-------[0m
|
|
|
|
| 705 |
Resources: None
|
| 706 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m4[0m
|
| 707 |
|
| 708 |
+
[1m[92m► c[0m
|
| 709 |
Victory Points: [97m2[0m
|
| 710 |
Resources: None
|
| 711 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
|
|
|
| 714 |
[93m-----[0m
|
| 715 |
Board Tiles: 19 tiles configured
|
| 716 |
|
| 717 |
+
[92m✓[0m c proposed a trade
|
| 718 |
|
| 719 |
[1m[96m==================================================[0m
|
| 720 |
[1m[96m GAME STATE [0m
|
| 721 |
[1m[96m==================================================[0m
|
| 722 |
|
| 723 |
Turn: [1m11[0m
|
| 724 |
+
Current Player: [1m[92m► c[0m
|
| 725 |
|
| 726 |
[1m[93mPLAYERS[0m
|
| 727 |
[93m-------[0m
|
|
|
|
| 736 |
Resources: None
|
| 737 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m4[0m
|
| 738 |
|
| 739 |
+
[1m[92m► c[0m
|
| 740 |
Victory Points: [97m2[0m
|
| 741 |
Resources: None
|
| 742 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
|
|
|
| 745 |
[93m-----[0m
|
| 746 |
Board Tiles: 19 tiles configured
|
| 747 |
|
| 748 |
+
[91m✗[0m c built a city
|
| 749 |
[91mError: You don't own the settlement at point 40[0m
|
| 750 |
|
| 751 |
[1m[96m==================================================[0m
|
|
|
|
| 753 |
[1m[96m==================================================[0m
|
| 754 |
|
| 755 |
Turn: [1m11[0m
|
| 756 |
+
Current Player: [1m[92m► c[0m
|
| 757 |
|
| 758 |
[1m[93mPLAYERS[0m
|
| 759 |
[93m-------[0m
|
|
|
|
| 768 |
Resources: None
|
| 769 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m4[0m
|
| 770 |
|
| 771 |
+
[1m[92m► c[0m
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 772 |
Victory Points: [97m2[0m
|
| 773 |
Resources: None
|
| 774 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m2[0m
|
| 775 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 776 |
[1m[93mBOARD[0m
|
| 777 |
[93m-----[0m
|
| 778 |
Board Tiles: 19 tiles configured
|
| 779 |
|
| 780 |
+
[92m✓[0m c built a city
|
| 781 |
|
| 782 |
[1m[96m==================================================[0m
|
| 783 |
[1m[96m GAME STATE [0m
|
| 784 |
[1m[96m==================================================[0m
|
| 785 |
|
| 786 |
Turn: [1m11[0m
|
| 787 |
+
Current Player: [1m[92m► c[0m
|
| 788 |
|
| 789 |
[1m[93mPLAYERS[0m
|
| 790 |
[93m-------[0m
|
|
|
|
| 799 |
Resources: None
|
| 800 |
Buildings: Settlements: [92m2[0m, Cities: [91m0[0m, Roads: [92m4[0m
|
| 801 |
|
| 802 |
+
[1m[92m► c[0m
|
| 803 |
Victory Points: [97m3[0m
|
| 804 |
Resources: None
|
| 805 |
Buildings: Settlements: [92m1[0m, Cities: [92m1[0m, Roads: [92m2[0m
|
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 |
-
#
|
| 1445 |
-
|
| 1446 |
-
|
| 1447 |
-
|
| 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 |
-
#
|
| 550 |
-
|
| 551 |
-
|
| 552 |
-
|
| 553 |
-
|
| 554 |
-
|
| 555 |
-
|
| 556 |
-
|
| 557 |
-
|
| 558 |
-
|
| 559 |
-
|
| 560 |
-
|
| 561 |
-
|
| 562 |
-
|
| 563 |
-
|
| 564 |
-
|
| 565 |
-
|
| 566 |
-
|
| 567 |
-
|
| 568 |
-
|
| 569 |
-
|
| 570 |
-
|
| 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':
|
|
|
|
|
|
|
| 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 |
-
|
| 636 |
-
|
| 637 |
-
|
| 638 |
-
|
| 639 |
-
|
| 640 |
-
'
|
| 641 |
-
|
| 642 |
-
'message': f"Turn {turn_number}: {player_name}'s turn begins"
|
| 643 |
-
}
|
| 644 |
|
| 645 |
-
#
|
| 646 |
-
self.
|
| 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 |
-
|
| 660 |
-
|
| 661 |
-
|
| 662 |
-
|
| 663 |
-
|
| 664 |
-
|
| 665 |
-
|
| 666 |
-
|
| 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 |
-
#
|
| 675 |
-
self.
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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')}")
|