the basics of creating a go bot

For discussing go computing, software announcements, etc.
phillip1882
Lives in gote
Posts: 323
Joined: Sat Jan 08, 2011 7:31 am
Rank: 6k
GD Posts: 25
OGS: phillip1882
Has thanked: 4 times
Been thanked: 39 times

the basics of creating a go bot

Post by phillip1882 »

i'm not sure if these algorithms are correct but they look like they are. i programmed them myself in python so hopefully they are right. please comment.

Code: Select all

board = ["+"]*361
black = 'B'
white = 'W'
empty = '+'
turn = "B"
ko = -1
def liberties(n, t, boardc, turn):
    #check the current position n. if empty, mark it and increase counter by 1
    if n == '+':
        boardc[n] = '*'
        return t+1
    #if stone, mark it and recursively check left right top and bottom.
    elif boardc[n] == turn:
        if turn == "B"
            boardc[n] = 'b'
        else:
            boardc[n] = 'w'
        if n%19 < 18:
            t = t +liberties(n+1, t, boardc)
        if n%19 > 0:
            t = t +liberties(n-1, t, boardc)
        if n/19 > 0:
            t = t +liberties(n-19, t, boardc)
        if n/19 < 18:
            t = t +liberties(n+19, t, boardc)
    else:
        return t

def group(n,t,boardc,turn):
    same as liberty except count stones instead of empty.
    if boardc[n] == turn:
        if turn == "B"
            boardc[n] = 'b'
        else:
            boardc[n] = 'w'
        t += 1
        if n%19 < 18:
            group(n+1, t, boardc,turn)
        if n%19 > 0:
            group(n-1, t, boardc,turn)
        if n/19 > 0:
            group(n-19, t, boardc,turn)
        if n/19 < 18:
            group(n+19, t, boardc,turn)
    else:
        return t

def next(turn):
    if turn == 'B':
        turn = 'W'
    else:
        turn = 'B'
    return turn

def is_legal(n,board,ko,turn):
    if n =="pass":
        return True
    if n == "resign"
        return True
    if n == ko:
        return False
    #if you capture, its legal, if not, check suicide
    boardc = board[:]
    turn = next(turn)
    if n%19 <18 and liberties(n+1,0,boardc,turn)==0:
        return True
    boardc = board[:]
    if n%19 >0 and liberties(n-1,0,boardc,turn)==0:
        return True
    boardc = board[:]
    if n/19 >0 and liberties(n-19,0,boardc,turn) ==0:
        return True
    boardc = board[:]
    if n/19 <18 and liberties(n+19,0,boardc,turn) ==0:
        return True
    boardc = board[:]
    turn = next(turn)
    if liberties(n,0,boardc,turn) ==0:
        return False
    else:
        return True

def capture(board):
    #clear captured stones denoted b or w, return total captured
    total = 0
    for i in range(0,len(board)):
        if board[i] != '+':
            if board[i] != "W" or board[i] != "B":
                board[i] = '+'
                total += 1
    return total

def play(n,board,ko,turn):
    capture stones, detect ko (single capture with a group of size 1).
    new_ko = -1
    caps = 0
    if is_legal(n,board,ko,turn):
        boardc = board[:]
        turn = next(turn)
        if n%19 <18 and liberties(n+1,0,boardc,turn)==0:
            liberties(n+1,0,board,turn)
            caps += capture(board,turn)
            if caps == 1:
                new_ko = n+1
        boardc = board[:]
        if n%19 >0 and liberties(n-1,0,boardc,turn)==0:
            liberties(n+1,0,board,turn)
            caps += capture(board,turn)
            if caps == 1:
                new_ko = n-1
        boardc = board[:]
        if n/19 >0 and liberties(n-19,0,boardc,turn) ==0:
            liberties(n-19,0,board,turn)
            caps += capture(board,turn)
            if caps == 1:
                new_ko = n-19
        boardc = board[:]
        if n/19 <18 and liberties(n+19,0,boardc,turn) ==0:
            liberties(n+19,0,board,turn)
            caps += capture(board,turn)
            if caps == 1:
                new_ko = n+19
        boardc = board[:]
        if caps == 1:
            turn = next(turn)
            if group(n,0,boardc,turn) == 1:
                ko = new_ko
            turn = next(turn)
    turn = next(turn)
    board[n] = turn
    turn = next(turn)

