This commit is contained in:
Dominik Moritz Roth 2022-04-14 20:24:48 +02:00
parent aabcea14ba
commit 2ced953c77
2 changed files with 118 additions and 26 deletions

View File

@ -72,15 +72,18 @@ class TTTState(State):
if self.board[ind]=='.': if self.board[ind]=='.':
yield Action(self.curPlayer, ind) yield Action(self.curPlayer, ind)
def getScoreFor(self, player): #def getScoreFor(self, player):
p = ['O','X'][player] # p = ['O','X'][player]
sco = 5 # sco = 5
for w in self.box_won: # for w in self.box_won:
if w==p: # if w==p:
sco += 1 # sco += 1
elif w!='.': # elif w!='.':
sco -= 0.5 # sco -= 0.5
return 1/sco # return 1/sco
def getPriority(self, score, cascadeMem):
return self.generation + score*10 - cascadeMem*5 + 100
def checkWin(self): def checkWin(self):
self.update_box_won() self.update_box_won()
@ -117,4 +120,4 @@ class TTTState(State):
if __name__=="__main__": if __name__=="__main__":
run = Runtime(TTTState()) run = Runtime(TTTState())
run.game(None, 4) run.game([0,1], 4)

View File

@ -30,6 +30,9 @@ class Action():
return "<P"+str(self.player)+"-"+str(self.data)+">" return "<P"+str(self.player)+"-"+str(self.data)+">"
class Universe(): class Universe():
def __init__(self):
self.scoreProvider = 'naive'
def newOpen(self, node): def newOpen(self, node):
pass pass
@ -45,18 +48,36 @@ class Universe():
def activateEdge(self, head): def activateEdge(self, head):
pass pass
from dataclasses import dataclass, field
from typing import Any
@dataclass(order=True)
class PQItem:
priority: int
data: Any=field(compare=False)
class QueueingUniverse(Universe): class QueueingUniverse(Universe):
def __init__(self): def __init__(self):
self.pq = [] super().__init__()
self.pq = PriorityQueue()
def newOpen(self, node): def newOpen(self, node):
heapq.headpush(self.pq, (node.priority, node)) item = PQItem(node.getPriority(), node)
self.pq.put(item)
def merge(self, node):
self.newOpen(node)
return node
def clearPQ(self): def clearPQ(self):
self.pq = [] self.pq = PriorityQueue()
def iter(self): def iter(self):
yield heapq.heappop(self.pq) while True:
try:
yield self.pq.get(False).data
except Empty:
time.sleep(1)
def activateEdge(self, head): def activateEdge(self, head):
head._activateEdge() head._activateEdge()
@ -90,7 +111,7 @@ class State(ABC):
return choose('What does player '+str(self.curPlayer)+' want to do?', 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):
# Used for ordering the priority queue # Used for ordering the priority queue
# Priority should not change for the same root # Priority should not change for the same root
# Lower prioritys get worked on first # Lower prioritys get worked on first
@ -138,6 +159,7 @@ class Node():
def __init__(self, state, universe=None, parent=None, lastAction=None): def __init__(self, state, universe=None, parent=None, lastAction=None):
self.state = state self.state = state
if universe==None: if universe==None:
print('[!] No Universe defined. Spawning one...')
universe = Universe() universe = Universe()
self.universe = universe self.universe = universe
self.parent = parent self.parent = parent
@ -216,12 +238,18 @@ class Node():
else: else:
self.strongDecay() self.strongDecay()
def decayEvent(self):
for c in self.childs:
c.strongDecay()
def strongDecay(self): def strongDecay(self):
if self._strongs == [None]*self.playersNum: if self._strongs == [None]*self.playersNum:
if not self.scoresAvaible(): if not self.scoresAvaible():
self._calcScores() self._calcScores()
self._strongs = self._scores self._strongs = self._scores
if self.parent:
return self.parent._pullStrong() return self.parent._pullStrong()
return 1
return None return None
def getSelfScore(self): def getSelfScore(self):
@ -247,16 +275,21 @@ class Node():
return False return False
return True return True
def askUserForAction(self):
return self.state.askUserForAction(self.avaibleActions)
def _calcScores(self): def _calcScores(self):
for p in range(self.state.playersNum): for p in range(self.state.playersNum):
self._calcScore(p) self._calcScore(p)
def _calcScore(self, player): def _calcScore(self, player):
if self.universe.scoreProvider == 'naive':
self._scores[player] = self.state.getScoreFor(player) self._scores[player] = self.state.getScoreFor(player)
else:
raise Exception('Uknown Score-Provider')
@property def getPriority(self):
def priority(self): return self.state.getPriority(self.getSelfScore(), self._cascadeMemory)
return self.state.getPriority(self.score)
@property @property
def playersNum(self): def playersNum(self):
@ -273,11 +306,15 @@ class Node():
def curPlayer(self): def curPlayer(self):
return self.state.curPlayer return self.state.curPlayer
def getWinner(self):
return self.state.checkWin()
def _activateEdge(self): def _activateEdge(self):
if not self.strongScoresAvaible(): if not self.strongScoresAvaible():
self.universe.newOpen(self) self.universe.newOpen(self)
else: else:
for c in self.childs: for c in self.childs:
if c._cascadeMemory > 0.0001:
c._activateEdge() c._activateEdge()
def __str__(self): def __str__(self):
@ -312,10 +349,56 @@ def choose(txt, options):
return opt return opt
print('[!] Invalid Input.') print('[!] Invalid Input.')
class Worker():
def __init__(self, universe):
self.universe = universe
self._alive = True
def run(self):
import threading
self.thread = threading.Thread(target=self.runLocal)
self.thread.start()
def runLocal(self):
for i, node in enumerate(self.universe.iter()):
if not self._alive:
return
node.decayEvent()
def kill(self):
self._alive = False
self.thread.join()
def revive(self):
self._alive = True
class Trainer():
def __init__(self):
pass
def spawnRuntime(self, initState):
self._runtime = Runtime(initState)
def setRuntime(self, runtime):
self._runtime = runtime
def playFrom(self, start=None):
if start==None:
start = self._runtime.head
self._runtime.game([1]*self._runtime.head.playersNum)
class Runtime(): class Runtime():
def __init__(self, initState): def __init__(self, initState):
universe = Universe() universe = QueueingUniverse()
self.head = Node(initState, universe = universe) self.head = Node(initState, universe = universe)
universe.newOpen(self.head)
def spawnWorker(self):
self.worker = Worker(self.head.universe)
self.worker.run()
def killWorker(self):
self.worker.kill()
def performAction(self, action): def performAction(self, action):
for c in self.head.childs: for c in self.head.childs:
@ -330,10 +413,13 @@ class Runtime():
def turn(self, bot=None, calcDepth=7): def turn(self, bot=None, calcDepth=7):
print(str(self.head)) print(str(self.head))
if bot==None: if bot==None:
c = choose('Select action?', ['human', 'bot', 'undo']) c = choose('Select action?', ['human', 'bot', 'undo', 'qlen'])
if c=='undo': if c=='undo':
self.head = self.head.parent self.head = self.head.parent
return return
elif c=='qlen':
print(self.head.universe.pq.qsize())
return
bot = c=='bot' bot = c=='bot'
if bot: if bot:
self.head.forceStrong(calcDepth) self.head.forceStrong(calcDepth)
@ -348,11 +434,14 @@ 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 = self.head.askUserForAction(self.head.avaibleActions) action = self.head.askUserForAction()
self.performAction(action) self.performAction(action)
def game(self, bots=None, calcDepth=7): def game(self, bots=None, calcDepth=7):
self.spawnWorker()
if bots==None: if bots==None:
bots = [None]*self.head.playersNum bots = [None]*self.head.playersNum
while True: while self.head.getWinner()==None:
self.turn(bots[self.head.curPlayer], calcDepth) self.turn(bots[self.head.curPlayer], calcDepth)
print(self.head.getWinner() + ' won!')
self.killWorker()