Welcome, guest | Sign In | My Account | Store | Cart
# Reversi/Othello Board Game using Minimax and Alpha-Beta Pruning
# https://en.wikipedia.org/wiki/Reversi
# https://en.wikipedia.org/wiki/Computer_Othello
# https://en.wikipedia.org/wiki/Minimax
# https://en.wikipedia.org/wiki/Alpha%E2%80%93beta_pruning
# https://en.wikipedia.org/wiki/Negamax
# https://en.wikipedia.org/wiki/Principal_variation_search
# FB36 - 20160831
import os, copy
n
= 8 # board size (even)
board
= [['0' for x in range(n)] for y in range(n)]
# 8 directions
dirx
= [-1, 0, 1, -1, 1, -1, 0, 1]
diry
= [-1, -1, -1, 0, 0, 1, 1, 1]

def InitBoard():
   
if n % 2 == 0: # if board size is even
        z
= (n - 2) / 2
        board
[z][z] = '2'
        board
[n - 1 - z][z] = '1'        
        board
[z][n - 1 - z] = '1'
        board
[n - 1 - z][n - 1 - z] = '2'
       
def PrintBoard():
    m
= len(str(n - 1))
   
for y in range(n):
        row
= ''
       
for x in range(n):
            row
+= board[y][x]
            row
+= ' ' * m
       
print row + ' ' + str(y)
   
print
    row
= ''
   
for x in range(n):
        row
+= str(x).zfill(m) + ' '
   
print row + '\n'

def MakeMove(board, x, y, player): # assuming valid move
    totctr
= 0 # total number of opponent pieces taken
    board
[y][x] = player
   
for d in range(8): # 8 directions
        ctr
= 0
       
for i in range(n):
            dx
= x + dirx[d] * (i + 1)
            dy
= y + diry[d] * (i + 1)
           
if dx < 0 or dx > n - 1 or dy < 0 or dy > n - 1:
                ctr
= 0; break
           
elif board[dy][dx] == player:
               
break
           
elif board[dy][dx] == '0':
                ctr
= 0; break
           
else:
                ctr
+= 1
       
for i in range(ctr):
            dx
= x + dirx[d] * (i + 1)
            dy
= y + diry[d] * (i + 1)
            board
[dy][dx] = player
        totctr
+= ctr
   
return (board, totctr)

def ValidMove(board, x, y, player):
   
if x < 0 or x > n - 1 or y < 0 or y > n - 1:
       
return False
   
if board[y][x] != '0':
       
return False
   
(boardTemp, totctr) = MakeMove(copy.deepcopy(board), x, y, player)
   
if totctr == 0:
       
return False
   
return True

minEvalBoard
= -1 # min - 1
maxEvalBoard
= n * n + 4 * n + 4 + 1 # max + 1
def EvalBoard(board, player):
    tot
= 0
   
for y in range(n):
       
for x in range(n):
           
if board[y][x] == player:
               
if (x == 0 or x == n - 1) and (y == 0 or y == n - 1):
                    tot
+= 4 # corner
               
elif (x == 0 or x == n - 1) or (y == 0 or y == n - 1):
                    tot
+= 2 # side
               
else:
                    tot
+= 1
   
return tot

# if no valid move(s) possible then True
def IsTerminalNode(board, player):
   
for y in range(n):
       
for x in range(n):
           
if ValidMove(board, x, y, player):
               
return False
   
return True

def GetSortedNodes(board, player):
    sortedNodes
= []
   
for y in range(n):
       
for x in range(n):
           
if ValidMove(board, x, y, player):
               
(boardTemp, totctr) = MakeMove(copy.deepcopy(board), x, y, player)
                sortedNodes
.append((boardTemp, EvalBoard(boardTemp, player)))
    sortedNodes
= sorted(sortedNodes, key = lambda node: node[1], reverse = True)
    sortedNodes
= [node[0] for node in sortedNodes]
   
return sortedNodes

def Minimax(board, player, depth, maximizingPlayer):
   
if depth == 0 or IsTerminalNode(board, player):
       
return EvalBoard(board, player)
   
if maximizingPlayer:
        bestValue
= minEvalBoard
       
for y in range(n):
           
for x in range(n):
               
if ValidMove(board, x, y, player):
                   
(boardTemp, totctr) = MakeMove(copy.deepcopy(board), x, y, player)
                    v
= Minimax(boardTemp, player, depth - 1, False)
                    bestValue
= max(bestValue, v)
   
else: # minimizingPlayer
        bestValue
= maxEvalBoard
       
for y in range(n):
           
for x in range(n):
               
if ValidMove(board, x, y, player):
                   
(boardTemp, totctr) = MakeMove(copy.deepcopy(board), x, y, player)
                    v
= Minimax(boardTemp, player, depth - 1, True)
                    bestValue
= min(bestValue, v)
   
return bestValue

def AlphaBeta(board, player, depth, alpha, beta, maximizingPlayer):
   
if depth == 0 or IsTerminalNode(board, player):
       
return EvalBoard(board, player)
   
if maximizingPlayer:
        v
= minEvalBoard
       
for y in range(n):
           
for x in range(n):
               
if ValidMove(board, x, y, player):
                   
(boardTemp, totctr) = MakeMove(copy.deepcopy(board), x, y, player)
                    v
= max(v, AlphaBeta(boardTemp, player, depth - 1, alpha, beta, False))
                    alpha
= max(alpha, v)
                   
if beta <= alpha:
                       
break # beta cut-off
       
return v
   
else: # minimizingPlayer
        v
= maxEvalBoard
       
for y in range(n):
           
for x in range(n):
               
if ValidMove(board, x, y, player):
                   
(boardTemp, totctr) = MakeMove(copy.deepcopy(board), x, y, player)
                    v
= min(v, AlphaBeta(boardTemp, player, depth - 1, alpha, beta, True))
                    beta
= min(beta, v)
                   
if beta <= alpha:
                       
break # alpha cut-off
       
return v

def AlphaBetaSN(board, player, depth, alpha, beta, maximizingPlayer):
   
if depth == 0 or IsTerminalNode(board, player):
       
return EvalBoard(board, player)
    sortedNodes
= GetSortedNodes(board, player)
   
if maximizingPlayer:
        v
= minEvalBoard
       
for boardTemp in sortedNodes:
            v
= max(v, AlphaBetaSN(boardTemp, player, depth - 1, alpha, beta, False))
            alpha
= max(alpha, v)
           
if beta <= alpha:
               
break # beta cut-off
       
return v
   
else: # minimizingPlayer
        v
= maxEvalBoard
       
for boardTemp in sortedNodes:
            v
= min(v, AlphaBetaSN(boardTemp, player, depth - 1, alpha, beta, True))
            beta
= min(beta, v)
           
if beta <= alpha:
               
break # alpha cut-off
       
return v

def Negamax(board, player, depth, color):
   
if depth == 0 or IsTerminalNode(board, player):
       
return color * EvalBoard(board, player)
    bestValue
= minEvalBoard
   
for y in range(n):
       
for x in range(n):
           
if ValidMove(board, x, y, player):
               
(boardTemp, totctr) = MakeMove(copy.deepcopy(board), x, y, player)
                v
= -Negamax(boardTemp, player, depth - 1, -color)
                bestValue
= max(bestValue, v)
   
return bestValue

def NegamaxAB(board, player, depth, alpha, beta, color):
   
if depth == 0 or IsTerminalNode(board, player):
       
return color * EvalBoard(board, player)
    bestValue
= minEvalBoard
   
for y in range(n):
       
for x in range(n):
           
if ValidMove(board, x, y, player):
               
(boardTemp, totctr) = MakeMove(copy.deepcopy(board), x, y, player)
                v
= -NegamaxAB(boardTemp, player, depth - 1, -beta, -alpha, -color)
                bestValue
= max(bestValue, v)
                alpha
= max(alpha, v)
               
if alpha >= beta:
                   
break
   
return bestValue

def NegamaxABSN(board, player, depth, alpha, beta, color):
   
if depth == 0 or IsTerminalNode(board, player):
       
return color * EvalBoard(board, player)
    sortedNodes
= GetSortedNodes(board, player)
    bestValue
= minEvalBoard
   
for boardTemp in sortedNodes:
        v
= -NegamaxABSN(boardTemp, player, depth - 1, -beta, -alpha, -color)
        bestValue
= max(bestValue, v)
        alpha
= max(alpha, v)
       
if alpha >= beta:
           
break
   
return bestValue

def Negascout(board, player, depth, alpha, beta, color):
   
if depth == 0 or IsTerminalNode(board, player):
       
return color * EvalBoard(board, player)
    firstChild
= True
   
for y in range(n):
       
for x in range(n):
           
if ValidMove(board, x, y, player):
               
(boardTemp, totctr) = MakeMove(copy.deepcopy(board), x, y, player)
               
if not firstChild:
                    score
= -Negascout(boardTemp, player, depth - 1, -alpha - 1, -alpha, -color)
                   
if alpha < score and score < beta:
                        score
= -Negascout(boardTemp, player, depth - 1, -beta, -score, -color)
               
else:
                    firstChild
= False
                    score
= -Negascout(boardTemp, player, depth - 1, -beta, -alpha, -color)
                alpha
= max(alpha, score)
               
if alpha >= beta:
                   
break
   
return alpha

def NegascoutSN(board, player, depth, alpha, beta, color):
   
if depth == 0 or IsTerminalNode(board, player):
       
return color * EvalBoard(board, player)
    sortedNodes
= GetSortedNodes(board, player)
    firstChild
= True
   
for boardTemp in sortedNodes:
       
if not firstChild:
            score
= -NegascoutSN(boardTemp, player, depth - 1, -alpha - 1, -alpha, -color)
           
if alpha < score and score < beta:
                score
= -NegascoutSN(boardTemp, player, depth - 1, -beta, -score, -color)
       
else:
            firstChild
= False
            score
= -NegascoutSN(boardTemp, player, depth - 1, -beta, -alpha, -color)
        alpha
= max(alpha, score)
       
if alpha >= beta:
           
break
   
return alpha

def BestMove(board, player):
    maxPoints
= 0
    mx
= -1; my = -1
   
for y in range(n):
       
for x in range(n):
           
if ValidMove(board, x, y, player):
               
(boardTemp, totctr) = MakeMove(copy.deepcopy(board), x, y, player)
               
if opt == 0:
                    points
= EvalBoard(boardTemp, player)
               
elif opt == 1:
                    points
= Minimax(boardTemp, player, depth, True)
               
elif opt == 2:
                    points
= AlphaBeta(board, player, depth, minEvalBoard, maxEvalBoard, True)
               
elif opt == 3:
                    points
= Negamax(boardTemp, player, depth, 1)
               
elif opt == 4:
                    points
= NegamaxAB(boardTemp, player, depth, minEvalBoard, maxEvalBoard, 1)
               
elif opt == 5:
                    points
= Negascout(boardTemp, player, depth, minEvalBoard, maxEvalBoard, 1)
               
elif opt == 6:
                    points
= AlphaBetaSN(board, player, depth, minEvalBoard, maxEvalBoard, True)
               
elif opt == 7:
                    points
= NegamaxABSN(boardTemp, player, depth, minEvalBoard, maxEvalBoard, 1)
               
elif opt == 8:
                    points
= NegascoutSN(boardTemp, player, depth, minEvalBoard, maxEvalBoard, 1)
               
if points > maxPoints:
                    maxPoints
= points
                    mx
= x; my = y
   
return (mx, my)

print 'REVERSI/OTHELLO BOARD GAME'
print '0: EvalBoard'
print '1: Minimax'
print '2: Minimax w/ Alpha-Beta Pruning'
print '3: Negamax'
print '4: Negamax w/ Alpha-Beta Pruning'
print '5: Negascout (Principal Variation Search)'
print '6: Minimax w/ Alpha-Beta Pruning w/ Sorted Nodes'
print '7: Negamax w/ Alpha-Beta Pruning w/ Sorted Nodes'
print '8: Negascout (Principal Variation Search) w/ Sorted Nodes'
opt
= int(raw_input('Select AI Algorithm: '))
if opt > 0 and opt < 9:
    depth
= 4
    depthStr
= raw_input('Select Search Depth (DEFAULT: 4): ')
   
if depthStr != '': depth = int(depth)
print '\n1: User 2: AI (Just press Enter for Exit!)'
InitBoard()
while True:
   
for p in range(2):
       
print
       
PrintBoard()
        player
= str(p + 1)
       
print 'PLAYER: ' + player
       
if IsTerminalNode(board, player):
           
print 'Player cannot play! Game ended!'
           
print 'Score User: ' + str(EvalBoard(board, '1'))
           
print 'Score AI  : ' + str(EvalBoard(board, '2'))
            os
._exit(0)            
       
if player == '1': # user's turn
           
while True:
                xy
= raw_input('X Y: ')
               
if xy == '': os._exit(0)
               
(x, y) = xy.split()
                x
= int(x); y = int(y)
               
if ValidMove(board, x, y, player):
                   
(board, totctr) = MakeMove(board, x, y, player)
                   
print '# of pieces taken: ' + str(totctr)
                   
break
               
else:
                   
print 'Invalid move! Try again!'
       
else: # AI's turn
           
(x, y) = BestMove(board, player)
           
if not (x == -1 and y == -1):
               
(board, totctr) = MakeMove(board, x, y, player)
               
print 'AI played (X Y): ' + str(x) + ' ' + str(y)
               
print '# of pieces taken: ' + str(totctr)

History