EZTIME2025 commited on
Commit
825efb1
·
1 Parent(s): a394713

knight finally finished!

Browse files
game_viz.log CHANGED
@@ -1390,6 +1390,8 @@ Current Player: ► b
1390
  -----
1391
  Board Tiles: 19 tiles configured
1392
 
 
 
1393
  ✓ b used Knight
1394
 
1395
  ==================================================
 
1390
  -----
1391
  Board Tiles: 19 tiles configured
1392
 
1393
+ ✓ ✨ b used Knight
1394
+ ✓ 🦹 b stole Sheep from a
1395
  ✓ b used Knight
1396
 
1397
  ==================================================
pycatan/game_manager.py CHANGED
@@ -15,6 +15,7 @@ from pycatan.user import User, UserList, validate_user_list, UserInputError
15
  from pycatan.game import Game
16
  from pycatan.statuses import Statuses
17
  from pycatan.card import DevCard
 
18
 
19
 
20
  class GameManager:
@@ -878,6 +879,9 @@ class GameManager:
878
  # Remove the card from player's hand
879
  self.game.players[player_id].remove_dev_card(DevCard.Knight)
880
 
 
 
 
881
  # Update visualizations
882
  player_name = self.users[player_id].name if hasattr(self.users[player_id], 'name') else f"Player {player_id}"
883
 
@@ -885,6 +889,23 @@ class GameManager:
885
  robber_msg = f"⚔️ {player_name} used a Knight card! Robber moved to {tile_display}."
886
  print(f"\n {robber_msg}")
887
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
888
  # Notify about robber move
889
  self._notify_all_users(
890
  "knight_used",
@@ -894,10 +915,43 @@ class GameManager:
894
  # Notify about card steal if victim exists
895
  if victim_id is not None:
896
  victim_name = self.users[victim_id].name if hasattr(self.users[victim_id], 'name') else f"Player {victim_id}"
897
- self._notify_all_users(
898
- "knight_steal",
899
- f"🎯 {player_name} stole a card from {victim_name}!"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
900
  )
 
 
 
 
 
901
 
902
  # Notify if player got Largest Army
903
  if self.game.largest_army == player_id:
@@ -1725,9 +1779,8 @@ class GameManager:
1725
  )
1726
 
1727
  # Send to visualizations
1728
- for viz in self.visualization_manager.visualizations:
1729
- if hasattr(viz, 'log_event'):
1730
- viz.log_event(log_entry)
1731
 
1732
  if distribution:
1733
  message = f"Rolled {total} ({die1}+{die2}). Resources distributed."
 
15
  from pycatan.game import Game
16
  from pycatan.statuses import Statuses
17
  from pycatan.card import DevCard
18
+ from pycatan.log_events import EventType, create_log_entry
19
 
20
 
21
  class GameManager:
 
879
  # Remove the card from player's hand
880
  self.game.players[player_id].remove_dev_card(DevCard.Knight)
881
 
882
+ # Get stolen card info from args (set by game.use_dev_card)
883
+ stolen_card = args.get('stolen_card')
884
+
885
  # Update visualizations
886
  player_name = self.users[player_id].name if hasattr(self.users[player_id], 'name') else f"Player {player_id}"
887
 
 
889
  robber_msg = f"⚔️ {player_name} used a Knight card! Robber moved to {tile_display}."
890
  print(f"\n {robber_msg}")
891
 
892
+ # Send Knight card usage log to visualizations
893
+ knight_log = create_log_entry(
894
+ event_type=EventType.USE_DEV_CARD,
895
+ turn=self._current_game_state.turn_number,
896
+ player_id=player_id,
897
+ player_name=player_name,
898
+ data={
899
+ 'card': 'Knight',
900
+ 'robber_tile': tile_display,
901
+ 'message': robber_msg
902
+ },
903
+ status="SUCCESS"
904
+ )
905
+
906
+ if self.visualization_manager:
907
+ self.visualization_manager.log_event(knight_log)
908
+
909
  # Notify about robber move
910
  self._notify_all_users(
911
  "knight_used",
 
915
  # Notify about card steal if victim exists
916
  if victim_id is not None:
917
  victim_name = self.users[victim_id].name if hasattr(self.users[victim_id], 'name') else f"Player {victim_id}"
918
+
919
+ # stolen_card was already retrieved from args above
920
+ if stolen_card:
921
+ # Map card type to Hebrew/English name
922
+ card_names = {
923
+ 'wood': 'עץ (Wood)',
924
+ 'brick': 'לבנה (Brick)',
925
+ 'sheep': 'כבשה (Sheep)',
926
+ 'wheat': 'חיטה (Wheat)',
927
+ 'ore': 'עפרה (Ore)'
928
+ }
929
+ card_display = card_names.get(stolen_card.value, stolen_card.value)
930
+ steal_msg = f"🎯 {player_name} stole {card_display} from {victim_name}!"
931
+ else:
932
+ steal_msg = f"🎯 {player_name} stole a card from {victim_name}!"
933
+
934
+ print(f"\n {steal_msg}")
935
+
936
+ # Send steal log to visualizations
937
+ steal_log = create_log_entry(
938
+ event_type=EventType.ROBBER_STEAL,
939
+ turn=self._current_game_state.turn_number,
940
+ player_id=player_id,
941
+ player_name=player_name,
942
+ data={
943
+ 'victim_id': victim_id,
944
+ 'victim': victim_name,
945
+ 'card': stolen_card.name if stolen_card else 'unknown', # Use .name to get 'Wood', 'Brick', etc.
946
+ 'message': steal_msg
947
+ },
948
+ status="SUCCESS"
949
  )
950
+
951
+ if self.visualization_manager:
952
+ self.visualization_manager.log_event(steal_log)
953
+
954
+ self._notify_all_users("knight_steal", steal_msg)
955
 
956
  # Notify if player got Largest Army
957
  if self.game.largest_army == player_id:
 
1779
  )
1780
 
1781
  # Send to visualizations
1782
+ if self.visualization_manager:
1783
+ self.visualization_manager.log_event(log_entry)
 
1784
 
1785
  if distribution:
1786
  message = f"Rolled {total} ({die1}+{die2}). Resources distributed."
pycatan/log_events.py CHANGED
@@ -199,6 +199,8 @@ class LogEntry:
199
  elif self.event_type == EventType.ROBBER_STEAL:
200
  victim = self.data.get('victim', '?')
201
  card = self.data.get('card', 'a card')
 
 
202
  return f"🦹 {player_str} stole {card} from {victim}"
203
 
204
  elif self.event_type == EventType.TURN_START:
 
199
  elif self.event_type == EventType.ROBBER_STEAL:
200
  victim = self.data.get('victim', '?')
201
  card = self.data.get('card', 'a card')
202
+ # Card names come from enum.name which is already capitalized (Wood, Brick, etc.)
203
+ # Just display as-is
204
  return f"🦹 {player_str} stole {card} from {victim}"
205
 
206
  elif self.event_type == EventType.TURN_START:
pycatan/visualization.py CHANGED
@@ -8,6 +8,7 @@ Different visualization types (console, web, log) inherit from this class.
8
  from abc import ABC, abstractmethod
9
  from typing import Dict, Any, List, Optional
10
  from .actions import Action, ActionResult
 
11
 
12
 
13
  class Visualization(ABC):
@@ -200,6 +201,12 @@ class VisualizationManager:
200
  if viz.is_enabled():
201
  viz.display_message(message)
202
 
 
 
 
 
 
 
203
  def get_visualization_count(self) -> int:
204
  """Get the number of registered visualizations."""
205
  return len(self.visualizations)
 
8
  from abc import ABC, abstractmethod
9
  from typing import Dict, Any, List, Optional
10
  from .actions import Action, ActionResult
