-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathgame.py
211 lines (151 loc) · 6.54 KB
/
game.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
"""This module controls the entire Ludo Game and interfaces with the provided client."""
# stdlib
import sys
import time
from itertools import permutations
# Our Code
from player import Player, Coin
from config import log
# These are used to interact with the client
def read_line():
return sys.stdin.readline().strip()
def read_moves():
return read_line().split("<next>")
def write_output(txt):
sys.stdout.write(txt + "\n")
sys.stdout.flush()
def read_die():
die = read_line()
if "DUCK" in die:
return []
return list(map(int, die.split(" ")[2:]))
class LudoGame:
def __init__(self, player_id, game_mode):
self.my_id = player_id
# Our games will only ever have 2 players
if game_mode == 0:
colors = ["RED", "YELLOW"]
else:
colors = ["BLUE", "GREEN"]
self.player = Player(colors[(self.my_id + 1) % 2])
self.opponent = Player(colors[self.my_id % 2])
# Position of each coin on the board is the core state of the game
# Store references to all coins
self.coins = self.player.coins.copy()
self.coins.update(self.opponent.coins)
def dump_state(self):
"""Serialize the state of the game."""
s = ""
# TODO: Players is not really required in state ?
s += "Players: " + "%s, %s" % (self.player.color, self.opponent.color)
s += "\n"
s += "Coins: " + ", ".join(["%s_%d" % (c, c.rel_pos) for c in self.coins.values()])
return s
def load_state(self, state):
"""Load the state of the game from a dump."""
players, coins, _ = state.strip().split("\n")
# Break second part into a list
_list = lambda s: s.strip().split(": ")[1].split(", ")
self.player = Player(_list(players)[0])
self.opponent = Player(_list(players)[1])
# A list of coin states
coin_objects = {}
for cs in _list(coins):
coin = Coin(cs[0], int(cs[1]))
coin.rel_pos = int(cs.split("_")[1])
coin_objects[str(coin)] = coin
self.coins = coin_objects
def run(self, no_board=False):
# I'm the 2nd player
if self.my_id == 2:
dice = read_line()
moves = read_moves()
# Update coin positions using these moves
self.opponent.make_moves(moves, self.player)
if not no_board:
self.update_board.emit(self.coins)
self.update_status.emit(self.opponent, moves)
# Track whether the 2nd player is repeating
opponent_repeating = False
while True:
log.warn(self.dump_state())
# 2nd player is not repeating, so it is my turn!
if not opponent_repeating:
# Roll the die
write_output("<THROW>")
# Read die rolls from client (stdin)
die_rolls = read_die()
log.info("Received Roll: %s", die_rolls)
# Save state
saved_positions = {
name: coin.rel_pos
for (name, coin) in self.coins.items()
}
# Store: [(possible_moves, benefit)]
all_possible_moves = []
# Consider all possible unique permutations of moves!
for possible_rolls in set(permutations(die_rolls)):
# Find all moves possible for this permutation of the rolls
possible_moves, benefit = self.player.get_multiple_moves(possible_rolls, self.opponent)
# Use percent_complete & profits of each move
# Add it to list
if possible_moves:
all_possible_moves.append((possible_moves, benefit + self.player.percent_complete - self.opponent.percent_complete))
# log.warn("State After these moves")
# log.warn(self.dump_state())
# Reset state
for name, coin in self.coins.items():
coin.rel_pos = saved_positions[name]
# log.warn("State Reset")
# log.warn(self.dump_state())
# if no move possible
if not all_possible_moves:
moves = "NA"
else:
# Only keep possible moves of maximal length
maximal = max(all_possible_moves, key=lambda t: len(t[0]))
max_len = len(maximal[0])
all_valid_moves = filter(lambda t: len(t[0]) == max_len, all_possible_moves)
# log.critical("Max len: %r", max_len)
# log.critical("Possible: %r", all_possible_moves)
# log.critical("Valid: %r", all_valid_moves)
# Sort all_valid_moves based on benefit
moves = sorted(all_valid_moves, key=lambda t: t[1])[-1][0]
# Play finally decided moves
self.player.make_moves(moves, self.opponent)
if not no_board:
self.update_board.emit(self.coins)
self.update_status.emit(self.player, moves)
# Convert to a format that the external client understands
moves = "<next>".join(moves)
# Send the moves to client (stdout)
log.info("Sending Moves: %s", moves)
write_output(moves)
else:
opponent_repeating = False
# Now read in opponent's dice rolls & moves
dice = read_line()
# If the moves I played didn't result in a REPEAT
# The opponent will now get another chance
if dice != "REPEAT":
moves = read_moves()
# Opponent made a move that resulted in a REPEAT!
# So the next turn won't be mine
if moves[-1] == 'REPEAT':
opponent_repeating = True
# Remove "REPEAT" from moves list
moves.pop()
self.opponent.make_moves(moves, self.player)
if not no_board:
self.update_board.emit(self.coins)
self.update_status.emit(self.opponent, moves)
if __name__ == '__main__':
# Test Board states
g = LudoGame()
log.warn(g.dump_state())
st = """
Players: RED, YELLOW
Coins: Y3_2, R3_4, Y1_8, R1_0, Y2_0, R0_9, R2_18, Y0_0
"""
g.load_state(st)
log.warn(g.dump_state())