New: Ultimate TicTacToe

This commit is contained in:
Dominik Moritz Roth 2022-04-14 15:28:08 +02:00
parent f9a5ba1964
commit aabcea14ba
2 changed files with 134 additions and 10 deletions

120
ultimatetictactoe.py Normal file
View File

@ -0,0 +1,120 @@
from vacuumDecay import *
from collections import Counter
import itertools
class TTTState(State):
def __init__(self, curPlayer=0, generation=0, playersNum=2, board=None, lastMove=-1):
if type(board) == type(None):
board = "." * 81
self.curPlayer = curPlayer
self.generation = generation
self.playersNum = playersNum
self.board = board
self.last_move = lastMove
self.possible_goals = [(0, 4, 8), (2, 4, 6)]
self.possible_goals += [(i, i+3, i+6) for i in range(3)]
self.possible_goals += [(3*i, 3*i+1, 3*i+2) for i in range(3)]
self.update_box_won()
def update_box_won(self):
state = self.board
temp_box_win = ["."] * 9
for b in range(9):
idxs_box = self.indices_of_box(b)
box_str = state[idxs_box[0]: idxs_box[-1]+1]
temp_box_win[b] = self.check_small_box(box_str)
self.box_won = temp_box_win
def indices_of_box(self, b):
return list(range(b*9, b*9 + 9))
def check_small_box(self, box_str):
for idxs in self.possible_goals:
(x, y, z) = idxs
if (box_str[x] == box_str[y] == box_str[z]) and box_str[x] != ".":
return box_str[x]
return "."
def mutate(self, action):
newBoard = self.board[:action.data] + ['O','X'][self.curPlayer] + self.board[action.data+1:]
return TTTState(curPlayer=(self.curPlayer+1)%self.playersNum, playersNum=self.playersNum, board=newBoard, lastMove=action.data)
def box(self, x, y):
return index(x, y) // 9
def next_box(self, i):
return i % 9
def indices_of_box(self, b):
return list(range(b*9, b*9 + 9))
def index(self, x, y):
x -= 1
y -= 1
return ((x//3)*27) + ((x % 3)*3) + ((y//3)*9) + (y % 3)
def getAvaibleActions(self):
if self.last_move == -1:
for i in range(9*9):
yield Action(self.curPlayer, i)
return
box_to_play = self.next_box(self.last_move)
idxs = self.indices_of_box(box_to_play)
if self.box_won[box_to_play] != ".":
pi_2d = [self.indices_of_box(b) for b in range(9) if self.box_won[b] == "."]
possible_indices = list(itertools.chain.from_iterable(pi_2d))
else:
possible_indices = idxs
for ind in possible_indices:
if self.board[ind]=='.':
yield Action(self.curPlayer, ind)
def getScoreFor(self, player):
p = ['O','X'][player]
sco = 5
for w in self.box_won:
if w==p:
sco += 1
elif w!='.':
sco -= 0.5
return 1/sco
def checkWin(self):
self.update_box_won()
game_won = self.check_small_box(self.box_won)
if game_won == '.':
return None
return game_won == 'X'
def __str__(self):
state = self.board
acts = list(self.getAvaibleActions())
if len(acts)<=9:
for i, act in enumerate(acts):
state = state[:act.data] + str(i+1) + state[act.data+1:]
s = []
for row in range(1, 10):
row_str = ["|"]
for col in range(1, 10):
row_str += [state[self.index(row, col)]]
if (col) % 3 == 0:
row_str += ["|"]
if (row-1) % 3 == 0:
s.append("-"*(len(row_str)*2-1))
s.append(" ".join(row_str))
s.append("-"*(len(row_str)*2-1))
return '\n'.join(s)
def getTensor(self):
pass
@classmethod
def getModel():
pass
if __name__=="__main__":
run = Runtime(TTTState())
run.game(None, 4)

View File

@ -2,7 +2,7 @@ import time
import random import random
import threading import threading
import torch import torch
from math import sqrt from math import sqrt, inf
#from multiprocessing import Event #from multiprocessing import Event
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from threading import Event from threading import Event
@ -84,7 +84,10 @@ class State(ABC):
@abstractmethod @abstractmethod
def getAvaibleActions(self): def getAvaibleActions(self):
# Should return an array of all possible actions # Should return an array of all possible actions
return [] return [i]
def askUserForAction(self, actions):
return choose('What does player '+str(self.curPlayer)+' want to do?', actions)
# improveMe # improveMe
def getPriority(self, score, cascadeMemory=None): def getPriority(self, score, cascadeMemory=None):
@ -176,7 +179,7 @@ class Node():
for p in range(self.playersNum): for p in range(self.playersNum):
cp = self.state.curPlayer cp = self.state.curPlayer
if cp == p: # P owns the turn; controlls outcome if cp == p: # P owns the turn; controlls outcome
best = 1000000000 best = inf
for c in self.childs: for c in self.childs:
if c.getStrongFor(p) < best: if c.getStrongFor(p) < best:
best = c.getStrongFor(p) best = c.getStrongFor(p)
@ -311,7 +314,8 @@ def choose(txt, options):
class Runtime(): class Runtime():
def __init__(self, initState): def __init__(self, initState):
self.head = Node(initState) universe = Universe()
self.head = Node(initState,universe = universe)
def performAction(self, action): def performAction(self, action):
for c in self.head.childs: for c in self.head.childs:
@ -323,16 +327,16 @@ class Runtime():
return return
raise Exception('No such action avaible...') raise Exception('No such action avaible...')
def turn(self, bot=None): def turn(self, bot=None, calcDepth=7):
print(str(self.head)) print(str(self.head))
if bot==None: if bot==None:
c = choose('?', ['human', 'bot', 'undo']) c = choose('Select action?', ['human', 'bot', 'undo'])
if c=='undo': if c=='undo':
self.head = self.head.parent self.head = self.head.parent
return return
bot = c=='bot' bot = c=='bot'
if bot: if bot:
self.head.forceStrong(7) self.head.forceStrong(calcDepth)
opts = [] opts = []
for c in self.head.childs: for c in self.head.childs:
opts.append((c, c.getStrongFor(self.head.curPlayer))) opts.append((c, c.getStrongFor(self.head.curPlayer)))
@ -344,11 +348,11 @@ class Runtime():
print('[#] I choose to play: ' + str(opts[0][0].lastAction)) print('[#] I choose to play: ' + str(opts[0][0].lastAction))
self.performAction(opts[0][0].lastAction) self.performAction(opts[0][0].lastAction)
else: else:
action = choose('What does player '+str(self.head.curPlayer)+' want to do?', self.head.avaibleActions) action = self.head.askUserForAction(self.head.avaibleActions)
self.performAction(action) self.performAction(action)
def game(self, bots=None): def game(self, bots=None, calcDepth=7):
if bots==None: if bots==None:
bots = [None]*self.head.playersNum bots = [None]*self.head.playersNum
while True: while True:
self.turn(bots[self.head.curPlayer]) self.turn(bots[self.head.curPlayer], calcDepth)