11
+ from .log_events import LogEntry
12
 
13
 
14
  class Visualization(ABC):
 
201
  if viz.is_enabled():
202
  viz.display_message(message)
203
 
204
+ def log_event(self, log_entry: LogEntry) -> None:
205
+ """Log a structured event to all enabled visualizations."""
206
+ for viz in self.visualizations:
207
+ if viz.is_enabled() and hasattr(viz, 'log_event'):
208
+ viz.log_event(log_entry)
209
+
210
  def get_visualization_count(self) -> int:
211
  """Get the number of registered visualizations."""
212
  return len(self.visualizations)
test_knight_steal.py ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Test Knight card with stolen card display
3
+ """
4
+ from pycatan import Game
5
+ from pycatan.card import DevCard, ResCard
6
+ from pycatan.board_definition import board_definition
7
+ from pycatan.actions import GamePhase
8
+ from pycatan.statuses import Statuses
9
+
10
+ def test_knight_with_steal():
11
+ """Test that Knight card shows which card was stolen."""
12
+ print("\n" + "="*60)
13
+ print("Testing Knight Card with Card Steal")
14
+ print("="*60)
15
+
16
+ # Create game with 3 players
17
+ game = Game(num_of_players=3)
18
+ game.game_phase = GamePhase.NORMAL_PLAY
19
+
20
+ # Give player 0 a Knight card
21
+ game.players[0].add_dev_card(DevCard.Knight)
22
+
23
+ # Give player 1 some resource cards
24
+ game.players[1].add_cards([ResCard.Wheat, ResCard.Wood, ResCard.Brick])
25
+
26
+ # Add a settlement for player 1 at a point adjacent to tile 5
27
+ # This allows player 0 to steal from player 1
28
+ # Tile 5 is at game coords (1,1)
29
+
30
+ # Get a point adjacent to tile (1,1)
31
+ tile = game.board.tiles[1][1]
32
+ if tile.points:
33
+ settlement_point = tile.points[0]
34
+ # Use game.add_settlement instead of player.add_settlement
35
+ game.add_settlement(player=1, point=settlement_point, is_starting=True)
36
+ print(f"\n✓ Game setup complete")
37
+ print(f" - Player 0: {len(game.players[0].dev_cards)} Knight card")
38
+ print(f" - Player 1: {len(game.players[1].cards)} resource cards, 1 settlement")
39
+ print(f" - Player 1's cards: {[card.value for card in game.players[1].cards]}")
40
+
41
+ # Test using Knight card on tile 5 and stealing from player 1
42
+ print(f"\n🧪 Testing Knight card with steal...")
43
+
44
+ args = {
45
+ 'robber_pos': [1, 1],
46
+ 'victim': 1
47
+ }
48
+
49
+ print(f"📢 Using Knight card to move robber to tile 5 and steal from Player 1")
50
+
51
+ # Record player 1's card count before
52
+ cards_before = len(game.players[1].cards)
53
+
54
+ # Execute
55
+ status = game.use_dev_card(0, DevCard.Knight, args)
56
+
57
+ if status == Statuses.ALL_GOOD:
58
+ cards_after = len(game.players[1].cards)
59
+ stolen = cards_before - cards_after
60
+
61
+ print(f"\n✅ Knight card used successfully!")
62
+ print(f" - Robber moved to tile 5 (coords [1, 1])")
63
+ print(f" - Player 1 had {cards_before} cards, now has {cards_after}")
64
+ print(f" - {stolen} card was stolen")
65
+ print(f" - Player 0 knights: {game.players[0].knight_cards}")
66
+
67
+ # Check if stolen card info is in args
68
+ if 'stolen_card' in args:
69
+ card = args['stolen_card']
70
+ print(f"\n✨ Stolen card: {card.value}")
71
+ print(f" Message should show: '🎯 Player 0 stole {card.value} from Player 1!'")
72
+ else:
73
+ print(f"\n⚠️ No stolen card info found in args")
74
+ else:
75
+ print(f"❌ Failed with status: {status}")
76
+ else:
77
+ print("❌ Could not find points on tile")
78
+
79
+ print("\n" + "="*60)
80
+ print("Test Complete!")
81
+ print("="*60)
82
+
83
+ if __name__ == "__main__":
84
+ test_knight_with_steal()
test_knight_viz.py ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Test Knight card with visualization logs
3
+ """
4
+ from pycatan import Game
5
+ from pycatan.card import DevCard, ResCard
6
+ from pycatan.board_definition import board_definition
7
+ from pycatan.actions import GamePhase
8
+ from pycatan.statuses import Statuses
9
+ from pycatan.game_manager import GameManager
10
+ from pycatan.human_user import HumanUser
11
+ from pycatan.console_visualization import ConsoleVisualization
12
+
13
+ def test_knight_with_visualizations():
14
+ """Test that Knight card logs appear in visualizations."""
15
+ print("\n" + "="*60)
16
+ print("Testing Knight Card with Visualization Logs")
17
+ print("="*60)
18
+
19
+ # Create users
20
+ users = [HumanUser("Alice", 0), HumanUser("Bob", 1), HumanUser("Charlie", 2)]
21
+
22
+ # Create console visualization
23
+ console_viz = ConsoleVisualization()
24
+
25
+ # Create game manager
26
+ gm = GameManager(users=users)
27
+
28
+ # Setup visualization manager
29
+ from pycatan.visualization import VisualizationManager
30
+ gm.visualization_manager = VisualizationManager()
31
+ gm.visualization_manager.add_visualization(console_viz)
32
+
33
+ # Setup game manually
34
+ gm.game.game_phase = GamePhase.NORMAL_PLAY
35
+
36
+ # Give player 0 (Alice) a Knight card
37
+ gm.game.players[0].add_dev_card(DevCard.Knight)
38
+
39
+ # Give player 1 (Bob) some resource cards
40
+ gm.game.players[1].add_cards([ResCard.Wheat, ResCard.Wood, ResCard.Brick])
41
+
42
+ # Add a settlement for player 1 at a point adjacent to tile 5
43
+ tile = gm.game.board.tiles[1][1] # Tile 5 is at (1,1)
44
+ if tile.points:
45
+ settlement_point = tile.points[0]
46
+ gm.game.add_settlement(player=1, point=settlement_point, is_starting=True)
47
+
48
+ print(f"\n✓ Game setup complete")
49
+ print(f" - Alice has {len(gm.game.players[0].dev_cards)} Knight card")
50
+ print(f" - Bob has {len(gm.game.players[1].cards)} resource cards")
51
+
52
+ # Create Knight action directly
53
+ from pycatan.actions import Action, ActionType
54
+
55
+ knight_action = Action(
56
+ action_type=ActionType.USE_DEV_CARD,
57
+ player_id=0,
58
+ parameters={
59
+ 'card_type': DevCard.Knight,
60
+ 'tile_id': 5,
61
+ 'victim_name': 'Bob'
62
+ }
63
+ )
64
+
65
+ print(f"\n🧪 Executing Knight card action...")
66
+ print(f" This should send logs to the visualization\n")
67
+
68
+ # Execute through GameManager
69
+ result = gm._use_knight_card(knight_action)
70
+
71
+ if result.success:
72
+ print(f"\n✅ Knight card executed successfully!")
73
+ print(f" Logs should have been displayed above with:")
74
+ print(f" 1. ⚔️ Knight card used message")
75
+ print(f" 2. 🎯 Card stolen message (with card type)")
76
+ else:
77
+ print(f"❌ Failed: {result.error_message}")
78
+ else:
79
+ print("❌ Could not find points on tile")
80
+
81
+ print("\n" + "="*60)
82
+ print("Test Complete!")
83
+ print("="*60)
84
+
85
+ if __name__ == "__main__":
86
+ test_knight_with_visualizations()