Gustav Johansson

Relevanta dokument
Lektion 2: Sökagenter. Robin Keskisärkkä

Föreläsning 2 Programmeringsteknik DD1310. Programmering. Programspråk

Python. Python är, som Scheme, ett interpreterat språk men det finns kompilatorer för Python.

Nu lär vi oss tre i rad

Python. Python är, som Scheme, ett interpreterat språk men det finns kompilatorer för Python.

Grundläggande datalogi - Övning 3

Föreläsning 10 Datalogi 1 DA2001. Utskrift på skärmen. Syntax. print( Hej ) Hur är det? Hej. print( Hej,end= ) print( Hur är det? ) HejHur är det?

Laboration: Whitebox- och blackboxtesting

Föreläsning 2 Programmeringsteknik och C DD1316. Programmering. Programspråk

Grundläggande datalogi - Övning 2

Föreläsning 2 Programmeringsteknik DD1310. Programmering. Programspråk

Föreläsning 2 Programmeringsteknik och C DD1316. Mikael Djurfeldt

Föreläsning 13 och 14: Binära träd

Föreläsning 2 Programmeringsteknik och Matlab DD1312. Programspråk. Utskrift på skärmen

Python. Vi har ofta behov av att behandla datastrukturer på ett enhetligt sätt så att vi kan göra samma sak i flera olika program.

Artificiell intelligens En agent som spelar Black Jack Andreas Perjons [andpe813]

Grundläggande datalogi - Övning 4

Alla datorprogram har en sak gemensam; alla processerar indata för att producera något slags resultat, utdata.

Föreläsning 2 Programmeringsteknik och Matlab DD1312. Programspråk. Utskrift på skärmen

Introduktion till programmering SMD180. Föreläsning 2: Variabler, uttryck och satser

Kan ett datorprogram spela solitär?

DD1310/DD1314/DA3009 Programmeringsteknik LÄRANDEMÅL... Vilka läser kursen? ...FLER LÄRANDEMÅL. Föreläsning 1

1/15/2013. DD1310/DD1314/DA3009 Programmeringsteknik. Lärandemål... Vilka läser kursen? ...fler lärandemål VARFÖR? Föreläsning 1

Sätt att skriva ut binärträd

DD1314 Programmeringsteknik

Tentamen i Introduktion till programmering

Genetisk programmering i Othello

Inom datalogin brukar man använda träd för att beskriva vissa typer av problem. Om man begränsar sig till träd där varje nod förgrenar sig högst två

Pythons standardbibliotek

Selektion och iteration

Programmering I Tobias Wrigstad fredag, 2009 augusti 28

Kurslitteraturen. C-nivå Villkorssatser [kap 8] if & elif & else and & or Loopar [kap 9] for

AI för Hive. Kungliga Tekniska Högskolan. Examensarbete inom datalogi, grundnivå (DD143X) Författare: Urban Pettersson

Mer grafik. Jan Erik Moström

Introduktion till programmering D0009E. Föreläsning 5: Fruktbara funktioner

AGA-regler. goforbundet.se/ Referens: AGA 1991

Datalogi, grundkurs 1

Handbok Othello. Clay Pradarits Utvecklare: Mario Weilguni Granskare: Lauri Watts Översättare: Stefan Asserhäll

Föreläsning 4: Kombinatorisk sökning

Tenta i Grundläggande programmering DD klockan

Ordlistor, filhantering och ut på webben. Linda Mannila

Introduktion till programmering SMD180. Föreläsning 5: Fruktbara funktioner

Övning 1 - Abstrakta datatyper

729G04 Programmering och diskret matematik. Python 2: Villkorssatser, sanningsvärden och logiska operatorer

Scratch Junior. makeandshape.com. by MIT. Gränssnitt Scratch Junior

Handbok Kigo. Sascha Peilicke Översättare: Stefan Asserhäll

Skillnader mellan Python och Java

Handbok Othello. Clay Pradarits Utvecklare: Mario Weilguni Granskare: Lauri Watts Översättare: Stefan Asserhäll

Tentamen FYTA11 Javaprogrammering

Föreläsning 8 Programmeringsteknik och Matlab DD1312. Klassmetod. Egen modul

Länkade listor, stackar och köer

Trädsökning och datorschack

Fredag 10 juni 2016 kl 8 12

