forked from nneonneo/threes-ai
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmanual_assistant.py
141 lines (115 loc) · 4.24 KB
/
manual_assistant.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
''' Help the user achieve a high score in a real game of threes by using a move searcher.
This assistant takes manual input from the user, allowing it to be used with any game. '''
from __future__ import print_function
import os
import numpy as np
import re
import sys
from base_assistant import run_assistant, movenames
from threes import do_move, get_lines
PY3 = sys.version_info[0] >= 3
if not PY3:
range = xrange
input = raw_input
def to_ind(val):
try:
return {0:0, 1:1, 2:2, 3:3, 6:4, 12:5, 24:6, 48:7, 96:8, 192:9, 384:10, 768:11, 1536:12, 3072:13, 6144:14}[val]
except KeyError as e:
raise Exception("Invalid value %s" % val)
class ManualAssistant:
def __init__(self):
self.last_board = None
self.last_move = None
def _ask_tileset(self):
tileset = input("Upcoming tile(s)? ")
tileset = {'blue': '1', 'red': '2', 'white': '3+'}.get(tileset, tileset)
if tileset in ('3+', '6+'):
return tileset # will be fixed up
tileset = re.split(r'[\s,]', tileset)
return {to_ind(int(v)) for v in tileset}
def _fixup_tileset(self, tileset, board):
if tileset not in ('3+', '6+'):
return tileset
maxval = board.max()
out = set(range(4, maxval-3+1))
if tileset == '3+':
out |= {3}
else:
out |= {4} # make sure the tileset isn't empty
return out
def _parse_delta(self, ind, val=None, move=None):
if self.last_board is None:
raise Exception("Can't specify a delta: last board is unknown")
ind = int(ind)
if val is None:
if len(self.last_tiles) > 1:
raise Exception("Can't omit tile value: multiple possible previous tiles")
val = list(self.last_tiles)[0]
else:
val = to_ind(int(val))
if val not in self.last_tiles:
raise Exception("New tile wasn't in previous tile set")
if move is None:
move = self.last_move
move = movenames.index(move)
newboard = self.last_board.copy()
changed = do_move(newboard, move)
line = get_lines(newboard, move)[ind-1]
if line[-1] != 0:
raise Exception("Incorrect changed row/col")
line[-1] = val
return newboard
def _parse_board(self, bits):
out = np.array([to_ind(int(x)) if x else 0 for x in bits], dtype=int)
return out.reshape((4,4))
def _ask_board(self):
if self.last_board is None:
print("Current board?")
else:
print("Current board or difference from last board?")
bits = []
while 1:
line = re.split(r'[\s,]+', input())
bits += line
if 1 <= len(bits) < 4:
return self._parse_delta(*bits)
elif len(bits) == 16:
return self._parse_board(bits)
elif len(bits) > 16:
raise Exception("More than 16 numbers specified!")
def gen_board(self):
while 1:
while 1:
try:
board = self._ask_board()
break
except Exception as e:
print("Didn't understand your input:", e)
while 1:
try:
tileset = self._ask_tileset()
break
except Exception as e:
print("Didn't understand your input:", e)
tileset = self._fixup_tileset(tileset, board)
yield board, tileset, False
self.last_board = board
self.last_tiles = tileset
def make_move(self, move):
print("*** Suggested move:", move)
print()
self.last_move = move
def parse_args(argv):
import argparse
parser = argparse.ArgumentParser(description="Suggest moves for Threes!")
args = parser.parse_args(argv)
return args
def main(argv):
from itertools import count
args = parse_args(argv)
print('Welcome to the Threes! assistant. See README.md for help on input formats.')
assistant = ManualAssistant()
run_assistant(assistant.gen_board(), assistant.make_move, False)
if __name__ == '__main__':
import sys
exit(main(sys.argv[1:]))