Skip to content

Commit a971d1f

Browse files
committed
fixed parallelization bug
1 parent 937a47c commit a971d1f

4 files changed

+167
-77
lines changed

DominoGame.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ def play_game(self):
9191
games_tied = 0 # Number of tied games
9292
round_scores = [] # List to store scores for each round
9393

94-
winning_score = 100 if self.variant != 'international' else 1
94+
winning_score = 100 if self.variant != 'international' else 150
9595

9696
while max(self.scores) < winning_score:
9797
round_winner, round_score = self.play_round()

DominoPlayer.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,11 @@ def next_move(self, game_state: DominoGameState, player_hand: list[tuple[int, in
7070
print('illegal input', e)
7171

7272
def end_round(self, scores: list[int], team: int) -> None:
73-
self.missing_tiles = defaultdict(set)
74-
print(f"HumanPlayer: Round ended. Scores - Team 1: {scores[0]}, Team 2: {scores[1]}")
75-
print(f"HumanPlayer: Your team (Team {team + 1}) score: {scores[team]}")
76-
print("HumanPlayer: Reset missing tiles for the next round.")
73+
# self.missing_tiles = defaultdict(set)
74+
# print(f"HumanPlayer: Round ended. Scores - Team 1: {scores[0]}, Team 2: {scores[1]}")
75+
# print(f"HumanPlayer: Your team (Team {team + 1}) score: {scores[team]}")
76+
# print("HumanPlayer: Reset missing tiles for the next round.")
77+
pass
7778

7879
def get_unplayed_tiles(self, game_state: DominoGameState, player_hand: list[tuple[int, int]]) -> list[tuple[int,int]]:
7980
max_pip = 9 if game_state.variant == "cuban" else 6

analytic_agent_player_parallel.py

+14-11
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def next_move(self, game_state: DominoGameState, player_hand: list[tuple[int,int
5050
if verbose:
5151
self.print_verbose_info(_player_hand, _unplayed_tiles, _knowledge_tracker, _player_tiles_count, _starting_player)
5252

53-
num_samples = 1000 if len(game_state.history) > 8 else 100 if len(game_state.history) > 4 else 25 if len(game_state.history) > 0 else 1
53+
num_samples = 1000 if len(game_state.history) > 8 else 100 if len(game_state.history) > 4 else 25 if len(game_state.history) > 0 else 25
5454
best_move = self.get_best_move(set(_player_hand), _remaining_tiles, _knowledge_tracker, _player_tiles_count, _board_ends, num_samples, verbose=verbose)
5555

5656
if best_move is None:
@@ -77,7 +77,7 @@ def print_verbose_info(self, player_hand: list[DominoTile], unplayed_tiles: list
7777
print(f" {PlayerPosition_names[player]}: {count}")
7878
print("----------------------------\n")
7979

80-
def sample_search(self, final_south_hand: set[DominoTile], final_remaining_tiles_without_south_tiles: set[DominoTile], player_tiles_count: dict[PlayerPosition, int], inferred_knowledge_for_current_player: CommonKnowledgeTracker, board_ends: tuple[int|None,int|None]) -> tuple[move, float]:
80+
def sample_and_search(self, final_south_hand: set[DominoTile], final_remaining_tiles_without_south_tiles: set[DominoTile], player_tiles_count: dict[PlayerPosition, int], inferred_knowledge_for_current_player: dict[PlayerPosition, set[DominoTile]], board_ends: tuple[int|None,int|None]) -> list[tuple[move, float]]:
8181
sample = generate_sample_from_game_state(
8282
# PlayerPosition.SOUTH,
8383
PlayerPosition_SOUTH,
@@ -107,6 +107,7 @@ def sample_search(self, final_south_hand: set[DominoTile], final_remaining_tiles
107107

108108
# possible_moves = list_possible_moves(sample_state, include_stats=False)
109109
possible_moves = list_possible_moves(sample_state)
110+
move_scores: list[tuple[move, float]] = []
110111

111112
sample_cache: dict[GameState, tuple[int, int]] = {}
112113
for move in possible_moves:
@@ -118,7 +119,9 @@ def sample_search(self, final_south_hand: set[DominoTile], final_remaining_tiles
118119

119120
# _, best_score, _ = get_best_move_alpha_beta(new_state, depth, sample_cache, best_path_flag=False)
120121
_, best_score, _ = get_best_move_alpha_beta(new_state, depth, sample_cache, best_path_flag=False)
121-
return move[0], best_score
122+
move_scores.append((move[0], best_score))
123+
# return move[0], best_score
124+
return move_scores
122125

123126
def get_best_move(self, final_south_hand: set[DominoTile], remaining_tiles: set[DominoTile],
124127
knowledge_tracker: CommonKnowledgeTracker, player_tiles_count: dict[PlayerPosition, int],
@@ -187,28 +190,28 @@ def get_best_move(self, final_south_hand: set[DominoTile], remaining_tiles: set[
187190

188191
# move_scores[move[0]].append(best_score)
189192

190-
# move, best_score = self.sample_search(final_south_hand, final_remaining_tiles_without_south_tiles, player_tiles_count, inferred_knowledge_for_current_player, board_ends)
191-
# move_scores[move].append(best_score)
192-
193-
# def sample_search(self, final_south_hand: set[DominoTile], final_remaining_tiles_without_south_tiles: set[DominoTile], player_tiles_count: dict[PlayerPosition, int], inferred_knowledge_for_current_player: CommonKnowledgeTracker, board_ends: tuple[int|None,int|None]) -> tuple[move, float]:
193+
# move, best_score = self.sample_and_search(final_south_hand, final_remaining_tiles_without_south_tiles, player_tiles_count, inferred_knowledge_for_current_player, board_ends)
194+
# sample_scores = self.sample_and_search(final_south_hand, final_remaining_tiles_without_south_tiles, player_tiles_count, inferred_knowledge_for_current_player, board_ends)
195+
# for move, score in sample_scores:
196+
# move_scores[move].append(score)
194197

195198
# Use ProcessPoolExecutor to parallelize the execution
196199
with ProcessPoolExecutor() as executor:
197200
futures = [
198201
executor.submit(
199-
self.sample_search,
202+
self.sample_and_search,
200203
final_south_hand,
201204
final_remaining_tiles_without_south_tiles,
202205
player_tiles_count,
203206
inferred_knowledge_for_current_player,
204207
board_ends
205208
)
206209
for _ in range(num_samples)
207-
# for _ in tqdm(range(num_samples), desc="Analyzing moves", leave=False)
208210
]
209211
for future in tqdm(as_completed(futures), total=num_samples, desc="Analyzing moves", leave=False):
210-
move, best_score = future.result()
211-
move_scores[move].append(best_score)
212+
sample_scores = future.result()
213+
for move, score in sample_scores:
214+
move_scores[move].append(score)
212215

213216
if not move_scores:
214217
if verbose:

test.py

+147-61
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,12 @@
3030
# local_not_with {'E': {1|3}, 'N': set(), 'W': {1|3}}
3131

3232

33-
from domino_probability_calc import calculate_tile_probabilities, PlayerTiles,generate_scenarios, generate_sample
34-
from domino_game_analyzer import DominoTile
33+
from domino_probability_calc import calculate_tile_probabilities,generate_scenarios, generate_sample
34+
from domino_data_types import DominoTile, PlayerTiles, PlayerPosition_SOUTH, GameState, PlayerPosition
35+
from analytic_agent_player_parallel import AnalyticAgentPlayer
36+
from domino_game_tracker import domino_game_state_our_perspective, generate_sample_from_game_state
37+
from domino_utils import history_to_domino_tiles_history
38+
from get_best_move2 import list_possible_moves
3539
import copy
3640

3741
def test_calculate_probabilities():
@@ -62,65 +66,147 @@ def test_calculate_probabilities():
6266
print(f" P({player} has {tile}) = {prob:.6f}")
6367
print()
6468

65-
def test_calculate_probabilities2():
66-
# tile 0|6
67-
# probabilities[tile] {'N': 0.3333333333333333, 'E': 0.0, 'W': 0.0}
68-
# not_with {'E': set(), 'N': {5|6, 1|4}, 'W': {1|4}}
69-
# not_with_local {'E': set(), 'N': {5|6, 1|4}, 'W': {1|4}}
70-
# known_with_local {'W': {0|6}}
71-
# prob.sum 0.3333333333333333
72-
# scenarios []
73-
# player_tiles PlayerTiles(N=1, E=1, W=1)
74-
# Remaining tiles: [0|0, 0|1, 0|2, 0|6, 1|3, 1|4, 1|5, 1|6, 2|2, 2|3, 2|6, 3|3, 5|6]
75-
76-
remaining_tiles = set([
77-
DominoTile(0, 0), DominoTile(0, 1), DominoTile(0, 2), DominoTile(0, 6),
78-
DominoTile(1, 3), DominoTile(1, 4), DominoTile(1, 5), DominoTile(1, 6),
79-
DominoTile(2, 2), DominoTile(2, 3), DominoTile(2, 6), DominoTile(3, 3),
80-
DominoTile(5, 6)
81-
# DominoTile(2, 4), DominoTile(3, 6) # Tiles in human player's hand
82-
])
83-
84-
# Define not_with based on _knowledge_tracker
85-
not_with = {
86-
'E': set(),
87-
'N': {DominoTile(5, 6), DominoTile(1, 4)},
88-
'W': {DominoTile(1, 4)}
89-
}
69+
# def test_calculate_probabilities2():
70+
# # tile 0|6
71+
# # probabilities[tile] {'N': 0.3333333333333333, 'E': 0.0, 'W': 0.0}
72+
# # not_with {'E': set(), 'N': {5|6, 1|4}, 'W': {1|4}}
73+
# # not_with_local {'E': set(), 'N': {5|6, 1|4}, 'W': {1|4}}
74+
# # known_with_local {'W': {0|6}}
75+
# # prob.sum 0.3333333333333333
76+
# # scenarios []
77+
# # player_tiles PlayerTiles(N=1, E=1, W=1)
78+
# # Remaining tiles: [0|0, 0|1, 0|2, 0|6, 1|3, 1|4, 1|5, 1|6, 2|2, 2|3, 2|6, 3|3, 5|6]
79+
80+
# remaining_tiles = set([
81+
# DominoTile(0, 0), DominoTile(0, 1), DominoTile(0, 2), DominoTile(0, 6),
82+
# DominoTile(1, 3), DominoTile(1, 4), DominoTile(1, 5), DominoTile(1, 6),
83+
# DominoTile(2, 2), DominoTile(2, 3), DominoTile(2, 6), DominoTile(3, 3),
84+
# DominoTile(5, 6)
85+
# # DominoTile(2, 4), DominoTile(3, 6) # Tiles in human player's hand
86+
# ])
87+
88+
# # Define not_with based on _knowledge_tracker
89+
# not_with = {
90+
# 'E': set(),
91+
# 'N': {DominoTile(5, 6), DominoTile(1, 4)},
92+
# 'W': {DominoTile(1, 4)}
93+
# }
94+
95+
# # Define player_tiles (assuming 7 tiles per player at the start)
96+
# player_tiles = PlayerTiles(N=1, E=1, W=1)
97+
98+
# # Call calculate_tile_probabilities
99+
# # probabilities = calculate_tile_probabilities(remaining_tiles, not_with, player_tiles)
100+
# # Print the results
101+
# # for tile, probs in probabilities.items():
102+
# # print(f"Tile {tile}:")
103+
# # for player, prob in probs.items():
104+
# # print(f" P({player} has {tile}) = {prob:.6f}")
105+
# # print()
106+
107+
# player_tiles = PlayerTiles(N=4, E=4, W=5)
108+
# sample = generate_sample(remaining_tiles, not_with, player_tiles)
109+
# print('sample',sample)
110+
111+
112+
113+
# def test_generate_scenarios():
114+
# player_tiles = [DominoTile(5,6)]
115+
# not_with = {'E': set(), 'N': set(), 'W': {DominoTile(5,6)}}
116+
# known_with = {'N': set(), 'E': set(), 'W': set()}
117+
# player_tiles = PlayerTiles(N=5, E=6, W=6)
118+
# scenarios = generate_scenarios(player_tiles, not_with, known_with)
119+
# print(scenarios)
120+
# print("known_with['N'].union(known_with['E']).union(known_with['W'])",known_with['N'].union(known_with['E']).union(known_with['W']))
121+
# not_with_local = copy.deepcopy(not_with)
122+
# # If found a duplication in not_with, it's added now to known_with and need to be removed from not_with
123+
# if any(len(s)>0 for s in known_with.values()):
124+
# for p, p_set in not_with_local.items():
125+
# not_with_local[p] = not_with_local[p] - known_with['N'].union(known_with['E']).union(known_with['W'])
126+
# print('not_with_local',not_with_local)
127+
128+
129+
def test_initial_moves() -> None:
130+
131+
# Player's hand: [4|6, 1|3, 2|2, 3|4, 0|4, 2|6, 2|3]
132+
# Remaining tiles: [0|0, 0|1, 0|2, 0|3, 0|5, 0|6, 1|1, 1|2, 1|4, 1|5, 1|6, 2|4, 2|5, 3|3, 3|5, 3|6, 4|4, 4|5, 5|5, 5|6, 6|6]
133+
# Move Statistics (based on 5 samples):
134+
135+
# Move: Play 3|4 on the left
136+
# Count: 5
137+
# Mean Score: -5.5000
138+
# Standard Deviation: 46.9441
139+
# Median Score: -30.0000
140+
# Mode Score: -30.0000
141+
# Min Score: -44.0000
142+
# Max Score: 68.0000
143+
144+
# Best Move Overall:
145+
# Best move: Play 3|4 on the left
146+
# Mean Expected Score: -5.5000
147+
# First move: (3, 4)
148+
ai_player = AnalyticAgentPlayer()
149+
verbose = True
150+
# unplayed_tiles = self.get_unplayed_tiles(game_state, player_hand)
151+
unplayed_tiles = [(0,0), (0,1), (0,2), (0,3), (0,5), (0,6), (1,1), (1,2), (1,4), (1,5), (1,6), (2,4), (2,5), (3,3), (3,5), (3,6), (4,4), (4,5), (5,5), (5,6), (6,6)]
152+
_unplayed_tiles = DominoTile.loi_to_domino_tiles(unplayed_tiles)
153+
154+
player_hand = [(4,6), (1,3), (2,2), (3,4), (0,4), (2,6), (2,3)]
155+
_player_hand = DominoTile.loi_to_domino_tiles(player_hand)
156+
157+
# _moves = history_to_domino_tiles_history(game_state.history)
158+
_moves = history_to_domino_tiles_history([])
159+
_remaining_tiles = set(_unplayed_tiles)
160+
# _initial_player_tiles = {p: 7 for p in PlayerPosition}
161+
_initial_player_tiles = {p: 7 for p in range(4)}
162+
# _starting_player = PlayerPosition((game_state.history[0][0] - self.position)%4) if len(game_state.history)>0 else PlayerPosition.SOUTH
163+
_starting_player = PlayerPosition_SOUTH
164+
165+
current_player, _final_remaining_tiles, _board_ends, _player_tiles_count, _knowledge_tracker = domino_game_state_our_perspective(
166+
_remaining_tiles, _moves, _initial_player_tiles, current_player=_starting_player)
167+
168+
if verbose:
169+
ai_player.print_verbose_info(_player_hand, _unplayed_tiles, _knowledge_tracker, _player_tiles_count, _starting_player)
170+
171+
# num_samples = 1000 if len(game_state.history) > 8 else 100 if len(game_state.history) > 4 else 25 if len(game_state.history) > 0 else 1
172+
num_samples = 1
173+
best_move = ai_player.get_best_move(set(_player_hand), _remaining_tiles, _knowledge_tracker, _player_tiles_count, _board_ends, num_samples, verbose=True)
174+
175+
# inferred_knowledge: dict[PlayerPosition, set[DominoTile]] = {
176+
# player: set() for player in range(4)
177+
# }
178+
179+
# sample = generate_sample_from_game_state(
180+
# # PlayerPosition.SOUTH,
181+
# PlayerPosition_SOUTH,
182+
# set(_player_hand),
183+
# set(_unplayed_tiles),
184+
# _player_tiles_count,
185+
# inferred_knowledge
186+
# )
187+
188+
# sample_hands = (
189+
# frozenset(_player_hand),
190+
# frozenset(sample['E']),
191+
# frozenset(sample['N']),
192+
# frozenset(sample['W'])
193+
# )
194+
195+
# sample_state = GameState(
196+
# player_hands=sample_hands,
197+
# # current_player=PlayerPosition.SOUTH,
198+
# current_player=PlayerPosition_SOUTH,
199+
# left_end=_board_ends[0],
200+
# right_end=_board_ends[1],
201+
# consecutive_passes=0
202+
# )
203+
204+
# possible_moves = list_possible_moves(sample_state)
205+
206+
# print('possible_moves', possible_moves)
90207

91-
# Define player_tiles (assuming 7 tiles per player at the start)
92-
player_tiles = PlayerTiles(N=1, E=1, W=1)
93-
94-
# Call calculate_tile_probabilities
95-
# probabilities = calculate_tile_probabilities(remaining_tiles, not_with, player_tiles)
96-
# Print the results
97-
# for tile, probs in probabilities.items():
98-
# print(f"Tile {tile}:")
99-
# for player, prob in probs.items():
100-
# print(f" P({player} has {tile}) = {prob:.6f}")
101-
# print()
102-
103-
player_tiles = PlayerTiles(N=4, E=4, W=5)
104-
sample = generate_sample(remaining_tiles, not_with, player_tiles)
105-
print('sample',sample)
106-
107-
108-
109-
def test_generate_scenarios():
110-
player_tiles = [DominoTile(5,6)]
111-
not_with = {'E': set(), 'N': set(), 'W': {DominoTile(5,6)}}
112-
known_with = {'N': set(), 'E': set(), 'W': set()}
113-
player_tiles = PlayerTiles(N=5, E=6, W=6)
114-
scenarios = generate_scenarios(player_tiles, not_with, known_with)
115-
print(scenarios)
116-
print("known_with['N'].union(known_with['E']).union(known_with['W'])",known_with['N'].union(known_with['E']).union(known_with['W']))
117-
not_with_local = copy.deepcopy(not_with)
118-
# If found a duplication in not_with, it's added now to known_with and need to be removed from not_with
119-
if any(len(s)>0 for s in known_with.values()):
120-
for p, p_set in not_with_local.items():
121-
not_with_local[p] = not_with_local[p] - known_with['N'].union(known_with['E']).union(known_with['W'])
122-
print('not_with_local',not_with_local)
123208

124209
if __name__ == "__main__":
125-
test_calculate_probabilities2()
126-
# test_generate_scenarios()
210+
# test_calculate_probabilities2()
211+
# test_generate_scenarios()
212+
test_initial_moves()

0 commit comments

Comments
 (0)