F11 - Rekursion. ID1004 Objektorienterad programmering Fredrik Kilander

Föreläsning 8 Datastrukturer (DAT037)

Grundläggande datalogi - Övning 1

Grafer, traversering. Koffman & Wolfgang kapitel 10, avsnitt 4

Övning 1. Abstrakta datatyper. 1. Stacken. class Stack: """A representation of a last-in-first-out (LIFO) stack of objects."""

Programmering grundkurs

Tommy Färnqvist, IDA, Linköpings universitet

Kodexempel från Programmering 2 Tobias Wrigstad, Ph.D.

Övning 3 - Tillämpad datalogi 2012

Fyra i rad Javaprojekt inom TDDC32

Programmeringsuppgift Game of Life

Dugga Datastrukturer (DAT036)

Lösningsförslag till tentamen i EDA011, lördagen den 16 december 2006

Filer. DA2001 (Föreläsning 16) Datalogi 1 Hösten / 19

Några saker till och lite om snabbare sortering

ETS052 Internet Routing. Jens A Andersson

Föreläsning 7 Datastrukturer (DAT037)

Introduktion till programmering SMD180. Föreläsning 8: Listor

Uppgifter teknik HT17 (uppdaterad v. 40)

TDDC30 Programmering i Java, Datastrukturer och Algoritmer Lektion 2. Laboration 2 Datastrukturer En liten uppgift Frågor

