Compare commits

...

9 Commits
master ... test

Author SHA1 Message Date
randomuser 467e53b1ce add evaulation function 2022-09-14 19:56:11 -05:00
randomuser 9d7cfee1f9 more 2022-07-11 19:33:16 -05:00
randomuser b5f2efb316 whoops 2022-07-11 19:28:20 -05:00
randomuser 842291d6d6 crappy algorithm 2022-07-11 19:27:49 -05:00
randomuser b30c1a7ffe add cli and improved board interface 2022-07-09 22:27:42 -05:00
randomuser c7f28a5c64 node implimentation 2022-07-09 18:45:25 -05:00
randomuser 845a0bcaad add test framework 2022-07-09 18:45:03 -05:00
randomuser 8c55d05404 minor fixes 2022-07-09 18:43:50 -05:00
randomuser 15aab805b9 add better board implimentation and positional object 2022-07-07 21:53:50 -05:00
6 changed files with 357 additions and 5 deletions

11
TODO Normal file
View File

@ -0,0 +1,11 @@
TODO
----
- make an interface for picking moves
- make an interface, period
- back-propagate values up the tree
BUGS
----
- test framework makes mistake

125
board2.py Normal file
View File

@ -0,0 +1,125 @@
#!/usr/bin/env python
import copy
from pos import TwoDPos
class BoardTools:
def __init__(self):
pass
def winning(self, b):
for i in range(3):
if b[i][0] == b[i][1] == b[i][2]:
if b[i][0] != Board.unplayed:
return b[i][0]
if b[0][i] == b[1][i] == b[2][i]:
if b[0][i] != Board.unplayed:
return b[0][i]
if b[0][0] == b[1][1] == b[2][2]:
if b[0][0] != Board.unplayed:
return b[0][0]
if b[2][0] == b[1][1] == b[0][2]:
if b[2][0] != Board.unplayed:
return b[2][0]
spaces = 0
for i in range(3):
for j in range(3):
if b[i][j] != Board.unplayed:
spaces += 1
if spaces == 9:
return Board.tie
return Board.unplayed
class Board:
unplayed = 0
cross = 1
nought = 2
tie = 3
def __init__(self):
self.board = [
[
[
[Board.unplayed for i in range(3)]
for j in range(3)
]
for k in range(3)
]
for l in range(3)
]
self.moves = []
self.turn = Board.cross
def copy(self):
new = Board()
new.board = copy.deepcopy(self.board)
new.moves = copy.deepcopy(self.moves)
new.turn = copy.deepcopy(self.turn)
return new
def set(self, a, b, c, d, val):
self.board[a][b][c][d] = val
def get(self, a, b, c, d):
return self.board[a][b][c][d]
def alternate(self):
if self.turn == Board.cross:
self.turn = Board.nought
else:
self.turn = Board.cross
def subWin(self, x, y):
return BoardTools.winning(None, self.board[x][y])
def metaboard(self):
return [[BoardTools.winning(None, self.board[x][y]) for x in range(3)] for y in range(3)]
def win(self):
return BoardTools.winning(self.metaboard())
def append(self, move):
self.moves.append(move)
self.set(*move.local(), self.turn)
self.alternate()
def reachableEmpty(self):
metaboard = self.metaboard()
buf = []
for i in range(3):
for j in range(3):
if metaboard[j][i] == Board.unplayed:
for k in range(3):
for l in range(3):
if self.board[i][j][k][l] == Board.unplayed:
buf.append(TwoDPos(TwoDPos.l, (i, j, k, l)))
return buf
def possible(self):
# check if board is in final state
isFinal = BoardTools.winning(self, self.metaboard())
if isFinal in [Board.tie, Board.cross, Board.nought]:
return []
try:
move = self.moves[-1]
except IndexError:
buf = []
for i in range(3):
for j in range(3):
for k in range(3):
for l in range(3):
buf.append(TwoDPos(TwoDPos.l, (i, j, k, l)))
return buf
local = move.local()
if self.subWin(local[2], local[3]) in [Board.tie, Board.nought, Board.cross]:
return self.reachableEmpty()
buf = []
for i in range(3):
for j in range(3):
if self.board[local[2]][local[3]][i][j] == Board.unplayed:
buf.append(TwoDPos(TwoDPos.l, (local[2], local[3], i, j)))
return buf

138
cli.py Normal file
View File

