-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmancala.py
132 lines (114 loc) · 4.46 KB
/
mancala.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
import sys
STORE = 6
class Game:
def __init__(self, players):
self.board = {1: [4,4,4,4,4,4,0], 2: [4,4,4,4,4,4,0]}
self.players = players
self.current_player = 1
def copy(self):
g = Game(self.players)
g.board = {1: self.board[1][:], 2: self.board[2][:]}
g.current_player = self.current_player
return g
def health_check(self):
total_stones = sum([sum(self.board[side]) for side in self.board])
if total_stones != 48:
print('HEALTH CHECK FAILED. TOTAL STONES:', total_stones)
self.print_board()
sys.exit()
def print_board(self, info=None):
if info:
print(info)
print(self.board[2][STORE], [p for p in reversed(self.board[2][:STORE])])
print(' ', self.board[1][:STORE], self.board[1][STORE])
print('---------')
def get_state(self):
return self.board[1] + self.board[2]
def get_valid_moves(self):
return [i for i in range(STORE) if not self.is_pit_empty(self.current_player, i)]
def check_bonus_round(self, landing):
return landing['pit'] == STORE
def is_game_over(self):
return any(sum(self.board[side][:STORE]) == 0 for side in self.board)
def is_pit_empty(self, side, pit):
return self.board[side][pit] == 0
def switch_side(self, side):
return 2 if side == 1 else 1
def player_choice(self):
return self.players[self.current_player].act(self)
def move(self, side, pit, simulate=False):
stones = self.board[side][pit]
if not simulate:
self.board[side][pit] = 0
start_side = side
while stones > 0:
pit = (pit + 1) % (STORE + 1)
if pit == STORE:
if start_side == side:
if not simulate:
self.board[side][STORE] += 1
stones -= 1
if stones > 0:
pit = -1
side = self.switch_side(side)
else:
if not simulate:
self.board[side][pit] += 1
stones -= 1
return {'side':side, 'pit':pit}
def capture(self, side, landing):
if side != landing['side'] or self.board[side][landing['pit']] > 1 or landing['pit'] == STORE:
return False
opponent = self.switch_side(side)
captured_pit = (STORE - 1) - landing['pit']
stones = self.board[opponent][captured_pit]
if stones == 0:
return False
self.board[side][STORE] += stones + 1
self.board[opponent][captured_pit] = 0
self.board[side][landing['pit']] = 0
return True
def capture_exposure(self):
exposures = set()
opponent = self.switch_side(self.current_player)
possible_moves = [pit for pit in range(STORE) if self.board[opponent][pit] > 0]
for p in possible_moves:
simulation = self.copy()
landing = simulation.move(opponent, p)
if simulation.capture(opponent, landing):
captured_pit = (STORE - 1) - landing['pit']
exposures.add((landing['pit'], self.board[self.current_player][captured_pit]))
return sum([s[1] for s in exposures])
def get_winner(self):
p1_stones = sum(self.board[1])
p2_stones = sum(self.board[2])
if p1_stones > p2_stones:
return 1
if p1_stones < p2_stones:
return 2
return 0
def step(self, pit, verbose=False):
info = {}
info['player'] = self.current_player
info['pit'] = pit
info['landing'] = self.move(self.current_player, pit)
info['capture'] = self.capture(self.current_player, info['landing'])
info['bonus_round'] = self.check_bonus_round(info['landing'])
info['game_over'] = self.is_game_over()
info['capture_exposure'] = self.capture_exposure()
if verbose:
self.print_board(info)
self.health_check()
if not info['bonus_round']:
self.current_player = 2 if self.current_player == 1 else 1
return info
def game_loop(self, verbose=False):
if verbose:
self.print_board()
while not self.is_game_over():
info = self.step(self.player_choice(), verbose)
if info['game_over']:
break
if verbose:
print('Winner:', self.get_winner())
return self.get_winner()