def scoresection(board, que, color, n)
    #mark each empty space until you get to a stone. if it's different color, no territory.
    if board[n] == "+":
        board[n] = "*"
        que += [n]
        if n%19 < 18:
             k,c  = scoresection(board,que,color,n+1)
             if c == "":
                 return k,c
        if n%19 > 0:
             k,c  = scoresection(board,que,color,n-1)
             if c == "":
                 return k,c
        if n/19 < 18:
            k,c  = scoresection(board,que,color,n+19)
            if c == "":
                return k,c
        if n/19 > 0:
            k,c  = scoresection(board,que,color,n-19)
            if c == "":
                return k,c
    elif board[n] == "B" and color = "":
        color = "B"
    elif board[n] == "W" and color = "":
        color = "W"
    elif color == board[n]:
        return que,color
    elif n not in que:
        return [],""

def evaluatescore(board,komi,handicap):
    #score the section of all 361 points. return total.
    que = []
    color =""
    totalBlack = 0
    totalWhite = 0
    boardC = board[:]
    for i in range(0,361):
        que,color = scoresection(boardC,que,color,n)
        if color != "":
            for i in range(0,len(que)):
                board[que[i]] = color
    for i in range(0,361):
        if boardC[i] == "B":
            totalBlack += 1
        if boardC[i] == "W":
            totalWhite += 1
    return totalBlack -totalWhite -komi -handicap
seki
Beginner
Posts: 1
Joined: Tue Feb 03, 2015 1:58 pm
Rank: IGS 13k
GD Posts: 0
KGS: seki
IGS: seki
Location: Vancouver, Canada

Re: the basics of creating a go bot

Post by seki »

Fun times :study:

What are you planning for the "Finding the best move" part? It feels like going Object-Oriented would make it easier to build the move finding logic?

For instance, you could parse the board into Group objects and check their liberty/eye count/potential territory value to help find the best move?

I have never written a GoBot myself yet, so I am curious to see what others are doing. I guess looking into Open Source projects like GNU Go would be a good resource too?
Pippen
Lives in gote
Posts: 677
Joined: Thu Sep 16, 2010 3:34 pm
GD Posts: 0
KGS: 2d
Has thanked: 6 times
Been thanked: 31 times

Re: the basics of creating a go bot

Post by Pippen »

Is that code or pseudo code? Could that program play a game of Go already if I let it run?

p.s. I have no idea about programming besides some very very basic stuff, but I always hoped I could learn more by looking at basic go program and learn from there.
phillip1882
Lives in gote
Posts: 323
Joined: Sat Jan 08, 2011 7:31 am
Rank: 6k
GD Posts: 25
OGS: phillip1882
Has thanked: 4 times
Been thanked: 39 times

Re: the basics of creating a go bot

Post by phillip1882 »

What are you planning for the "Finding the best move" part?
i'm not sure yet. i have a general idea of what i want: some kind of monte carlo approach, but focusing on the influence of each group rather than the final score. but how exactly to program that will take me some time.
is this code or pseudo-code?
yes its runnable code but it can't really do anything yet. i have no code calling the functions i'm declaring, so this won't actaully play go yet. getting there.
User avatar
quantumf
Lives in sente
Posts: 844
Joined: Tue Apr 20, 2010 11:36 pm
Rank: 3d
GD Posts: 422
KGS: komi
Has thanked: 180 times
Been thanked: 151 times

Re: the basics of creating a go bot

Post by quantumf »

phillip1882 wrote:i'm not sure yet. i have a general idea of what i want: some kind of monte carlo approach, but focusing on the influence of each group rather than the final score. but how exactly to program that will take me some time.
Interesting idea. Two questions

1. Influence becomes zero at the end of the game, and tends towards zero as you approach the end. This suggests that the bot's endgame will be hopeless, unless some kind of hybrid approach is chosen.

2. Do you have in mind an monte carlo algorithm that does playouts with each a move more or less at random and then chooses the move that leads to the highest average influence (either highest absolute value or highest average) instead of highest winning percentage?
User avatar
oca
Lives in gote
Posts: 699
Joined: Wed Feb 19, 2014 2:53 am
Rank: DDK
GD Posts: 0
KGS: aco
IGS: oca
OGS: oca
Location: Switzerland
Has thanked: 485 times
Been thanked: 166 times

