f8adde21071f89865425109af67349d27141e15e
[menace.git] / nac-trinket / menace.py
1 from nac import *
2
3 import itertools
4 import functools
5 import collections
6 import random
7
8 INITIAL_BEAD_COUNT = 3
9
10
11 def new_game(player1, player2):
12 return {'board': empty_board(),
13 'player1': player1,
14 'player2': player2,
15 'player1_active': True,
16 'history': []}
17
18 def game_finished(game):
19 return (winner(game['board']) is not None) or (game['board'].count('.') == 0)
20
21
22 def active_player(game):
23 if game['player1_active']:
24 return game['player1']
25 else:
26 return game['player2']
27
28
29 def new_menace():
30 boxes = {'human?': False}
31 for b in non_winning_boards():
32 box = collections.Counter()
33 for s in vacants(b):
34 box[s] = INITIAL_BEAD_COUNT
35 boxes[b] = box
36 return boxes
37
38 def menace_move(game):
39 board, r, f = canonical(game['board'])
40 player = active_player(game)
41 token = token_for_player(game['player1_active'])
42 cmove = random.choice(list(player[board].elements()))
43 cmove_board = apply_move(empty_board(), cmove, token)
44 moved_board = untransform(cmove_board, r, f)
45 return moved_board.index(token)
46
47 def new_human():
48 return {'human?': True}
49
50
51 def human_move(game):
52 if game['history']:
53 print('Opponent moved to', game['history'][-1]['move'])
54 else:
55 print('You play first.')
56 print('The board is:')
57 print(show_board(game['board']))
58
59 valid_moves = vacants(game['board'])
60 valid_input = False
61
62 while not valid_input:
63 user_input = input('Your move ({})? '.format(', '.join(str(m) for m in valid_moves)))
64 if user_input.isnumeric():
65 move = int(user_input)
66 if move in valid_moves:
67 valid_input = True
68 else:
69 print('Number not a valid move.')
70 else:
71 print('Please enter a number.')
72 return move
73
74 def make_move(game):
75 if game['player1_active']:
76 active = game['player1']
77 else:
78 active = game['player2']
79 if active['human?']:
80 move = human_move(game)
81 else:
82 move = menace_move(game)
83 token = token_for_player(game['player1_active'])
84 game['history'] += [{'player1?': game['player1_active'], 'move': move, 'board': game['board']}]
85 game['board'] = apply_move(game['board'], move, token)
86 game['player1_active'] = not game['player1_active']
87
88
89 def play_game(game):
90 while not game_finished(game):
91 make_move(game)
92
93 def winning_player(game):
94 if winner(game['board']) is None:
95 return None
96 elif game['history'][-1]['player1?']:
97 return game['player1']
98 else:
99 return game['player2']
100
101 def losing_player(game):
102 if winner(game['board']) is None:
103 return None
104 elif game['history'][-1]['player1?']:
105 return game['player2']
106 else:
107 return game['player1']
108
109
110 def winning_moves(game):
111 return [h for h in game['history']
112 if h['player1?'] == game['history'][-1]['player1?']]
113
114 def losing_moves(game):
115 return [h for h in game['history']
116 if h['player1?'] != game['history'][-1]['player1?']]
117
118
119 def update_players(game, allow_drop_move=False):
120 if winner(game['board']) is not None:
121 update_winner(game)
122 update_loser(game, allow_drop_move=allow_drop_move)
123
124
125 def update_winner(game):
126 player = winning_player(game)
127 moves = winning_moves(game)
128 for m in moves:
129 board, r, f = canonical(m['board'])
130 move_board = apply_move(empty_board(), m['move'], '+')
131 cmove_board = transform(move_board, r, f)
132 cmove = cmove_board.index('+')
133 player[board][cmove] += 1
134
135 def update_loser(game, allow_drop_move=False):
136 player = losing_player(game)
137 moves = losing_moves(game)
138 for m in moves:
139 board, r, f = canonical(m['board'])
140 move_board = apply_move(empty_board(), m['move'], '+')
141 cmove_board = transform(move_board, r, f)
142 cmove = cmove_board.index('+')
143 # if player[board][cmove] > 1:
144 # player[board][cmove] -= 1
145 if allow_drop_move:
146 if len(list(player[board].elements())) > 1:
147 player[board][cmove] -= 1
148 else:
149 if player[board][cmove] > 1:
150 player[board][cmove] -= 1
151
152
153
154 def count_wins(p1, p2, plays=1000):
155 wins = 0
156 draws = 0
157 for _ in range(plays):
158 g = new_game(p1, p2)
159 play_game(g)
160 if winner(g['board']) is None:
161 draws += 1
162 elif winning_player(g) == p1:
163 wins += 1
164 return wins, draws
165
166
167 def game_with_players(p1, p2, report_result_for=None):
168 if random.choice([True, False]):
169 g = new_game(p1, p2)
170 else:
171 g = new_game(p2, p1)
172 play_game(g)
173
174 if report_result_for:
175 print('\nFinal position')
176 print(show_board(g['board']))
177 if winner(g['board']) is None:
178 print('A draw')
179 elif winning_player(g) == ph:
180 print('You won')
181 else:
182 print('You lost')
183
184 return g
185
186 def train_players(p1, p2, rounds=10000, allow_drop_move=False):
187 for _ in range(rounds):
188 g = game_with_players(p1, p2)
189 update_players(g, allow_drop_move=allow_drop_move)
190 return p1, p2
191