@ -0,0 +1,138 @@
#!/usr/bin/env python
from board2 import Board
from board2 import BoardTools
from pos import TwoDPos
from pos import TwoDUtils
from node import Node
class StateError(BaseException):
pass
piecemaps = {
0: '.',
1: 'x',
2: 'o',
}
mappings = TwoDUtils.reverse
def tree_of_node(node, count=5):
if count == 1:
score = 0
for i in range(9):
for j in range(9):
p = TwoDPos(TwoDPos.g, (i + 1, j + 1))
a, b, c, d = p.local()
if node.inner.board[a][b][c][d] == Board.cross:
score += 1
elif node.inner.board[a][b][c][d] == Board.nought:
score -= 1
return score
parent = node
print("considering board with moveset " + str(parent.inner.moves))
possible = parent.inner.possible()
if len(possible) != 0:
print("permuting!")
for i in possible:
copy = parent.inner.copy()
copy.append(i)
childnode = Node()
childnode.inner = copy
parent.children.append(childnode)
for i in parent.children:
tree_of_node(i, count - 1)
else:
print("calculated score")
rawscore = BoardTools.winning(None, parent.inner.metaboard())
if rawscore == Board.cross:
score = -1
elif rawscore == Board.nought:
score = 1
elif rawscore == Board.tie:
score = 0
else:
score = None
raise StateError("aaaa")
print("score" + str(score))
return score
def render(board):
print("123 456 789")
moves = board.possible()
moves = [i.glob() for i in moves]
for i in range(9):
buf = []
for j in range(9):
ri = mappings[i]
string = ''.join([ri, str(j + 1)])
pos = TwoDPos(TwoDPos.g, string)
item = board.get(*pos.local())
if string in moves:
buf.append('?')
else:
buf.append(piecemaps[item])
if j in [2, 5]:
buf.append(" | ")
if j == 8:
buf.append(" " + ri)
print(''.join(buf))
if i in [2, 5]:
print("----+-----+----")
print("to play: " + str(board.turn))
def help_text():
print("m - make a move")
print("p - see coordinates of legal moves")
print("t - see whose turn it is")
print("r - render the board to the screen")
print("q - quit program")
print("h - show help (this list)")
def move(board, move):
move = TwoDPos(TwoDPos.g, move)
print(move)
board.append(move)
def possibilities(board):
print(board.possible())
def turn(board):
print("to play: " + str(board.turn))
def main():
b = Board()
print("type h for help")
while True:
# try:
cmd = input("> ")
spl = cmd.split(' ')
if spl[0] == "m":
move(b, spl[1])
elif spl[0] == "p":
possibilities(b)
elif spl[0] == "t":
turn(b)
elif spl[0] == "r":
render(b)
elif spl[0] == "h":
help_text()
elif spl[0] == "z":
# create node from current board
node = Node()
node.inner = b
tree_of_node(node)
elif spl[0] == "q":
break;
# except:
# print("error occured")
return
if __name__ == "__main__":
main()

17
node.py Normal file
View File

@ -0,0 +1,17 @@
# node implementation
class Node:
def __init__(self):
self.children = []
self.score = None
self.inner = None
def calculate(self):
if self.children == []:
return self.score
total = 0
items = 0
for i in self.children:
score = i.calculate()
if score != None:
items += 1
total += score

12
pos.py
View File

@ -21,16 +21,26 @@ class TwoDPos:
l = 1 l = 1
def __init__(self, mode, param): def __init__(self, mode, param):
if mode == TwoDPos.g: if mode == TwoDPos.g:
try:
self.pos = ( self.pos = (
TwoDUtils.mappings[param[0]], TwoDUtils.mappings[param[0]],
int(param[1]) - 1, int(param[1]) - 1,
) )
except KeyError:
self.pos = (
int(param[0]) - 1,
int(param[1]) - 1,
)
elif mode == TwoDPos.l: elif mode == TwoDPos.l:
self.pos = ( self.pos = (
param[0] * 3 + param[2], param[0] * 3 + param[2],
param[1] * 3 + param[3] + 1, param[1] * 3 + param[3],
) )
def __repr__(self):
return ' '.join([self.glob(), str(self.local())])
def glob(self): def glob(self):
ret = [] ret = []
ret.append(TwoDUtils.reverse[self.pos[0]]) ret.append(TwoDUtils.reverse[self.pos[0]])

51
test.py Normal file
View File

@ -0,0 +1,51 @@
#!/usr/bin/env python3
from board2 import Board
from pos import TwoDPos
b = Board()
print(b.possible())
print("moving to d4")
b.append(TwoDPos(TwoDPos.g, "d4"))
print(b.possible())
print("moving to b2")
b.append(TwoDPos(TwoDPos.g, "b2"))
print(b.possible())
print("moving to d5")
b.append(TwoDPos(TwoDPos.g, "d5"))
print(b.possible())
print("moving to b5")
b.append(TwoDPos(TwoDPos.g, "b5"))
print(b.possible())
print("moving to d6")
b.append(TwoDPos(TwoDPos.g, "d6"))
print(b.possible())
print("moving to b8")
b.append(TwoDPos(TwoDPos.g, "b8"))
print(b.possible())
print("there should be a square captured in the middle")
print("moving to a7")
b.append(TwoDPos(TwoDPos.g, "a7"))
print(b.possible())
print("moving to c3")
b.append(TwoDPos(TwoDPos.g, "c3"))
print(b.possible())
print("moving to g7")
b.append(TwoDPos(TwoDPos.g, "g7"))
print(b.possible())
print("moving to b3")
b.append(TwoDPos(TwoDPos.g, "b3"))
print(b.possible())
print("moving to d7")
b.append(TwoDPos(TwoDPos.g, "d7"))
print(b.possible())
print("moving to a3")
b.append(TwoDPos(TwoDPos.g, "a3"))
print(b.possible())
print("moving to c9")
b.append(TwoDPos(TwoDPos.g, "c9"))
print(b.possible())
print("moving to g9")
b.append(TwoDPos(TwoDPos.g, "g9"))
print(b.possible())