Re: the basics of creating a go bot

Post by oca »

If I had to do a go engine, I will neither do a monte-carlo, nor a machine learning one.
Not that these approach are wrong, that's just that I'm not interested in build a strong computer, I'm interested as getting "a bit" stronger myself [edit]with no illusions however[/edit].

With that goal, building a program that eats tons of pro games and learn from them will certainly improve my computer skills, but not my go skills...

building a go engine that plays "like me" would be more the idea for me, the program would certainly never be a strong one, but I'm sure I can discover more things realated to go with that kind of approach.

Once again, in your case, buidling a monte carlo engine may be just fine, it just depend what we are looking for.
Converting the book Shape UP! by Charles Matthews/Seong-June Kim
to the gobook format. last updated april 2015 - Index of shapes, p.211 / 216
Mike Novack
Lives in sente
Posts: 1045
Joined: Mon Aug 09, 2010 9:36 am
GD Posts: 0
Been thanked: 182 times

Re: the basics of creating a go bot

Post by Mike Novack »

For those new to the game (programming)

You won't believe this, but the place to start is not by diving in with code of real programming languages. At the design stage, you shouldn't be at the level of language, should all still be abstract. Starting right away with code will not save you time in the long run. It will not make it easier.

Nor is a "go playing program" the right place to start gaining experience with software design. Even very experienced programmers have failed at this. For example --- just prior to programs using MCTS evaluation, the two strongest go playing AIs were go++7 and mfog 11 in that order (they were close, but go++7 maybe a stone stronger at that point). But MCTS changed everything, and since a very computationally intensive approach, the ability to design so could efficiently use the crunch power of multi-core CPUs important.

Fotland had that expertise (multi-core) but Mick Reiss did not. I was doing beta testing on go++8 as Mick struggled and failed to keep up in the race (he never got go++8 fast enough to be competitive and eventually gave up).

Seriously, start with problems that are simple and well defined. Hone your software designing on those. Especially learning how to divide problems up into parts even simpler and more well defined that you can test independently of the whole.
Bill Spight
Honinbo
Posts: 10905
Joined: Wed Apr 21, 2010 1:24 pm
Has thanked: 3651 times
Been thanked: 3373 times

Re: the basics of creating a go bot

Post by Bill Spight »

quantumf wrote:
phillip1882 wrote:i'm not sure yet. i have a general idea of what i want: some kind of monte carlo approach, but focusing on the influence of each group rather than the final score. but how exactly to program that will take me some time.
Interesting idea. Two questions

1. Influence becomes zero at the end of the game, and tends towards zero as you approach the end. This suggests that the bot's endgame will be hopeless, unless some kind of hybrid approach is chosen.
Influence is ambiguous. In the context of a computer algorithm, influence becomes territory at the end of the game. It is neither necessary nor desirable for influence in a go program to be what go players call outside influence, which indeed becomes zero at the end of the game, when there is no outside to influence. :)
The Adkins Principle:
At some point, doesn't thinking have to go on?
— Winona Adkins

Visualize whirled peas.

Everything with love. Stay safe.
User avatar
oren
Oza
Posts: 2777
Joined: Sun Apr 18, 2010 5:54 pm
GD Posts: 0
KGS: oren
Tygem: oren740, orenl
IGS: oren
Wbaduk: oren
Location: Seattle, WA
Has thanked: 251 times
Been thanked: 549 times

Re: the basics of creating a go bot

Post by oren »

Mike Novack wrote:For those new to the game (programming)

You won't believe this, but the place to start is not by diving in with code of real programming languages. At the design stage, you shouldn't be at the level of language, should all still be abstract. Starting right away with code will not save you time in the long run. It will not make it easier.
I'll disagree with this. If you know how to program and what you're doing, start with a plan and design. If you're getting started, just start coding and having fun and learn what's going on with the system. Look up other examples and read how to do it online.

Of course to come back to go, it's exactly the same. I will get a beginner to play some 9x9 and keep playing until they have some idea what's going on. Then you can sit back and plan out deep strategies.