if (n==null) { return null; } else { return new Node(n.data, copy(n.next));

Övning 6. Ali Tofigh 24 Oktober, 2006

TDDC30 Programmering i Java, Datastrukturer och Algoritmer Lektion 2. Länkade listor Stackar Köer MyList Iteratorer Lab 2 Exceptions Paket

Tenta (TEN3) i kursen 729G04 Programmering och diskret matematik 5 feb 2016, kl 14:00-18:00

public class Main extends MovieClip { var hillpage:hillpage; var ifpage:ifpage;

TENTAMEN PROGRAMMERING I JAVA, 5P SOMMARUNIVERSITETET

732G Linköpings universitet 732G11. Johan Jernlås. Översikt. Repetition. Exempelduggan. Luffarschack. Koda spel

Några inbyggda funktioner (med resultat!) Introduktion till programmering D0009E. Föreläsning 4: Villkor och rekursion. Modulus-operatorn.

Föreläsning Datastrukturer (DAT036)

Lösningsförslag. 1 Lösningsförslag. Uppgift 1

Lösningsförslag till tentamen

Föreläsning Datastrukturer (DAT037)

EnKlass. Instans 3 av EnKlass. Instans 2 av EnKlass

Sökning. Sökning. Köoperationer. Generell sökalgoritm

Algoritmer och datastrukturer TDA Fredrik Johansson

PYTHON SNABBREPETITION MEN FÖRST LITE ALLMÄNT OM PROGRAMMERING 729G04 PYTHON 2. Dagens Python

Namn Stil Dokumentation Objekt-orienterad programmering OBS OBS OBS OBS OBS OBS OBS OBS

Föreläsning Datastrukturer (DAT036)

Idag: Centrerad utskrift. Granskning. DD1311 Programmeringsteknik med PBL. Granskning Felhantering GUI. Föreläsning 15.

Grundläggande programmering med C# 7,5 högskolepoäng

Referenshanteringsprogrammet

732G Linköpings universitet 732G11. Johan Jernlås. Översikt. Repetition. Muddy. Funktioner / metoder. Punktnotation. Evalueringsordning

Lösning till tentamen EDAA45 Programmering, grundkurs

Övning 2. (Länkade) Listor, noder

Tentamen i Objektorienterad programmering

Föreläsning 2 Programmeringsteknik och C DD1316

729G04 Programmering och diskret matematik. Python 3: Loopar

Transkript:

LINKOPINGS UNIVERSITET, IDA Liv eller död agent Artificiell intelligens på liv eller död Gustav Johansson 2039-09-16

Innehållsförteckning Go - introduktion... 1 Liv eller död... 2 Liv eller död agent... 4 Representation... 4 Problemidentifikation... 4 Utvärdering... 4 Sökning... 5 Utvärdering och resultat... 5 Referenser... 7

Go - introduktion Spelet Go är ett brädspel som spelas mellan två spelare vilka omväxlande spelar svarta eller vita stenar på spelbrädets lediga platser. Spelets bräde består av ett rutnät med 19x19 linjer, spelets pjäser spelas på skärningspunkterna mellan dessa linjer. Om en sten placeras på brädet så säger man att antalet friheter som stenen har utgörs av alla angränsade tomma skärningspunkter, se figur 1. Figur 1 Figur 2 Figur 3 I figur 1 så har den svarta stenen fyra friheter vilka är markerade med blå kryss, i figur 2, en frihet och i figur 3 så har den vita spelaren fångat den svarta stenen då den inte längre har några friheter alls. Vidare så kan antalet friheter för en sten utökas om den kopplas ihop med en annan sten av samma färg, dessa kallas då för en grupp av stenar, exempelvis så har gruppen i figur 4 sex friheter. Figur 4 Spelets mål är att avgränsa mer territorium än sin motspelare. Spelet avslutas när båda spelarna anser att det inte finns några meningsfulla drag kvar att göra. Under vägen så genomgås tre övergripande perioder. Öppningsspelet, mellanspelet samt slutspelet. Alla dessa faser av spelet är intressanta och har problem som är intressanta för AI. Ett specifikt problem som ofta uppstår under mellanspelet är s.k. liv eller död situationer. 1

Liv eller död I liv eller död situationer så utvärderas en eller flera gruppers status som levande, död eller osäker. När man bedömer gruppers levnadsstatus pratar man ofta om ögon. Ett öga är ett område som är inringat av en grupp. Formen och storleken på ett öga samt antalet ögon som en grupp har är alla avgörande faktorer för liv eller död utvärdering. Den vita gruppen i figur 5 har ett öga, men ögon kan vara formade på många olika sätt, det viktiga är att det är helt eller potentiellt helt omringade områden. Figur 5 Den vita gruppen har ett öga som är den markerade positionen En grupp är levande om den alltid har minst en frihet kvar oavsett vad motspelaren gör, med reservation för att spelaren som gruppen tillhör inte saboterar den själv. Ett exempel visas i figur 6 Figur 6 Den vita gruppen ovan är levande Den vita gruppen har alltid minst en frihet kvar oavsett var svart spelar. Skulle svart välja att placera en sten i något av de markerade områdena skulle den stenen omedelbart ha noll friheter och därför vara död. 2

En grupp sägs vara död om spelare som gruppen tillhör inte kan skapa två ögon för gruppen oavsett vad den gör. Se figur 7 för exempel Figur 7 En död vit grupp Den vita gruppen ovan är död, för att den endast har ett område som kan klassas som ett öga och kan inte skapa fler friheter på något sätt. En grupps status sägs vara osäker när den inte entydligt lever eller är död. Här handlar det om att levnadsstatus avgörs beroende på hur spelarna gör sina drag. Figur 8 Svarta gruppens status som levande eller död är osäker. I Figur 8 så är den viktiga rutan den som är markerad, om svart spelar på den positionen så är gruppen levande, men om vit spelar på den positionen så är den död. 3

Liv eller död agent För att en datoragent ska kunna lösa liv eller död problem så finns det ett antal problem som måste lösas. Representation Ett första problem är representation av brädet. I mitt fall så har jag funnit ett färdigt program som spelar Go. Detta programmet heter Disco och är skrivet av Mark Dufour. I filen go.py så återfinns klassern Board som innehåller all information om brädet och spelarnas drag. Brädets spelpositioner listas i Board som värden mellan 0 och 80 där varje siffra innehåller information om huruvida rutan är tom eller innehåller vit eller svart spelpjäs. Positionerna innehåller dessutom information om vilka positioner som är grannar. Utöver denna information så finns det även information om antal fångar som tagits och en del annan information. Problemidentifikation Nästa problem som uppstår är hur man ska identifiera grupper som är potentiella liv-eller-död problem. Detta är för att det är praktiskt omöjligt att utvärdera varenda position i hela brädet i något sökdjup som är intressant. Även med ett 9x9 bräde och ett sökdjup på 6 så tar det fruktansvärt lång tid att hitta lösningar. Genom att identifiera och avgränsa potentiella problemområden minskar man tidsoch minneskomplexiteten väldigt mycket. Den metod jag valt att använda är att först sätta ihop de grupper som tillhör varandra och sedan utvärdera dessa grupper mot fara. Vidare så har jag försökt att lokalisera sökrymden till kringliggande tomma områden. Än så länge klarar koden att identifiera problem som är på den mycket lätta skalan. Utvärdering Vidare problem är utvärdering av resultat. Jag har valt att skapa en utvärdering som funkar för lokala problem av typen liv-eller-död. B = P + C x2 C x2 Där B = utvärdering för svart, P = antal svarta stenar spelade, C = antal vita fångade stenar och C = antal stenar fångade av svart. Nu är utvärderingsfunktionen ett av de största problemen som Go programmerare ställs inför, men hyffsade uppskattningar, speciellt för sådana här specialiserade, lokala, situationer kan ofta uppskattas med en finurlig funktion. Den jag valt löser de korta, lokala problem som agenten i övrigt har förmågan att lösa. Mer komplexa problem kräver mer utförlig utvärderingsfunktion. 4

Sökning Slutligen så ska agenten lyckas söka igenom den förhoppningsvis avgränsade sökrymden med någon typ av sökmetod. Den metod jag valt att använda är minimax metod med alpha-beta cutoff med bestämt ett maxdjup som beror på förgreningsfaktorn. Förgreningsfaktorn bestäms av storleken på gruppen i fråga. Just nu så utvärderas exempelvis en förgreningsfaktor på 10 på ett sökdjup av 4. Utvärdering och resultat Just nu så klarar problemlösaren att lösa mycket enkla problem, men när problemen blir lite svårare så har koden mycket svårare att hitta rätt lösning. Mina problem ligger huvudsakligen i området problemidentifikation. Byuung-Doo Lee beskriver i sin avhandling[] en liv-eller-död agent som använder sig av mönster igenkänning samt analys av ögonformation i syfte att generera ett litet antal öppningsdrag. Den metoden visar på stor potential och bred generaliserbarhet. I min ursprungliga plan hade jag tänkt implementera en ögon-analys funktion men fastnade på andra problem. I min bedömning så är det för krångligt att skriva ett mönsterigenkänningssystem samt producera den mängd problem för den att träna sig på. Jag menar att det går att avgränsa sökrymden med mer klumpiga, närhets heuristiker för att begränsa sökrymden. Slutligen så går det alldeles för långsamt. Problemet i fig 12 tog ca 2 minuter att lösa. Det hade en förgreningsfaktor på 12 och ett sökdjup på 5. En simpel ögonanalys skulle reducera de initiala noderna till bara 3 och en mer effektiv alpha-beta cutoff skulle ytterligare reducera söktiden. Försvårar man problemet bara lite i stil med figur 13 så missar sökningen lösningen, om inte sökdjupet ytterligare expanderas. Figur 9 Svart att spela för att överleva, kortsiktigt. Blå krysset markerar rätt drag 5

Figur 10 Figur 11 Vit att spela och döda den svarta gruppen. Vit sten på den markerade positionen i figur X avgör. Figur 12 Markerade positionen avgör huruvida svarta gruppen överlever eller inte Figur 13 Svart har valet mellan att placera en sten i ena eller andra markerade positionen för att överleva Slutsatsen är att problemlösaren tyvärr inte är praktiskt användbar, men med lite mer arbete och så skulle den kunna vara åtminstone intressant. Det finns en stor mängd intressanta metoder som bara väntar på att bli implementerade. Mönsterigenkänning är inte den enda, det finns vidare intressanta algoritmer. 6

Referenser Bruno Bouzy, T. C. (2000). Computer Go: An AI oriented survey. Lee, B.-D. (2004). Life-and-death Problem Solver in Go. Auckland. 7

Bilaga 1: go.py kod # -*- coding: cp1252 -*- import random, math, sys SIZE = 9 GAMES = 1500 KOMI = 7.5 EMPTY, WHITE, BLACK = 0, 1, 2 SHOW = {EMPTY: '.', WHITE: 'o', BLACK: 'x'} PASS = -1 MAXMOVES = SIZE*SIZE*3 TIMESTAMP = 0 MOVES = 0 def to_pos(x,y): return y * SIZE + x def to_xy(pos): y, x = divmod(pos, SIZE) return x, y class Square: def init (self, board, pos): self.board = board self.pos = pos self.timestamp = TIMESTAMP self.removestamp = TIMESTAMP self.zobrist_strings = [random.randrange(sys.maxint) for i in range(3)] def set_neighbours(self): x, y = self.pos % SIZE, self.pos / SIZE; self.neighbours = [] for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]: newx, newy = x + dx, y + dy if 0 <= newx < SIZE and 0 <= newy < SIZE: self.neighbours.append(self.board.squares[to_pos(newx, newy)]) def move(self, color): global TIMESTAMP, MOVES TIMESTAMP += 1 MOVES += 1 self.board.zobrist.update(self, color) self.color = color self.reference = self self.ledges = 0 self.used = True for neighbour in self.neighbours: neighcolor = neighbour.color if neighcolor == EMPTY: self.ledges += 1 else: neighbour_ref = neighbour.find(update=true) if neighcolor == color: if neighbour_ref.reference.pos!= self.pos: self.ledges += neighbour_ref.ledges neighbour_ref.reference = self 8

self.ledges -= 1 else: neighbour_ref.ledges -= 1 if neighbour_ref.ledges == 0: neighbour.remove(neighbour_ref) self.board.zobrist.add() def remove(self, reference, update=true): self.board.zobrist.update(self, EMPTY) self.removestamp = TIMESTAMP if update: self.color = EMPTY self.board.emptyset.add(self.pos) if self.board.color == BLACK: self.board.white_dead += 1 else: self.board.black_dead += 1 for neighbour in self.neighbours: if neighbour.color!= EMPTY and neighbour.removestamp!= TIMESTAMP: neighbour_ref = neighbour.find(update) if neighbour_ref.pos == reference.pos: neighbour.remove(reference, update) else: if update: neighbour_ref.ledges += 1 def find(self, update=false): reference = self.reference if reference.pos!= self.pos: reference = reference.find(update) if update: self.reference = reference return reference def repr (self): return repr(to_xy(self.pos)) class EmptySet: def init (self, board): self.board = board self.empties = range(size*size) self.empty_pos = range(size*size) def random_choice(self): choices = len(self.empties) while choices: i = int(random.random()*choices) pos = self.empties[i] if self.board.useful(pos): return pos choices -= 1 self.set(i, self.empties[choices]) self.set(choices, pos) return PASS def add(self, pos): self.empty_pos[pos] = len(self.empties) self.empties.append(pos) def remove(self, pos): 9

self.set(self.empty_pos[pos], self.empties[len(self.empties)-1]) self.empties.pop() def set(self, i, pos): self.empties[i] = pos self.empty_pos[pos] = i class ZobristHash: def init (self, board): self.board = board self.hash_set = set() self.hash = 0 for square in self.board.squares: self.hash ^= square.zobrist_strings[empty] self.hash_set.clear() self.hash_set.add(self.hash) def update(self, square, color): self.hash ^= square.zobrist_strings[square.color] self.hash ^= square.zobrist_strings[color] def add(self): self.hash_set.add(self.hash) def dupe(self): return self.hash in self.hash_set class Board: def init (self): self.groups={} self.terminal = 10 self.squares = [Square(self, pos) for pos in range(size*size)] for square in self.squares: square.set_neighbours() self.reset() ######################################################################## ## def terminal(self): ## if self.score > 10: ## self.terminal = True ######################################################################## def reset(self): for square in self.squares: square.color = EMPTY square.used = False self.emptyset = EmptySet(self) self.zobrist = ZobristHash(self) self.color = BLACK self.finished = False self.lastmove = -2 self.history = [] self.white_dead = 0 self.black_dead = 0 def move(self, pos): square = self.squares[pos] 10

if pos!= PASS: square.move(self.color) self.emptyset.remove(square.pos) elif self.lastmove == PASS: self.finished = True if self.color == BLACK: self.color = WHITE else: self.color = BLACK self.lastmove = pos self.history.append(pos) def random_move(self): return self.emptyset.random_choice() def useful_fast(self, square): if not square.used: for neighbour in square.neighbours: if neighbour.color == EMPTY: return True return False def useful(self, pos): global TIMESTAMP TIMESTAMP += 1 square = self.squares[pos] if self.useful_fast(square): return True old_hash = self.zobrist.hash self.zobrist.update(square, self.color) empties = opps = weak_opps = neighs = weak_neighs = 0 for neighbour in square.neighbours: neighcolor = neighbour.color if neighcolor == EMPTY: empties += 1 continue neighbour_ref = neighbour.find() if neighbour_ref.timestamp!= TIMESTAMP: if neighcolor == self.color: neighs += 1 else: opps += 1 neighbour_ref.timestamp = TIMESTAMP neighbour_ref.temp_ledges = neighbour_ref.ledges neighbour_ref.temp_ledges -= 1 if neighbour_ref.temp_ledges == 0: if neighcolor == self.color: weak_neighs += 1 else: weak_opps += 1 neighbour_ref.remove(neighbour_ref, update=false) dupe = self.zobrist.dupe() self.zobrist.hash = old_hash strong_neighs = neighs-weak_neighs strong_opps = opps-weak_opps return not dupe and \ (empties or weak_opps or (strong_neighs and (strong_opps or weak_neighs))) def useful_moves(self): return [pos for pos in self.emptyset.empties if self.useful(pos)] def replay(self, history): 11

for pos in history: self.move(pos) def score(self, color): if color == WHITE: count = KOMI + self.black_dead *2 - self.white_dead * 2 else: count = self.white_dead * 2 - self.black_dead * 2 for square in self.squares: squarecolor = square.color if squarecolor == color: count += 1 elif squarecolor == EMPTY: surround = 0 for neighbour in square.neighbours: if neighbour.color == color: surround += 1 if surround == len(square.neighbours): count += 1 return count def check(self): for square in self.squares: if square.color == EMPTY: continue members1 = set([square]) changed = True while changed: changed = False for member in members1.copy(): for neighbour in member.neighbours: if neighbour.color == square.color and neighbour not in members1: changed = True members1.add(neighbour) ledges1 = 0 for member in members1: for neighbour in member.neighbours: if neighbour.color == EMPTY: ledges1 += 1 root = square.find() #print 'members1', square, root, members1 #print 'ledges1', square, ledges1 members2 = set() for square2 in self.squares: if square2.color!= EMPTY and square2.find() == root: members2.add(square2) ledges2 = root.ledges #print 'members2', square, root, members1 #print 'ledges2', square, ledges2 assert members1 == members2 assert ledges1 == ledges2, ('ledges differ at %r: %d %d' % (square, ledges1, ledges2)) empties1 = set(self.emptyset.empties) 12

empties2 = set() for square in self.squares: if square.color == EMPTY: empties2.add(square.pos) def repr (self): result = [] for y in range(size): start = to_pos(0, y) result.append(''.join([show[square.color]+' ' for square in self.squares[start:start+size]])) return '\n'.join(result) class UCTNode: def init (self): self.bestchild = None self.pos = -1 self.wins = 0 self.losses = 0 self.pos_child = [None for x in range(size*size)] self.parent = None def play(self, board): """ verkar generera en nod utifrn sig sjlv dr du r. Detta grs.. jag vet inte hru mnga gnger""" """ testa!! Tills child!= false.. vettefan.""" """ uct tree search """ color = board.color node = self path = [node] while True: pos = node.select(board) if pos == PASS: break board.move(pos) child = node.pos_child[pos] if not child: child = node.pos_child[pos] = UCTNode() child.unexplored = board.useful_moves() child.pos = pos child.parent = node path.append(child) break path.append(child) node = child self.random_playout(board) self.update_path(board, color, path) def select(self, board): """ select move; unexplored children first, then according to uct value """ if self.unexplored: i = random.randrange(len(self.unexplored)) pos = self.unexplored[i] self.unexplored[i] = self.unexplored[len(self.unexplored)-1] self.unexplored.pop() return pos elif self.bestchild: return self.bestchild.pos else: 13

return PASS def random_playout(self, board): """ random play until both players pass """ for x in range(maxmoves): # XXX while not self.finished? if board.finished: break board.move(board.random_move()) def update_path(self, board, color, path): """ update win/loss count along path """ wins = board.score(black) >= board.score(white) for node in path: if color == BLACK: color = WHITE else: color = BLACK if wins == (color == BLACK): node.wins += 1 else: node.losses += 1 if node.parent: node.parent.bestchild = node.parent.best_child() def score(self): winrate = self.wins/float(self.wins+self.losses) parentvisits = self.parent.wins+self.parent.losses if not parentvisits: return winrate nodevisits = self.wins+self.losses return winrate + math.sqrt((math.log(parentvisits))/(5*nodevisits)) def best_child(self): maxscore = -1 maxchild = None for child in self.pos_child: if child and child.score() > maxscore: maxchild = child maxscore = child.score() return maxchild def best_visited(self): maxvisits = -1 maxchild = None for child in self.pos_child: if child and (child.wins+child.losses) > maxvisits: maxvisits, maxchild = (child.wins+child.losses), child return maxchild def user_move(board): while True: text = raw_input('?').strip() if text == 'p': return PASS if text == 'q': raise EOFError try: x, y = [int(i) for i in text.split()] except ValueError: continue if not (0 <= x < SIZE and 0 <= y < SIZE): continue pos = to_pos(x, y) 14

if board.useful(pos): return pos def computer_move(board): global MOVES pos = board.random_move() if pos == PASS: return PASS tree = UCTNode() tree.unexplored = board.useful_moves() nboard = Board() for game in range(games): node = tree nboard.reset() nboard.replay(board.history) node.play(nboard) # print 'moves', MOVES return tree.best_visited().pos def calgroups(board): testcolors = {BLACK:[],WHITE:[]} alreadystored = [] for u in testcolors: for square in board.squares: isingroup = False if square.color == u: for x in square.neighbours: if x.color == u: isingroup = True break if isingroup == True: groupstored = False for i in testcolors[u]: for z in i: if square not in z: for p in z: if square in p.neighbours: z.append(square) groupstored = True if groupstored == False: testcolors[u].append([[square]]) board.groups = testcolors print testcolors def calliberties(board): blackies = board.groups[black] whities = board.groups[white] for x in whities: liberties =0 neighbouries=0 for y in x[0]: for u in y.neighbours: if u!= 0: neighbouries+=1 15

if u.color == EMPTY: liberties +=1 x.append(liberties) for x in blackies: neighbouries = 0 liberties = 0 for y in x[0]: for u in y.neighbours: if u!= 0: neighbouries+=1 if u.color == EMPTY: liberties +=1 x.append(liberties) board.groups[black] = blackies board.groups[white] = whities def checkfordanger(board): groups = board.groups returngrp = [] prio1 = False prio2 = False for u in groups: for x in groups[u]: if x[1] < 3: prio1 = True if len(x[0]) > len(returngrp): returngrp = x[0] elif x[1] < 4: if prio1 == False: returngrp = max(len(returngrp),len(x[0])) return returngrp def identifysearch(group,board): global RARGH searchspace = [] for x in group: for u in x.neighbours: if u not in searchspace and u.color == 0: searchspace.append(u) for i in u.neighbours: if i not in searchspace and i.color == 0: searchspace.append(i) for p in i.neighbours: if p not in searchspace and p.color == 0: searchspace.append(p) print searchspace testlist = [] tobeappended = [1,2,3,4,5,6,7,8,9] ## for x in group: ## for x in group: ## for u in tobeappended: ## if u+x.pos not in testlist and u+x.pos < 81: ## testlist.append(u+x.pos) ## if u-x.pos not in testlist and u-x.pos > -1: ## testlist.append(u-x.pos) searchspace2=[] for x in searchspace: searchspace2.append(x.pos) for x in searchspace2: 16

if x not in testlist: testlist.append(x) ## print searchspace2 for x in testlist: if board.squares[x].color == 2 or board.squares[x].color == 1: testlist.remove(x) for x in testlist: if board.squares[x].color == 2 or board.squares[x].color == 1: testlist.remove(x) for x in testlist: if board.squares[x].color == 2 or board.squares[x].color == 1: testlist.remove(x) testrange = testlist print testlist return testlist testrange = [] ######################################################################## EVALDIC = {} def alphabetatest(board,searchspace): if board.color == BLACK: player = BLACK opponent = WHITE else: player = WHITE opponent = BLACK print len(searchspace) if len(searchspace) > 9: depth = 5 elif len(searchspace) > 6: depth = 7 else: depth = 9 if depth > len(searchspace): depth = len(searchspace) depth = depth + len(board.history) depth = len(board.history) + 6 def maxivali(history,move,alpha,beta): tbored = Board() for x in history: tbored.move(x) tbored.move(move) value = -1000 if len(tbored.history)>depth: return tbored.score(player) for x in searchspace: if x not in tbored.history: value = max(value,minivali(tbored.history,x,alpha,beta)) if value >= beta: return value alpha = max(alpha,value) return value def minivali(history,move,alpha,beta): tbored = Board() 17

for x in history: tbored.move(x) tbored.move(move) value = 1000 if len(tbored.history)> depth: return tbored.score(player) for x in searchspace: if x not in tbored.history: value = min(value,maxivali(tbored.history,x,alpha,beta)) if value <= alpha: return value beta = min(beta,value) return value for x in searchspace: EVALDIC[x] = minivali(board.history,x,-10000,10000) val = -100 for u in EVALDIC: if EVALDIC[u] >val and EVALDIC[u]!= -1000 and EVALDIC[u]!=1000: action = u val = EVALDIC[u] return action ######################################################################## ##class NEWSEARCHBITCH(board): ##def minimaxie(board): ## player = board.color ## ## def max_val(board): ## # if board.terminal == True: ## # return utility in posit ## #v=-infinity ## board = Board() 18

def versus_cpu(): while True: if board.lastmove!= PASS: print board print 'thinking..' pos = computer_move(board) if pos == PASS: print 'I pass.' else: print 'I move here:', to_xy(pos) board.move(pos) break #board.check() if board.finished: break if board.lastmove!= PASS: print board pos = user_move(board) board.move(pos) #board.check() if board.finished: break print 'WHITE:', board.score(white) print 'BLACK:', board.score(black) ##if name == ' main ': ## random.seed(1) ## try: ## versus_cpu() ## except EOFError: ## pass ##board.move(to_pos(4,1)) ##board.move(to_pos(5,1)) ##board.move(to_pos(1,1)) ##board.move(to_pos(7,1)) ##board.move(to_pos(2,1)) ## ##board.move(to_pos(4,4)) ##board.move(to_pos(6,6)) ##board.move(to_pos(3,4)) ##board.move(to_pos(7,6)) ## ##board.move(to_pos(1,8)) ##board.move(to_pos(5,2)) ##board.move(to_pos(2,8)) ##board.move(to_pos(6,2)) ##board.move(to_pos(3,8)) ##board.move(to_pos(7,2)) ##board.move(to_pos(4,8)) ##board.move(to_pos(8,1)) ## Exempelproblem: 1-2 step problem ## ## ##board.move(to_pos(0,0)) 19

## ##board.move(to_pos(6,8)) ##board.move(to_pos(5,8)) ##board.move(to_pos(5,7)) ##board.move(to_pos(4,8)) ##board.move(to_pos(6,7)) ##board.move(to_pos(1,1)) ## Exempelproblem: 2-2 step problem. ##board.move(to_pos(0,0)) ##board.move(to_pos(6,8)) ##board.move(to_pos(5,8)) ##board.move(to_pos(5,7)) ##board.move(to_pos(4,8)) ##board.move(to_pos(4,7)) ##board.move(to_pos(2,7)) ##board.move(to_pos(1,1)) ## Exempelproblem 3 - kort problem ##board.move(to_pos(0,1)) ##board.move(to_pos(1,1)) ##board.move(to_pos(1,0)) ##board.move(to_pos(0,2)) ## Exempelproblem 4 - problem! ##board.move(to_pos(2,0)) ##board.move(to_pos(3,0)) ##board.move(to_pos(1,1)) ##board.move(to_pos(3,1)) ##board.move(to_pos(2,1)) ##board.move(to_pos(3,2)) ##board.move(to_pos(0,2)) ##board.move(to_pos(2,3)) ##board.move(to_pos(8,8)) ##board.move(to_pos(0,3)) ##board.move(to_pos(1,2)) ##board.move(to_pos(1,3)) ##board.move(to_pos(7,7)) ##board.move(to_pos(2,2)) ## Exempelproblem 5 - problem, sv 岡 re! board.move(to_pos(2,0)) board.move(to_pos(3,0)) board.move(to_pos(1,1)) board.move(to_pos(3,1)) board.move(to_pos(2,1)) board.move(to_pos(3,2)) board.move(to_pos(0,3)) board.move(to_pos(2,3)) board.move(to_pos(1,3)) board.move(to_pos(0,4)) board.move(to_pos(1,2)) 20

board.move(to_pos(1,4)) board.move(to_pos(8,8)) board.move(to_pos(2,2)) board.move(to_pos(8,1)) board.move(to_pos(2,4)) calgroups(board) calliberties(board) x = checkfordanger(board) u = identifysearch(x,board) alphabetatest(board,u) 21