''' connect four game play by running connect4() ''' import random def connect4(): answer = str(input('Would you like to play the computer? (y/n): ')) if answer == 'y': ply = int(input('What level of difficulty would you like? (0,1,2,3,4): ')) print('Patience please while the game loads...') playerX = Player('X', 'RANDOM', ply) board=Board(6,7) board.playGameH(playerX) elif answer == 'n': print('Patience please while the game loads and the \'players\' make their choices...') playerX = Player('X', 'RANDOM', 4) playerO = Player('O', 'RANDOM', 4) board = Board(6,7) board.playGameAI(playerX, playerO) class Board: """ A user-defined data structure that stores and manipulates connect-4 boards """ def __init__(self, height, width): """ Creates a connect four board of an inputted width and height. """ self.data = [[' ']*width for row in range(height)] self.height=height self.width=width def __repr__(self): """ Prints the connect four board, with column labels. """ s = '' for row in range(self.height): s += '|' for col in range(self.width): s += self.data[row][col] + '|' s += '\n' for col in range(self.width): s += ' ' + str(col%10) return s def addMove(self, col, ox): """ Adds a piece X or O in the given column. """ for row in range(self.height): if self.data[row][col] != ' ': break if self.data[row][col] != ' ': self.data[row-1][col] = ox else: self.data[row][col] = ox def clear(self): """ Clears board of pieces and returns empty board. """ self.data = [[' ']*self.width for row in range(self.height)] def setBoard(self, moveString): """ Takes in a string of columns and places alternating checkers in those columns, starting with 'X'. For example, call board.setBoard('0123456') to see 'X's and 'O's alternate on the bottom row, or board.setBoard('000000') to see them alternate in the left column. moveString must be a string of integers. """ nextChar = 'X' # start by playing 'X' for colString in moveString: col = int(colString) if 0 <= col < self.width: self.addMove(col, nextChar) if nextChar == 'X': nextChar = 'O' else: nextChar = 'X' def isMoveLegal(self, col): """ Returns True if move piece placement is legal, False otherwise. """ return 0 <= col < self.width and self.data[0][col]==' ' def isFull(self): """ Returns True if board is full, False otherwise. """ i=True for row in range(self.height): for col in range(self.width): if self.data[row][col] == ' ': i=False return i def deleteMove(self, col, ox): """ Removes a piece X or O from the give column. """ for row in range(0,self.height): if self.data[row][col] == ox: break if self.data[row][col] == ox: self.data[row][col] = ' ' def isWinFor(self, ox): """ Returns True if X or O has four pieces in a row, horizontally, vertically, or diagonally. """ return self.isWinForMajorDiagonal(ox) or \ self.isWinForMinorDiagonal(ox) or \ self.isVerticalWin(ox) or \ self.isHorizontalWin(ox) def isWinForMajorDiagonal(self, ox): """ Checks for a win along a major diagonal. """ for row in range(self.height-3): for col in range(self.width-3): if self.data[row][col] == self.data[row+1][col+1] == \ self.data[row+2][col+2] == self.data[row+3][col+3] == ox: return True return False def isWinForMinorDiagonal(self, ox): """ Checks for a win along a minor diagonal. """ for row in range(3,self.height): for col in range(self.width-3): if self.data[row][col] == self.data[row-1][col+1] == \ self.data[row-2][col+2] == self.data[row-3][col+3] == ox: return True return False def isVerticalWin(self, ox): """ Checks for a vertical win. """ for col in range(self.width): for row in range(self.height-3): if self.data[row][col] == self.data[row+1][col] == \ self.data[row+2][col] == self.data[row+3][col] == ox: return True return False def isHorizontalWin(self, ox): """ Checks for a horizontal win. """ for row in range(self.height): for col in range(self.width - 3): if self.data[row][col] == self.data[row][col + 1] == \ self.data[row][col + 2] == self.data[row][col + 3] == ox: return True return False def hostGame(self): print() print('Welcome to Connect Four!') print() while True: print(self) print() col=int(input('X\'s choice: ')) while self.isMoveLegal(col) == False: print('Not a legal move! Choose again!') col=int(input('X\'s choice: ')) self.addMove(col, 'X') print() if self.isWinFor('X'): print(self) print() print('X wins -- Congratulations!') self.clear() break if self.isFull(): print(self) print() print('End of game: no winner. Goodbye!') self.clear() break print(self) print() col=int(input('O\'s choice: ')) while self.isMoveLegal(col) == False: print('Not a legal move! Choose again!') col=int(input('X\'s choice: ')) self.addMove(col, 'O') print() if self.isWinFor('O'): print(self) print() print('O wins -- Congratulations!') self.clear() break if self.isFull(): print(self) print() print('End of game: no winner. Goodbye!') self.clear() break def playGameH(self, playerX): while not self.isFull(): self.addMove(playerX.nextMove(self),playerX.ox) print(self) if self.isWinFor(playerX.ox): print(playerX.ox, 'wins') return if self.isFull(): print(self) print() print('End of game: no winner. Goodbye!') self.clear() break col=int(input('Your choice: ')) while self.isMoveLegal(col) == False: print('Not a legal move! Choose again!') col=int(input('Your choice: ')) self.addMove(col, 'O') print() if self.isWinFor('O'): print(self) print() print('You win -- Congratulations!') self.clear() break if self.isFull(): print(self) print() print('End of game: no winner. Goodbye!') self.clear() break def playGameAI(self,playerX,playerO): while not self.isFull(): self.addMove(playerX.nextMove(self),playerX.ox) print(self) if self.isWinFor(playerX.ox): print(playerX.ox, 'wins.') return self.addMove(playerO.nextMove(self),playerO.ox) print(self) if self.isWinFor(playerO.ox): print(playerO.ox, 'wins.') return print('ERROR') return # BELOW IS THE PLAYER CLASS - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | # BELOW IS THE PLAYER CLASS - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | # BELOW IS THE PLAYER CLASS - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | class Player: """ An AI player for Connect Four """ def __init__(self, ox, tiebreakingType, ply): """ Constructs a Player object. """ assert(ox == 'X' or ox == 'O') assert(tiebreakingType == "LEFT" or tiebreakingType == "RIGHT" or tiebreakingType == "RANDOM") assert(ply >= 0) self.ox = ox self.tiebreakingType = tiebreakingType self.ply = ply def __repr__(self): """ Creates a string representation of the Player object. """ s = "Player for " + self.ox + "\n" s += " with tiebreak type: " + self.tiebreakingType + "\n" s += " and ply == " + str(self.ply) + "\n\n" return s def opponentChecker(self): """ Returns the piece the player's opponent is using. """ if self.ox == 'X': return 'O' else: return 'X' def scoreBoard(self, board): """ Returns the score of a board for a given player. """ if board.isWinFor(self.ox): return 100.0 elif board.isWinFor(self.opponentChecker()): return 0.0 else: return 50.0 def tiebreakMove(self, scores): """ Returns the column number of the best move according the column scores and the player's tiebreak method. """ maxScore = max(scores) playCol = 0 if self.tiebreakingType == 'LEFT': for i in range(len(scores)): if scores[i] == maxScore: playCol = i break elif self.tiebreakingType == 'RIGHT': for i in range(len(scores)): if scores[len(scores)-i-1] == maxScore: playCol = len(scores)-i-1 break else: while True: playCol = random.choice(range(len(scores))) if scores[playCol] == maxScore: break return playCol def scoresFor(self, board): """ """ scores = [50.0]*board.width for col in range(board.width): if not board.isMoveLegal(col): scores[col] = -1.0 elif board.isWinFor('X') or board.isWinFor('O'): scores[col] = self.scoreBoard(board) elif self.ply == 0: scores[col] = 50.0 else: board.addMove(col,self.ox) amy = Player(self.opponentChecker(), self.tiebreakingType, self.ply-1) amyScores = amy.scoresFor(board) scores[col] = 100.0-max(amyScores) board.deleteMove(col, self.ox) return scores def nextMove(self, board): ''' Returns next move with the given board''' return self.tiebreakMove(self.scoresFor(board))