So go have fun!
phillip1882
Lives in gote
Posts: 323
Joined: Sat Jan 08, 2011 7:31 am
Rank: 6k
GD Posts: 25
OGS: phillip1882
Has thanked: 4 times
Been thanked: 39 times

Re: the basics of creating a go bot

Post by phillip1882 »

I'm tentatively calling this version 1. I've done some very basic error checking, and as far as i can tell, everything is now in working order. I have officially programmed the rules of the game, as far as i can tell.

Code: Select all

import sys
def liberties(n, t, boardc, turn):
    #check the current position n. if empty, mark it and increase counter by 1
    if boardc[n] == '+':
        boardc[n] = '*'
        return 1
    #if stone, mark it and recursively check left right top and bottom.
    elif boardc[n] == turn:
        if turn == "B":
            boardc[n] = 'b'
        else:
            boardc[n] = 'w'
        if n%19 < 18:
            t = t +liberties(n+1, t, boardc, turn)
        if n%19 > 0:
            t = t +liberties(n-1, t, boardc, turn)
        if n/19 > 0:
            t = t +liberties(n-19, t, boardc, turn)
        if n/19 < 18:
            t = t +liberties(n+19, t, boardc, turn)
    else:
        return 0
    return t
    
def group(n,t,boardc,turn):
    #same as liberty except count stones instead of empty.
    if boardc[n] == turn:
        if turn == "B":
            boardc[n] = 'b'
        else:
            boardc[n] = 'w'
        t += 1
        if n%19 < 18:
            group(n+1, t, boardc,turn)
        if n%19 > 0:
            group(n-1, t, boardc,turn)
        if n/19 > 0:
            group(n-19, t, boardc,turn)
        if n/19 < 18:
            group(n+19, t, boardc,turn)
    else:
        return t

def printBoard(board):
    for i in range(0,19):
        sys.stdout.write(chr(i+65))
    sys.stdout.write("\n")
    for i in range(0,19):
        for j in range(0,19):
            sys.stdout.write(board[19*i+j])
        sys.stdout.write(str(i+1)+"\n")

def clearLibMark(board):
    for i in range(0,361):
        if board[i] == "*":
            board[i] = "+"
        if board[i] == "w":
            board[i] = "W"
        if board[i] == "b":
            board[i] = "B"
            
def posToInt(v):
   x = v[0]
   v = v[1:]
   y = int(v)
   x = ord(x)-65
   return 19*(y-1)+x

def next(turn):
    if turn == 'B':
        turn = 'W'
    else:
        turn = 'B'
    return turn

def is_legal(n,board,koState,turn):
    if n =="PASS":
        return True
    if n == "RESIGN":
        return True
    if board[n] !="+":
        return False
    boardc = board[:] 
    #if you capture, check ko, if not ko, its legal, if no capture, check suicide
    boardc[n] = turn
    turn = next(turn)
    if n%19 <18 and liberties(n+1,0,boardc,turn)==0:
        capture(boardc)
    clearLibMark(boardc)
    if n%19 >0 and liberties(n-1,0,boardc,turn)==0:
        capture(boardc)
    clearLibMark(boardc)
    if n/19 >0 and liberties(n-19,0,boardc,turn) ==0:
        capture(boardc)
    clearLibMark(boardc)
    #printBoard(boardc)
    if n/19 <18 and liberties(n+19,0,boardc,turn) ==0:
        capture(boardc)
    clearLibMark(boardc)
    check = [False]*5
    for i in range(0,361):
        if boardc[i] != koState[0][i]:
            check[0] = True
        if boardc[i] != koState[1][i]:
            check[1] = True
        if boardc[i] != koState[2][i]:
            check[2] = True
        if boardc[i] != koState[3][i]:
            check[3] = True
        if boardc[i] != koState[4][i]:
            check[4] = True
    if not (check[0] and check[1] and check[2] and check[3] and check[4]):
        print check[0],check[1],check[2],check[3],check[4]
        return False
    turn = next(turn)
    if liberties(n,0,boardc,turn) ==0:
        return False
    else:
        return True

def capture(board):
    #clear captured stones denoted b or w, return total captured
    total = 0
    for i in range(0,len(board)):
        if board[i] != '+':
            if board[i] == "w" or board[i] == "b":
                board[i] = '+'
                total += 1
    return total

