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