From fdd34152093bed78892daf5ded998c9df155d5b8 Mon Sep 17 00:00:00 2001 From: Guoliang Cao Date: Sun, 5 Apr 2020 12:24:40 -0400 Subject: [PATCH 1/3] Daoqi support in python (WIP) --- python/board.py | 101 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/python/board.py b/python/board.py index ca561a90d..0d606a2dd 100644 --- a/python/board.py +++ b/python/board.py @@ -81,6 +81,107 @@ def loc_x(self,loc): def loc_y(self,loc): return (loc // self.dy)-1 + def update_adj(self, loc): + x = self.loc_x(loc) + y = self.loc_y(loc) + size_1 = self.size - 1 + + if x == 0: + self.adj[1] = size_1 + self.adj[2] = 1 + elif x == size_1: + self.adj[1] = -1 + self.adj[2] = -size_1 + else: + self.adj[1] = -1 + self.adj[2] = 1 + + if y == 0: + self.adj[0] = self.dy * size_1 + self.adj[3] = self.dy + elif y == size_1: + self.adj[0] = -self.dy + self.adj[3] = -self.dy * size_1 + else: + self.adj[0] = -self.dy + self.adj[3] = self.dy + + return self.adj + + def update_diag(self, loc): + x = self.loc_x(loc) + y = self.loc_y(loc) + size_1 = self.size - 1 + + if x == 0: + if y == 0: + self.diag = [ + size_1 * self.dy + size_1, + size_1 * self.dy + 1, + self.dy + size_1, + self.dy + 1, + ] + elif y == size_1: + self.diag = [ + size_1 * self.dy - 1, + size_1 * self.dy - size_1, + self.dy - 1, + self.dy - size_1, + ] + else: + self.diag = [ + size_1 * self.dy - 1, + size_1 * self.dy + 1, + self.dy - 1, + self.dy + 1, + ] + elif x == size_1: + if y == 0: + self.diag = [ + -self.dy + size_1, + -self.dy + 1, + -size_1 * self.dy + size_1, + -size_1 * self.dy + 1, + ] + elif y == size_1: + self.diag = [ + -self.dy - 1, + -self.dy - size_1, + -size_1 * self.dy - 1, + -size_1 * self.dy - size_1, + ] + else: + self.diag = [ + -self.dy - 1, + -self.dy + 1, + -size_1 * self.dy - 1, + -size_1 * self.dy + 1, + ] + else: + if y == 0: + self.diag = [ + -self.dy + size_1, + -self.dy + 1, + self.dy + size_1, + self.dy + 1, + ] + elif y == size_1: + self.diag = [ + -self.dy - 1, + -self.dy - size_1, + self.dy - 1, + self.dy - size_1, + ] + else: + self.diag = [ + -self.dy - 1, + -self.dy + 1, + self.dy - 1, + self.dy + 1 + ] + + return self.diag + def is_adjacent(self,loc1,loc2): return loc1 == loc2 + self.adj[0] or loc1 == loc2 + self.adj[1] or loc1 == loc2 + self.adj[2] or loc1 == loc2 + self.adj[3] From 9f1a9bf7e1aa47dce87efaa43d8920e9861bcc5a Mon Sep 17 00:00:00 2001 From: Guoliang Cao Date: Sun, 5 Apr 2020 12:51:19 -0400 Subject: [PATCH 2/3] Daoqi support in python (WIP) --- python/board.py | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/python/board.py b/python/board.py index 0d606a2dd..0956a7e87 100644 --- a/python/board.py +++ b/python/board.py @@ -183,6 +183,7 @@ def update_diag(self, loc): return self.diag def is_adjacent(self,loc1,loc2): + self.update_adj(loc2) return loc1 == loc2 + self.adj[0] or loc1 == loc2 + self.adj[1] or loc1 == loc2 + self.adj[2] or loc1 == loc2 + self.adj[3] def pos_zobrist(self): @@ -196,6 +197,7 @@ def num_liberties(self,loc): return self.group_liberty_count[self.group_head[loc]] def is_simple_eye(self,pla,loc): + self.update_adj(loc) adj0 = loc + self.adj[0] adj1 = loc + self.adj[1] adj2 = loc + self.adj[2] @@ -209,6 +211,7 @@ def is_simple_eye(self,pla,loc): opp = Board.get_opp(pla) opp_corners = 0 + self.update_diag(loc) diag0 = loc + self.diag[0] diag1 = loc + self.diag[1] diag2 = loc + self.diag[2] @@ -255,6 +258,7 @@ def would_be_legal(self,pla,loc): return True def would_be_suicide(self,pla,loc): + self.update_adj(loc) adj0 = loc + self.adj[0] adj1 = loc + self.adj[1] adj2 = loc + self.adj[2] @@ -277,6 +281,7 @@ def would_be_suicide(self,pla,loc): return True def would_be_single_stone_suicide(self,pla,loc): + self.update_adj(loc) adj0 = loc + self.adj[0] adj1 = loc + self.adj[1] adj2 = loc + self.adj[2] @@ -304,6 +309,8 @@ def get_liberties_after_play(self,pla,loc,maxLibs): libs = [] capturedGroupHeads = [] + self.update_adj(loc) + #First, count immediate liberties and groups that would be captured for i in range(4): adj = loc + self.adj[i] @@ -331,6 +338,7 @@ def wouldBeEmpty(possibleLib): #Next, walk through all stones of all surrounding groups we would connect with and count liberties, avoiding overlap. connectingGroupHeads = [] for i in range(4): + self.update_adj(loc) adj = loc + self.adj[i] if self.board[adj] == pla: head = self.group_head[adj] @@ -339,6 +347,7 @@ def wouldBeEmpty(possibleLib): cur = adj while True: + self.update_adj(cur) for k in range(4): possibleLib = cur + self.adj[k] if possibleLib != loc and wouldBeEmpty(possibleLib) and possibleLib not in libs: @@ -439,6 +448,7 @@ def playRecordedUnsafe(self,pla,loc): capDirs = [] opp = Board.get_opp(pla) old_simple_ko_point = self.simple_ko_point + self.update_adj(loc) for i in range(4): adj = loc + self.adj[i] if self.board[adj] == opp and self.group_liberty_count[self.group_head[adj]] == 1: @@ -464,6 +474,7 @@ def undo(self,record): #Re-fill stones in all captured directions for capdir in capDirs: + self.update_adj(loc) adj = loc + self.adj[capdir] if self.board[adj] == Board.EMPTY: self.floodFillStones(opp,adj) @@ -496,6 +507,7 @@ def undo(self,record): #Rebuild each chain adjacent now for i in range(4): + self.update_adj(loc) adj = loc + self.adj[i] if self.board[adj] == pla and self.group_head[adj] == Board.PASS_LOC: self.rebuildChain(pla,adj) @@ -538,6 +550,7 @@ def floodFillStonesHelper(self, head, tailTarget, loc, pla): #Recursively add stones around us. nextTailTarget = loc for i in range(4): + self.update_adj(loc) adj = loc + self.adj[i] if self.board[adj] == Board.EMPTY: nextTailTarget = self.floodFillStonesHelper(head,nextTailTarget,adj,pla) @@ -566,7 +579,9 @@ def rebuildChain(self,pla,loc): #some invalid location, such as NULL_LOC or a location not of color. def rebuildChainHelper(self, head, tailTarget, loc, pla): #Count new liberties - for dloc in self.adj: + for i in range(4): + self.update_adj(loc) + dloc = self.adj[i] if self.board[loc+dloc] == Board.EMPTY and not self.is_group_adjacent(head,loc+dloc): self.group_liberty_count[head] += 1 @@ -579,6 +594,7 @@ def rebuildChainHelper(self, head, tailTarget, loc, pla): #Recursively add stones around us. nextTailTarget = loc for i in range(4): + self.update_adj(loc) adj = loc + self.adj[i] if self.board[adj] == pla and self.group_head[adj] != head: nextTailTarget = self.rebuildChainHelper(head,nextTailTarget,adj,pla) @@ -597,6 +613,7 @@ def add_unsafe(self,pla,loc): self.group_head[loc] = loc self.group_stone_count[loc] = 1 liberties = 0 + self.update_adj(loc) for dloc in self.adj: if self.board[loc+dloc] == Board.EMPTY: liberties += 1 @@ -669,6 +686,7 @@ def add_unsafe(self,pla,loc): #Apply the specified delta to the liberties of all adjacent groups of the specified color def changeSurroundingLiberties(self,loc,pla,delta): + self.update_adj(loc) #Carefully avoid doublecounting adj0 = loc + self.adj[0] adj1 = loc + self.adj[1] @@ -690,6 +708,7 @@ def changeSurroundingLiberties(self,loc,pla,delta): self.group_liberty_count[self.group_head[adj3]] += delta def countImmediateLiberties(self,loc): + self.update_adj(loc) adj0 = loc + self.adj[0] adj1 = loc + self.adj[1] adj2 = loc + self.adj[2] @@ -706,6 +725,7 @@ def countImmediateLiberties(self,loc): return count def is_group_adjacent(self,head,loc): + self.update_adj(loc) return ( self.group_head[loc+self.adj[0]] == head or \ self.group_head[loc+self.adj[1]] == head or \ @@ -732,6 +752,7 @@ def merge_unsafe(self,loc0,loc1): new_liberties = self.group_liberty_count[phead] loc = child while True: + self.update_adj(loc) adj0 = loc + self.adj[0] adj1 = loc + self.adj[1] adj2 = loc + self.adj[2] @@ -782,6 +803,7 @@ def remove_unsafe(self,group): loc = group while True: #Add a liberty to all surrounding opposing groups, taking care to avoid double counting + self.update_adj(loc) adj0 = loc + self.adj[0] adj1 = loc + self.adj[1] adj2 = loc + self.adj[2] @@ -845,6 +867,7 @@ def remove_single_stone_unsafe(self,rloc): def findLiberties(self, loc, buf): cur = loc while True: + self.update_adj(loc) for i in range(4): lib = cur + self.adj[i] if self.board[lib] == Board.EMPTY: @@ -887,6 +910,7 @@ def hasLibertyGainingCaptures(self, loc): cur = loc while True: + self.update_adj(loc) for i in range(4): adj = cur + self.adj[i] if self.board[adj] == opp: @@ -906,6 +930,7 @@ def wouldBeKoCapture(self, loc, pla): #Check that surounding points are are all opponent owned and exactly one of them is capturable opp = Board.get_opp(pla) oppCapturableLoc = None + self.update_adj(loc) for i in range(4): adj = loc + self.adj[i] if self.board[adj] != Board.WALL and self.board[adj] != opp: @@ -924,6 +949,7 @@ def wouldBeKoCapture(self, loc, pla): return True def countHeuristicConnectionLiberties(self,loc,pla): + self.update_adj(loc) adj0 = loc + self.adj[0] adj1 = loc + self.adj[1] adj2 = loc + self.adj[2] @@ -1257,6 +1283,7 @@ def calculateAreaForPla(self, pla, safeBigTerritories, unsafeBigTerritories, isM containsOpp = [False for i in range(maxRegions)] def isAdjacentToPlaHead(loc,plaHead): + self.update_adj(loc) for i in range(4): adj = loc + self.adj[i] if self.board[adj] == pla and self.group_head[adj] == plaHead: @@ -1274,6 +1301,8 @@ def buildRegion(head, tailTarget, loc, regionIdx): return tailTarget regionHeadByLoc[loc] = head + self.update_adj(loc) + #First, filter out any pla heads it turns out we're not vital for because we're not adjacent to them #In the case where suicide is allowed, we only do this filtering on intersections that are actually empty if isMultiStoneSuicideLegal or self.board[loc] == Board.EMPTY: @@ -1304,6 +1333,7 @@ def buildRegion(head, tailTarget, loc, regionIdx): nextEmptyOrOpp[loc] = tailTarget nextTailTarget = loc for i in range(4): + self.update_adj(loc) adj = loc + self.adj[i] if self.board[adj] == Board.EMPTY or self.board[adj] == opp: nextTailTarget = buildRegion(head,nextTailTarget,adj,regionIdx) @@ -1337,6 +1367,7 @@ def buildRegion(head, tailTarget, loc, regionIdx): vStart = vitalStart[regionIdx] assert(vStart + 4 <= vitalForPlaHeadsListsMaxLen) initialVLen = 0 + self.update_adj(loc) for i in range(4): adj = loc + self.adj[i] if self.board[adj] == pla: @@ -1409,6 +1440,7 @@ def buildRegion(head, tailTarget, loc, regionIdx): #Walk the pla chain to update bordering regions cur = plaHead while(True): + self.update_adj(loc) for j in range(4): adj = cur + self.adj[j] if self.board[adj] == Board.EMPTY or self.board[adj] == opp: @@ -1466,6 +1498,11 @@ def calculateNonDameTouchingAreaHelper(self, basicArea, result): for y in range(self.size): for x in range(self.size): loc = self.loc(x,y) + self.update_adj(loc) + ADJ0 = self.adj[0] + ADJ1 = self.adj[1] + ADJ2 = self.adj[2] + ADJ3 = self.adj[3] if basicArea[loc] != Board.EMPTY and not isDameTouching[loc]: #Touches dame? if((self.board[loc+ADJ0] == Board.EMPTY and basicArea[loc+ADJ0] == Board.EMPTY) or @@ -1508,6 +1545,8 @@ def calculateNonDameTouchingAreaHelper(self, basicArea, result): nextLoc = queue[queueHead] queueHead += 1 + self.update_adj(nextLoc) + #Look all around it, floodfill for j in range(4): adj = nextLoc + self.adj[j] From 58b6535f13193998e61f3980581f4cf643111d93 Mon Sep 17 00:00:00 2001 From: Guoliang Cao Date: Sun, 26 Apr 2020 11:30:01 -0400 Subject: [PATCH 3/3] VS Code launch configurations --- .vscode/launch.json | 53 +++++++++++++++++++++++++++++++++++++++++++++ .vscode/tasks.json | 12 ++++++++++ 2 files changed, 65 insertions(+) create mode 100644 .vscode/launch.json create mode 100644 .vscode/tasks.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..3e5fa3fee --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,53 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python: shuffle.py", + "type": "python", + "request": "launch", + "preLaunchTask": "cleanup debug directory", + "cwd": "${workspaceRoot}", + "program": "${workspaceRoot}/python/shuffle.py", + "args": [ + "shared/selfplay/*/tdata/", + "-min-rows", "1", + "-max-rows", "1000000000", + "-expand-window-per-row", "0.4", + "-taper-window-exponent", "0.675", + "-out-dir", "shared/shuffleddata/debug/train", + "-out-tmp-dir", "shared/tmp/train", + "-approx-rows-per-out-file", "70000", + "-num-processes", "4", + "-batch-size", "256", + "-keep-target-rows", "1200000", + ], + "console": "integratedTerminal", + }, + { + "name": "Python: train.py", + "type": "python", + "request": "launch", + "cwd": "${workspaceRoot}", + "program": "${workspaceRoot}/python/train.py", + "args": [ + "-traindir", "shared/train/TEST", + "-datadir", "shared/shuffleddata/current/", + "-exportdir", "shared/tfsavedmodels_toexport", + "-exportprefix", "TEST", + "-pos-len", "19", + "-batch-size", "256", + "-max-epochs-this-instance", "3", + "-samples-per-epoch", "10000", + "-gpu-memory-frac", "0.6", + "-model-kind", "b6c96", + "-sub-epochs", "4", + "-swa-sub-epoch-scale", "4", + "-lr-scale", "1.0", + ], + "console": "integratedTerminal", + }, + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 000000000..a16a3fe09 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,12 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "cleanup debug directory", + "type": "shell", + "command": "rm -rf shared/shuffleddata/debug; mkdir -p shared/shuffleddata/debug; mkdir -p shared/tmp/train" + } + ] +} \ No newline at end of file