def play(n,board,koState,turn):
    if is_legal(n,board,koState,turn):            
        board[n] = turn
        turn = next(turn)
        if n%19 <18 and liberties(n+1,0,board,turn)==0:
            capture(board)
        clearLibMark(board)
        if n%19 >0 and liberties(n-1,0,board,turn)==0:
            capture(board)
        clearLibMark(board)
        if n/19 >0 and liberties(n-19,0,board,turn) ==0:
            capture(board)
        clearLibMark(board)
        if n/19 <18 and liberties(n+19,0,board,turn) ==0:
            capture(board)
        clearLibMark(board)
        updateKoState(koState,board)
    return turn

def deadStones(board,n):
    if board[n] == "W":
        group(n,0,board,"W")
    if board[n] == "B":
        group(n,0,board,"B")
    capture(board)

def updateKoState(koState,board):
    koState[0],koState[1],koState[2],koState[3],koState[4] = (
    board[:],koState[0][:],koState[1][:],koState[2][:],koState[3][:])
    
def scoreSection(board, que, color, n):
    if board[n] == "+":
        board[n] = "*"
        que += [n]
        if n%19 > 0:
            que, color = scoreSection(board, que, color, n-1)
        if n%19 <18:
            que, color = scoreSection(board, que, color, n+1)
        if n/19 >0:
            que, color = scoreSection(board, que, color, n-19)
        if n/19 <18:
            que, color = scoreSection(board, que, color, n+19)
    elif board[n] == "W" or board[n] == "B":
        if color == "Neutral":
            color = board[n]
            return que,color
        elif color == board[n]:
            return que,color
        else:
            return [],"None"
    return que,color
        
def evaluateScore(board,komi,handicap):
    #score the section of all 361 points. return total.
    que = []
    color ="Neutral"
    totalBlack = 0
    totalWhite = 0
    boardC = board[:]
    for i in range(0,361):
        que,color = scoreSection(boardC,que,color,i)
        if que != [] and color != "None":
            for j in range(0,len(que)):
                boardC[que[j]] = color
        que, color = [],"Neutral"
    for i in range(0,361):
        if boardC[i] == "B":
            totalBlack += 1
        if boardC[i] == "W":
            totalWhite += 1
    return totalBlack -totalWhite -komi -handicap

board = ["+"]*361
black = 'B'
white = 'W'
empty = '+'
turn = "B"
koState = [board[:]]+[board[:]]+[board[:]]+[board[:]]+[board[:]]
handicap = int(raw_input("handicap?"))
for i in range(0,handicap):
    printBoard()
    n = raw_input("position of handi-stone?")
    n = posToInt(n)
    play(n,board,koState,turn)
komi = float(raw_input("komi?"))
check = 0
while check < 2:
    printBoard(board)
    n = raw_input("position of move? (or PASS RESIGN) ")
    if n == "PASS":
        check += 1
        koState = [board[:]]+[board[:]]+[board[:]]+[board[:]]+[board[:]]
        turn = next(turn)
    elif n == "RESIGN":
        break
    else:
        check = 0
        n = posToInt(n)
        print n
        turn = play(n,board,koState,turn)

while True:
    n = raw_input("dead stone(s) coordinate? DONE to stop ")
    if n == "DONE":
        break
    n = posToInt(n)
    deadStones(board,n)
    printBoard(board)

k = evaluateScore(board,komi,handicap)
print(k)
if n == "RESIGN":
   turn = next(turn)
   print turn, "wins"
else:
   if k > 0:
       print "B wins"
   else:
       print "W wins"
you can use this program to play against yourself in console. not very visual but its a step by step process.
xed_over
Oza
Posts: 2264
Joined: Mon Apr 19, 2010 11:51 am
Has thanked: 1179 times
Been thanked: 553 times

Re: the basics of creating a go bot

Post by xed_over »

phillip1882 wrote: I've done some very basic error checking, and as far as i can tell, everything is now in working order.
line 211, you forgot to pass the board object to the printBoard() function.

and I'd recommend using a diagram similar to what GnuGo uses:

Code: Select all

    A B C D E F G H J K L M N O P Q R S T
 19 . . . . . . . . . . . . . . . . . . . 19
 18 . . . . . . . . . . . . . . . . . . . 18
 17 . . . . . . . . . . . . . . . . . . . 17
 16 . . . + . . . . . + . . . . . + . . . 16
 15 . . . . . . . . . . . . . . . . . . . 15
 14 . . . . . . . . . . . . . . . . . . . 14
 13 . . . . . . . . . . . . . . . . . . . 13
 12 . . . . . . . . . . . . . . . . . . . 12
 11 . . . . . . . . . . . . . . . . . . . 11
 10 . . . + . . . . . + . . . . . + . . . 10
  9 . . . . . . . . . . . . . . . . . . .  9
  8 . . . . . . . . . . . . . . . . . . .  8
  7 . . . . . . . . . . . . . . . . . . .  7
  6 . . . . . . . . . . . . . . . . . . .  6
  5 . . . . . . . . . . . . . . . . . . .  5
  4 . . . + . . . . . + . . . . . + . . .  4
  3 . . . . . . . . . . . . . . . . . . .  3
  2 . . . . . . . . . . . . . . . . . . .  2
  1 . . . . . . . . . . . . . . . . . . .  1
    A B C D E F G H J K L M N O P Q R S T
its easier to read than yours...

and your coordinates are way, way off...
D4 (upper right?) and P16 (lower middle?)

Code: Select all

ABCDEFGHIJKLMNOPQRS
+++++++++++++++++++1
+++++++++++++++++++2
+++++++++++++++++++3
+++++++++++++++++++4
++++++++++++++++B++5
+++++++++++++++++++6
+++++++++++++++++++7
+++++++++++++++++++8
+++++++++++++++++++9
+++++++++++++++++++10
+++++++++++++++++++11
+++++++++++++++++++12
+++++++++++++++++++13
+++++++++++++++++++14
+++++++++++++++++++15
+++++++++++++++++++16
+++++++++++++++++++17
+++++++++B+++++++++18
+++++++++++++++++++19
skydyr
Oza
Posts: 2495
Joined: Wed Aug 01, 2012 8:06 am
GD Posts: 0
Universal go server handle: skydyr
Online playing schedule: When my wife is out.
Location: DC
Has thanked: 156 times
Been thanked: 436 times

Re: the basics of creating a go bot

Post by skydyr »

xed_over wrote:
and your coordinates are way, way off...
D4 (upper right?) and P16 (lower middle?)

Code: Select all

ABCDEFGHIJKLMNOPQRS
+++++++++++++++++++1
+++++++++++++++++++2
+++++++++++++++++++3
+++++++++++++++++++4
++++++++++++++++B++5
+++++++++++++++++++6
+++++++++++++++++++7
+++++++++++++++++++8
+++++++++++++++++++9
+++++++++++++++++++10
+++++++++++++++++++11
+++++++++++++++++++12
+++++++++++++++++++13
+++++++++++++++++++14
+++++++++++++++++++15
+++++++++++++++++++16
+++++++++++++++++++17
+++++++++B+++++++++18
+++++++++++++++++++19
I would recommend skipping the letter I as well.
xed_over
Oza
Posts: 2264
Joined: Mon Apr 19, 2010 11:51 am
Has thanked: 1179 times
Been thanked: 553 times

Re: the basics of creating a go bot

Post by xed_over »

I don't have a problem with using the letter "I". Its more difficult to program around it. I wouldn't worry about it for a first program.


I figured out my problem with the coordinates... you're expecting uppercase, and I'm typing lower.
Adding a .upper() to your raw_input fixes that. You've got to do better at sanitizing your inputs.

you're next problem seems to be... when setting handicap, you still play black next, but it should be white.
and when resigning, you need to skip marking the dead stones.


And I'd suggest using a 2-dimentional array 19x19, or perhaps even better, maybe a 2-dimentional hash, instead of a single 361 array. Then you'll be closer to matching standard sgf coordinates, in order to read/write games.

and why are you defining a global 'empty', but you don't use it? Instead, you hardcode the "+" string everywhere, instead of using the global where you can then actually change the value in only one place.
phillip1882
Lives in gote
Posts: 323
Joined: Sat Jan 08, 2011 7:31 am
Rank: 6k
GD Posts: 25
OGS: phillip1882
Has thanked: 4 times
Been thanked: 39 times

Re: the basics of creating a go bot

Post by phillip1882 »

dubbing this version 1.02, minor visual changes. and score changes. makes white the first player after handi.

Code: Select all

import sys
def liberties(n, t, boardc, turn):
    #check the current position n. if empty, mark it and increase counter by 1
    if boardc[n] == '+':
        boardc[n] = '*'
        return 1
    #if stone, mark it and recursively check left right top and bottom.
    elif boardc[n] == turn:
        if turn == "B":
            boardc[n] = 'b'
        else:
            boardc[n] = 'w'
        if n%19 < 18:
            t = t +liberties(n+1, t, boardc, turn)
        if n%19 > 0:
            t = t +liberties(n-1, t, boardc, turn)
        if n/19 > 0:
            t = t +liberties(n-19, t, boardc, turn)
        if n/19 < 18:
            t = t +liberties(n+19, t, boardc, turn)
    else:
        return 0
    return t
    
def group(n,t,boardc,turn):
    #same as liberty except count stones instead of empty.
    if boardc[n] == turn:
        if turn == "B":
            boardc[n] = 'b'
        else:
            boardc[n] = 'w'
        t += 1
        if n%19 < 18:
            group(n+1, t, boardc,turn)
        if n%19 > 0:
            group(n-1, t, boardc,turn)
        if n/19 > 0:
            group(n-19, t, boardc,turn)
        if n/19 < 18:
            group(n+19, t, boardc,turn)
    else:
        return t

def printBoard(board):
    i = 0
    while i < 20:
        if i == 8:
            i += 1
        sys.stdout.write(chr(i+65)+" ")
        i += 1
    sys.stdout.write("\n")
    for i in range(0,19):
        for j in range(0,19):
            if board[19*i+j] == "B" or board[19*i+j] == "W":
                sys.stdout.write(board[19*i+j]+" ")
                #print "Got here 1"
            else:
                check1 = False
                check2  = False
                if i == 3 or i == 9 or i == 15:
                    check1 = True 
                if j == 3 or j == 9 or j == 15:
                    check2  = True
                if check1 and check2:
                    sys.stdout.write(board[19*i+j]+" ")
                    #print "got here2"
                else:
                    sys.stdout.write(". ")
        sys.stdout.write(str(19-i)+"\n")

def clearLibMark(board):
    for i in range(0,361):
        if board[i] == "*":
            board[i] = "+"
        if board[i] == "w":
            board[i] = "W"
        if board[i] == "b":
            board[i] = "B"
            
def posToInt(v):
   x = v[0]
   v = v[1:]
   y = int(v)
   x = ord(x.upper())-65
   if x>=8:
       x-=1
   return 19*(18-(y-1))+x

def next(turn):
    if turn == 'B':
        turn = 'W'
    else:
        turn = 'B'
    return turn

def is_legal(n,board,koState,turn):
    if n =="PASS":
        return True
    if n == "RESIGN":
        return True
    if board[n] !="+":
        return False
    boardc = board[:] 
    #if you capture, check ko, if not ko, its legal, if no capture, check suicide
    boardc[n] = turn
    turn = next(turn)
    if n%19 <18 and liberties(n+1,0,boardc,turn)==0:
        capture(boardc)
    clearLibMark(boardc)
    if n%19 >0 and liberties(n-1,0,boardc,turn)==0:
        capture(boardc)
    clearLibMark(boardc)
    if n/19 >0 and liberties(n-19,0,boardc,turn) ==0:
        capture(boardc)
    clearLibMark(boardc)
    #printBoard(boardc)
    if n/19 <18 and liberties(n+19,0,boardc,turn) ==0:
        capture(boardc)
    clearLibMark(boardc)
    check = [False]*5
    for i in range(0,361):
        if boardc[i] != koState[0][i]:
            check[0] = True
        if boardc[i] != koState[1][i]:
            check[1] = True
        if boardc[i] != koState[2][i]:
            check[2] = True
        if boardc[i] != koState[3][i]:
            check[3] = True
        if boardc[i] != koState[4][i]:
            check[4] = True
    if not (check[0] and check[1] and check[2] and check[3] and check[4]):
        print check[0],check[1],check[2],check[3],check[4]
        return False
    turn = next(turn)
    if liberties(n,0,boardc,turn) ==0:
        return False
    else:
        return True

def capture(board):
    #clear captured stones denoted b or w, return total captured
    total = 0
    for i in range(0,len(board)):
        if board[i] != '+':
            if board[i] == "w" or board[i] == "b":
                board[i] = '+'
                total += 1
    return total

def play(n,board,koState,turn):
    if is_legal(n,board,koState,turn):            
        board[n] = turn
        turn = next(turn)
        if n%19 <18 and liberties(n+1,0,board,turn)==0:
            capture(board)
        clearLibMark(board)
        if n%19 >0 and liberties(n-1,0,board,turn)==0:
            capture(board)
        clearLibMark(board)
        if n/19 >0 and liberties(n-19,0,board,turn) ==0:
            capture(board)
        clearLibMark(board)
        if n/19 <18 and liberties(n+19,0,board,turn) ==0:
            capture(board)
        clearLibMark(board)
        updateKoState(koState,board)
    return turn

def deadStones(board,n):
    if board[n] == "W":
        group(n,0,board,"W")
    if board[n] == "B":
        group(n,0,board,"B")
    capture(board)

def updateKoState(koState,board):
    koState[0],koState[1],koState[2],koState[3],koState[4] = (
    board[:],koState[0][:],koState[1][:],koState[2][:],koState[3][:])
    
def scoreSection(board, que, color, n):
    if board[n] == "+":
        board[n] = "*"
        que += [n]
        if n%19 > 0:
            que, color = scoreSection(board, que, color, n-1)
        if n%19 <18:
            que, color = scoreSection(board, que, color, n+1)
        if n/19 >0:
            que, color = scoreSection(board, que, color, n-19)
        if n/19 <18:
            que, color = scoreSection(board, que, color, n+19)
    elif board[n] == "W" or board[n] == "B":
        if color == "Neutral":
            color = board[n]
            return que,color
        elif color == board[n]:
            return que,color
        else:
            return [],"None"
    return que,color
        
def evaluateScore(board,komi,handicap):
    #score the section of all 361 points. return total.
    que = []
    color ="Neutral"
    totalBlack = 0
    totalWhite = 0
    boardC = board[:]
    for i in range(0,361):
        que,color = scoreSection(boardC,que,color,i)
        if que != [] and color != "None":
            for j in range(0,len(que)):
                boardC[que[j]] = color
        que, color = [],"Neutral"
    for i in range(0,361):
        if boardC[i] == "B":
            totalBlack += 1
        if boardC[i] == "W":
            totalWhite += 1
    return totalBlack -totalWhite -komi -handicap
    
board = ["+"]*361
black = 'B'
white = 'W'
empty = '+'
turn = "B"
koState = [board[:]]+[board[:]]+[board[:]]+[board[:]]+[board[:]]
handicap = int(raw_input("handicap?"))
for i in range(0,handicap):
    printBoard(board)
    n = raw_input("position of handi-stone?")
    n = posToInt(n)
    play(n,board,koState,turn)
if handicap > 0:
    turn = next(turn)
komi = float(raw_input("komi?"))
check = 0
while check < 2:
    printBoard(board)
    n = raw_input("position of move? (or PASS RESIGN) ")
    if n == "PASS":
        check += 1
        koState = [board[:]]+[board[:]]+[board[:]]+[board[:]]+[board[:]]
        turn = next(turn)
    elif n == "RESIGN":
        break
    else:
        check = 0
        n = posToInt(n)
        print n
        turn = play(n,board,koState,turn)
if n !=  "RESIGN":
    while True:
        n = raw_input("dead stone(s) coordinate? DONE to stop ")
        if n == "DONE":
            break
        n = posToInt(n)
        deadStones(board,n)
        printBoard(board)
    
    k = evaluateScore(board,komi,handicap)
    print(k)
if n == "RESIGN":
   turn = next(turn)
   print turn, "wins"
else:
   if k > 0:
       print "B wins"
   else:
       print "W wins"
phillip1882
Lives in gote
Posts: 323
Joined: Sat Jan 08, 2011 7:31 am
Rank: 6k
GD Posts: 25
OGS: phillip1882
Has thanked: 4 times
Been thanked: 39 times

Re: the basics of creating a go bot

Post by phillip1882 »

i don't use the global empty, black, and white, because i wanted to make sure my program worked properly first.
Post Reply