-
Notifications
You must be signed in to change notification settings - Fork 0
/
game.py
96 lines (69 loc) · 2.37 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
import abc
import itertools
import typing
def to_non_empty(iterable):
try:
return itertools.chain([next(iterable)], iterable)
except StopIteration:
return None
class Player(abc.ABC):
@abc.abstractmethod
def get_move(self, state: 'GameState'): pass
def notify_bad_move(self):
pass
def notify_game_end(self, state):
pass
class GameState(abc.ABC):
# @abc.abstractstaticmethod
# def initial_state(self) -> 'GameState': pass
def __init__(self, players, player_index=0):
self.m_players = players
self.m_curr_player_index = player_index
self.m_moves = None
def get_curr_player(self):
return self.m_players[self.m_curr_player_index]
def _next_player_index(self):
return (self.m_curr_player_index + 1) % len(self.m_players)
def notify_move(self):
pass
@abc.abstractmethod
def get_winner(self) -> Player: pass
@abc.abstractmethod
def eval(self) -> int: pass
@abc.abstractmethod
def get_moves(self) -> typing.Generator[int, None, None]: pass
def no_moves(self):
return to_non_empty(self.get_moves()) is None
@abc.abstractmethod
def move(self, move) -> 'GameState': pass
@abc.abstractmethod
def __eq__(self, other) -> bool:
return self.m_curr_player_index == other.m_curr_player_index
class Game:
def __init__(self, start_state: GameState):
self.m_state: GameState = start_state
self.m_winner: Player = None
def _is_tie(self):
return self.m_state.no_moves()
def _is_game_over(self):
self.m_winner = self.m_state.get_winner()
return self.m_winner is not None or self._is_tie()
def _do_next_move(self):
new_state = None
curr_player = self.m_state.get_curr_player()
while new_state is None:
move = curr_player.get_move(self.m_state)
new_state = self.m_state.move(move)
if new_state is None:
curr_player.notify_bad_move()
self.m_state = new_state
self.m_state.notify_move()
def get_winner(self):
return self.m_winner
def play(self):
yield self.m_state
while not self._is_game_over():
self._do_next_move()
yield self.m_state
for player in self.m_state.m_players:
player.notify_game_end(self.m_state)