Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import random
- class Connect4:
- def __init__(self, width, height, window=None): # Initializes the game by setting the parameters for the board and the data that will be stored in the board (or list of lists)
- self.width = width
- self.height = height
- self.data = []
- for row in range(self.height): # This for statement creates the list of lists represented by self.data
- boardRow = []
- for col in range(self.width):
- boardRow += [' ']
- self.data += [boardRow]
- def __repr__(self): # Sets the literal structure of the board so that it's easy to interpret for the players
- s = ''
- for row in range(self.height):
- s += '|'
- for col in range(self.width):
- s += self.data[row][col] + '|'
- s += '\n'
- s += '--'*self.width + '-\n'
- for col in range(self.width):
- s += ' ' + str(col%10)
- s += '\n'
- return s
- def clear(self): # Clears the current state of the board
- for row in range(self.height):
- for col in range(self.width): # For every value in the board
- self.data[row][col] = ' ' # Override whatever is there with a space (the original value each spot starts with)
- def addMove(self, col, ox): # Adds a "checker" to the column passed
- if self.allowsMove(col): # Uses allowsMove to check if the column is available
- for row in range(self.height):
- if self.data[row][col] != ' ': # If the spot is not empty...
- self.data[row-1][col] = ox # Add the checker to the spot above it
- return
- self.data[self.height-1][col] = ox # Otherwise but the checker there
- def allowsMove(self, col): # Tests to see if a column has room for another move
- if 0 <= col < self.width:
- return self.data[0][col] == ' '
- else:
- return False
- def delMove(self, col): # Deletes the last move played in column passed to the function
- for row in range(self.height): # For each row..
- if self.data[row][col] != ' ': # If the indexed spot is not empty
- self.data[row][col] = ' ' # Override whatever is there with a space
- return
- return
- def isFull(self): # Checks if the board is full or not
- for values in self.data: # For each value in the entire board, if there's a space anywhere then it's not full (False). If there isn't a space, then the board is full (True).
- if ' ' in values:
- return False
- return True
- def winsFor(self, ox): # Checks to see if a checker ('X' or 'O') won the Connect 4.
- for row in range(self.height): # Horizontal check
- for col in range(self.width - 3): # Uses an anchor point (subtracting 3 from the width and so forth) from where to start checking so it doesn't go out of range.
- if self.data[row][col] == ox and self.data[row][col+1] == ox and self.data[row][col+2] == ox and self.data[row][col+3] == ox:
- return True
- for row in range(self.height - 3): # Vertical check
- for col in range(self.width):
- if self.data[row][col] == ox and self.data[row+1][col] == ox and self.data[row+2][col] == ox and self.data[row+3][col] == ox:
- return True
- for row in range(self.height - 3): # SW>NE check
- for col in range(self.width - 3):
- if self.data[row][col] == ox and self.data[row+1][col+1] == ox and self.data[row+2][col+2] == ox and self.data[row+3][col+3] == ox:
- return True
- for row in range(3, self.height): # SE>NW check
- for col in range(self.width - 3):
- if self.data[row][col] == ox and self.data[row-1][col+1] == ox and self.data[row-2][col+2] == ox and self.data[row-3][col+3] == ox:
- return True
- return False
- def hostGame(self): # Makes the game more playable by asking the user their turn and checking for wins or ties automatically
- move = 0 # This is used later so it will rotate back and forth between asking X to move and asking O to move
- while self.isFull() == False and self.winsFor('OX') != True: # While the board is not full and nobody has won yet, continue the loop
- if move == 0: # When move is 0, it's X's turn (X always goes first)
- X = int(input('Player "X", what column would you like to move? ')) # Asks for the user's input for which column to move, then turns it into an integer and stores it in X
- if self.allowsMove(X): # If that position is allowed...
- self.addMove(X, 'X') # Add that move for 'X'
- move += 1 # Increment move by 1 so that move won't be 0, and thus it will be O's turn
- if self.winsFor('X'): # Check to see if X won the game with that move
- print('Congratulations Player "X", you win!')
- return
- else:
- while self.allowsMove(X) == False: # While the user keeps putting invalid columns...
- X = int(input('Oops! Your response was invalid, try again: ')) # Keep asking until the user chooses a column that is allowed
- self.addMove(X, 'X')
- move += 1
- if self.winsFor('X'):
- print('Congratulations Player "X", you win!')
- return
- else: # O's turn to play, same mechanics just for a different player
- O = int(input('Player "O", what column would you like to move? '))
- if self.allowsMove(O):
- self.addMove(O, 'O')
- move -= 1 # Decrements move so it will be 0 and thus will be X's turn again
- if self.winsFor('O'):
- print('Congratulations Player "O", you win!')
- return
- else:
- while self.allowsMove(O) == False:
- O = int(input('Oops! Your response was invalid, try again: '))
- move -= 1
- self.addMove(O, 'O')
- if self.winsFor('O'):
- print('Congratulations Player "O", you win!')
- return
- print('It\'s a tie!') # If it breaks out of the loop and someone hasn't already won, then that mean the board must be full and thus would be a tie
- return
- def playGameWith(self, aiPlayer):
- move = 0 # This is used later so it will rotate back and forth between asking X to move and asking O to move
- while self.isFull() == False and self.winsFor('OX') != True: # While the board is not full and nobody has won yet, continue the loop
- if move == 0: # When move is 0, it's X's turn (X always goes first)
- print(board)
- X = int(input('Player "X", what column would you like to move? ')) # Asks for the user's input for which column to move, then turns it into an integer and stores it in X
- if self.allowsMove(X): # If that position is allowed...
- self.addMove(X, 'X') # Add that move for 'X'
- move += 1 # Increment move by 1 so that move won't be 0, and thus it will be O's turn
- if self.winsFor('X'): # Check to see if X won the game with that move
- print('Congratulations Player "X", you win!')
- return
- else:
- while self.allowsMove(X) == False: # While the user keeps putting invalid columns...
- X = int(input('Oops! Your response was invalid, try again: ')) # Keep asking until the user chooses a column that is allowed
- self.addMove(X, 'X')
- move += 1
- if self.winsFor('X'):
- print('Congratulations Player "X", you win!')
- return
- else: # O's turn to play, same mechanics just for a different player
- print(board)
- O = aiPlayer.nextMove(self)
- self.addMove(O, 'O')
- move -= 1 # Decrements move so it will be 0 and thus will be X's turn again
- if self.winsFor('O'):
- print('Congratulations Player "O", you win!')
- return
- print('It\'s a tie!') # If it breaks out of the loop and someone hasn't already won, then that mean the board must be full and thus would be a tie
- return
- class Player:
- def __init__(self, ox, tbt, ply):
- self.ox = ox
- self.tbt = tbt
- self.ply = ply
- def __repr__(self):
- intro = 'Tiebreak Type' + self.tbt
- intro += '\n' + 'Ply:' + str(self.ply)
- return intro
- def opponent(self):
- if self.ox == 'X':
- return 'O'
- else:
- return 'X'
- def scoreBoard(self, b):
- if b.winsFor(self.opponent()) == True:
- return 0
- elif b.winsFor(self.ox) == True:
- return 100
- else:
- return 50
- def tieBreakType(self, scores):
- eachCol = []
- t = 0
- scoreMax = max(scores)
- for col in range(len(scores)):
- if scores[col] == scoreMax:
- t += 1
- eachCol += [col]
- if t > 1:
- if self.tbt == 'Random':
- return random.choice(eachCol)
- elif self.tbt == 'Right':
- return max(eachCol)
- elif self.tbt == 'Left':
- return min(eachCol)
- else:
- return eachCol[0]
- def scoresFor(self, b, ox, ply): # Evaluates each column value from scoreBoard and establishes difficulty (ply) through recursion
- colScore = [50]*b.width # Pre-seeds a list with the default value of 50, uses list multiplication to get the width of the board to determine how many columns are needed
- for col in range(b.width): # For each column...
- if b.allowsMove(col) == False: # Base Case, if the column is full it should always have the value of -1
- colScore[col] = -1
- elif b.winsFor(self.opponent()) or b.winsFor(self.ox): # Base Case, the column is either valued as a loss (0) or a win(100)
- colScore[col] = self.scoreBoard(b)
- elif self.ply == 0: # Base Case, ply 0 will always value every column (except full columns) at 50
- colScore[col] = 50
- elif self.ply > 0: # Evaluates higher plys through recursion
- b.addMove(col, self.ox) # Add a move to every column and evaluate
- if self.scoreBoard == 0 or self.scoreBoard == 100: # Evaluate if the scoreBoard is a loss or a win
- colScore[col] = self.scoreBoard(b)
- else:
- p = Player(self.opponent(), self.tbt, self.ply-1) # Sets up the parameters of the game, subtracts 1 from ply to avoid loop
- decision = p.scoresFor(b, self.ox, self.ply) # This is the recursive step that uses scoresFor to evaluate the board based on the opponent, tiebreak type, and ply number.
- colScore[col] = 100 - max(decision)
- b.delMove(col) # Delete the move from every column after done evaluating
- return colScore
- def nextMove(self, b):
- colScore = self.scoresFor(b, self.ox, self.ply)
- return self.tieBreakType(colScore)
- tbt = str(input("What is O's Tiebreak Type? "))
- ply = int(input("What is O's Ply number? "))
- board = Connect4(7, 6)
- print(board)
- playerO = Player('O', tbt, ply)
- board.playGameWith(playerO)
- def main():
- pass
- if __name__ == '__main__':
- main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement