connect four (game)

namenobodywants at gmail.com namenobodywants at gmail.com
Fri Nov 24 10:33:22 EST 2017


hi all

i've just finished my first excursion into artificial intelligence with a game less trivial than tictactoe, and here it is in case anybody can offer criticism/suggestions/etc

peace
stm


###############################
#) connectfour - python 3.6.1
###############################

from tkinter import *
from random  import choice

class infinity:
    def __init__(self,signum): self.signum = signum
    def __repr__(self):     return '+oo' if self.signum > 0 else '-oo'
    def __lt__(self,other): return False if self.signum > 0 else True
    def __le__(self,other): return False if self.signum > 0 else True
    def __gt__(self,other): return True  if self.signum > 0 else False
    def __ge__(self,other): return True  if self.signum > 0 else False    
    def __eq__(self,other): return isinstance(other,infinity) and self.signum == other.signum
    
ex, oh, blank = 'red black white' .split()

startboard = {(i,j):blank for i in range(6) for j in range(7)}

def getlines(board):
    horizontal = [[(i,   j+k) for k in range(4)] for i in range(6) for j in range(7)]
    vertical   = [[(i+k, j)   for k in range(4)] for i in range(6) for j in range(7)]
    positive   = [[(i+k, j+k) for k in range(4)] for i in range(6) for j in range(7)]
    negative   = [[(i+k, j-k) for k in range(4)] for i in range(6) for j in range(7)]
    linear     = horizontal + vertical + positive + negative
    lines      = [line for line in linear if all(i in range(6) and j in range(7) for (i,j) in line)]
    return [[board[square] for square in line] for line in lines]

def getwinner(board):
    lines = getlines(board)
    return ex if [ex]*4 in lines else oh if [oh]*4 in lines else None

def getmover(board):
    boardvalues = list(board.values())
    return ex if boardvalues.count(ex)==boardvalues.count(oh) else oh

def getoptimum(mover):
    return max if mover == ex else min

def getaccessibles(board):
    return [j for j in range(7) if board[0,j] == blank]

def getvalue(winner,accessibles):
    return infinity(+1) if winner == ex else infinity(-1) if winner == oh else 0 if not accessibles else None

def makemove(board,column,mover):
    board = board.copy()
    empties = [row for row in range(6) if board[row,column] == blank]
    if not empties: return board
    board[max(empties),column] = mover
    return board

def takemoveback(board,column):
    board = board.copy()
    occupied = [row for row in range(6) if board[row,column] != blank]
    if not occupied: return board
    board[min(occupied),column] = blank
    return board

def guessvalue(board):
    lines = getlines(board)
    exs = [line for line in lines if line.count(ex)==3 and line.count(oh)==0] 
    ohs = [line for line in lines if line.count(oh)==3 and line.count(ex)==0] 
    return len(exs)-len(ohs)
        
def heuristicvalue(board,depth):    
    winner      = getwinner(board)
    accessibles = getaccessibles(board)
    value       = getvalue(winner,accessibles)
    if value != None: return value
    if depth == 0:    return guessvalue(board)
    mover    = getmover(board)
    optimum  = getoptimum(mover)
    children = [makemove(board,column,mover) for column in accessibles]
    return optimum(heuristicvalue(child,depth-1) for child in children)

def getmoves(board,depth):     
    accessibles = getaccessibles(board)
    if not accessibles: return []
    mover     = getmover(board)
    optimum   = getoptimum(mover)    
    children  = [makemove(board,column,mover) for column in accessibles]
    values    = [heuristicvalue(child,depth)  for child  in children]
    bestvalue = optimum(values)    
    return [accessibles[index] for index in range(len(accessibles)) if values[index] == bestvalue]

class grid:    
    def __init__(self,window):
        self.window        = window
        self.boardframe    = Frame(window.root)
        self.rowframes     = [Frame(self.boardframe) for anonymous in range(6)]
        self.squarebuttons = [Button(self.rowframes[number//7], width=3, command=self.window.squarecommand(number), background=blank) for number in range(42)]
    def pack(self):
        self.boardframe .pack(side=TOP)
        [frame          .pack(side=TOP)  for frame  in self.rowframes]  
        [button         .pack(side=LEFT) for button in self.squarebuttons]

class caption:
    def __init__(self,window):
        self.window      = window
        self.statusframe = Frame(window.root)
        self.statuslabel = Label(self.statusframe, height=2)
    def pack(self):
        self.statusframe .pack(side=TOP)
        self.statuslabel .pack(side=LEFT)

class buttonpanel:
    def __init__(self,window,depth):
        self.window          = window
        self.controlframe    = Frame(window.root)
        self.makemoveframe   = Frame(self.controlframe)
        self.takebackframe   = Frame(self.controlframe)
        self.startoverframe  = Frame(self.controlframe)
        self.makemovebutton  = Button(self.makemoveframe,  text='make move',  command=self.window.movecommand(depth))
        self.takebackbutton  = Button(self.takebackframe,  text='take back',  command=self.window.takeback)
        self.startoverbutton = Button(self.startoverframe, text='start over', command=self.window.startover)
    def pack(self):
        self.controlframe    .pack(side=TOP)
        self.makemoveframe   .pack(side=LEFT)
        self.takebackframe   .pack(side=LEFT)
        self.startoverframe  .pack(side=RIGHT)
        self.makemovebutton  .pack(side=TOP)
        self.takebackbutton  .pack(side=TOP)
        self.startoverbutton .pack(side=TOP)

class window:
    def __init__(self, depth=2):
        self.root        = Tk()
        self.grid        = grid       (self)
        self.caption     = caption    (self)
        self.buttonpanel = buttonpanel(self, depth=2)
        self.grid        .pack()
        self.caption     .pack()
        self.buttonpanel .pack()
        self.startover()
        self.root.title('connect four')
        self.root.mainloop()
    def startover(self): 
        self.board = startboard
        self.movelist = []
        self.display()
    def display(self):
        winner = getwinner(self.board)
        mover  = getmover (self.board)
        tie    = blank not in self.board.values()
        status = winner + ' wins' if winner else 'tied game' if tie else mover + ' to move'
        [self.grid.squarebuttons[number].config(background=self.board[divmod(number,7)]) for number in range(42)]
        self.caption.statuslabel.config(text = status.upper() if winner or tie else status)
    def squarecommand(self,number):
        def returnvalue():
            if getwinner(self.board) or blank not in self.board.values(): return
            column = number % 7
            self.board = makemove(self.board, column, getmover(self.board)) 
            self.display()
            self.grid.squarebuttons[column].flash()
            self.movelist += [column]
        return returnvalue
    def movecommand(self,depth):    
        def returnvalue():        
            if getwinner(self.board) or blank not in self.board.values(): return
            column = choice(getmoves(self.board,depth))
            self.board = makemove(self.board, column, getmover(self.board))
            self.display()
            self.grid.squarebuttons[column].flash()
            self.movelist += [column]
        return returnvalue
    def takeback(self):
        if not self.movelist: return
        self.board = takemoveback(self.board, self.movelist[-1]) 
        self.display()
        self.movelist = self.movelist[:-1]




More information about the Python-list mailing list