Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import random
- from tkinter import *
- class Connect4:
- def __init__(self, rowCount, colCount, circleSize, window):
- self.margin = 25
- self.rowCount = rowCount
- self.colCount = colCount
- self.circleSize = circleSize
- self.circleSpacing = 10
- self.messageSize = 15
- self.width = self.colCount * (self.circleSize + self.circleSpacing)
- self.height = self.rowCount * (self.circleSize + self.circleSpacing) + self.messageSize*4
- self.window = window
- self.data = []
- self.frame = Frame(window)
- self.frame.pack()
- self.diameter = self.circleSize #self.rowCount * self.colCount
- self.initialColor = 'white'
- self.resetButton = Button(self.frame, text='Restart', command=self.restartGame)
- self.resetButton.place(x=340, y=0)
- self.quitButton = Button(self.frame, text='Quit', command=self.quitGame)
- self.quitButton.pack(side=TOP)
- self.draw = Canvas(self.frame, height=self.height, width=self.width)
- self.draw.bind('<Button-1>', self.mouseInput)
- self.circles = []
- self.colors = []
- self.ox = 'X'
- y = self.circleSpacing
- for row in range(self.rowCount):
- circleRow = []
- colorRow = []
- x = self.circleSpacing
- for col in range(self.colCount):
- circleRow += [self.draw.create_oval(x, y, x + self.diameter, y + self.diameter, fill=self.initialColor)]
- colorRow += [self.initialColor]
- x += self.diameter + self.circleSpacing
- self.circles += [circleRow]
- self.colors += [colorRow]
- y += self.diameter + self.circleSpacing
- self.message = self.draw.create_text(self.messageSize, self.height-self.messageSize, text=f'{self.ox}\'s turn!', anchor='w', font='Courier 27')
- for row in range(self.rowCount):
- boardRow = []
- for col in range(self.colCount):
- boardRow += [' ']
- self.data += [boardRow]
- self.draw.pack(padx = self.margin, pady = self.margin)
- 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 mouseInput(self, event):
- col = int(event.x/(self.diameter+self.circleSpacing))
- self.hostGame(aiPlayer)
- self.updateBoard(col)
- def quitGame(self):
- self.window.destroy()
- def restartGame(self):
- self.clear()
- for row in range(self.rowCount):
- for col in range(self.colCount):
- self.draw.itemconfig(self.circles[row][col], fill='white')
- def clear(self): # Clears the current state of the board
- self.ox = ' '
- for row in range(self.height):
- for col in range(self.width): # For every value in the board
- self.placeChecker(row, col, 'white')
- self.data[row][col] = ' ' # Override whatever is there with a space (the original value each spot starts with)
- self.ox = 'X'
- def setChecker(self, row, col, color): # Where the user clicked
- self.data[row][col] = self.ox
- self.draw.itemconfig(self.circles[row][col], fill=color)
- def setText(self, text):
- self.draw.itemconfig(self.message, text=text)
- def lowestRow(self, col):
- if self.allowsMove(col):
- for row in range(self.rowCount):
- if self.data[row][col] != ' ': # If the spot is not empty...
- return row - 1
- return self.rowCount - 1
- def hostGame(self, aiPlayer):
- if self.winsFor(self.ox):
- # The move they just did let them win
- self.setText(f'{self.ox} won!')
- else:
- O = aiPlayer.nextMove(self.data)
- self.placeChecker(O, color='black')
- self.addMove(O, self.ox)
- #self.ox = 'O' if self.ox == 'X' else 'X'
- self.setText(f'{self.ox}\'s turn!')
- def placeChecker(self, col, color = None):
- if color is None:
- color = 'red' if self.ox == 'X' else 'black'
- row = self.lowestRow(col)
- self.setChecker(row, col, color)
- def updateBoard(self, col): # Adds a "checker" to the column passed
- self.placeChecker(col)
- self.addMove(col, self.ox)
- def addMove(self, col, ox):
- if self.allowsMove(col): # Uses allowsMove to check if the column is available
- for row in range(self.rowCount):
- 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.rowCount-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.colCount:
- 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.rowCount): # 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.rowCount): # Horizontal check
- for col in range(self.colCount - 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.rowCount - 3): # Vertical check
- for col in range(self.colCount):
- 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.rowCount - 3): # SW>NE check
- for col in range(self.colCount - 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.rowCount): # SE>NW check
- for col in range(self.colCount - 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(b)
- 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(b)
- O = aiPlayer.nextMove()
- 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
- for col in range(len(scores)):
- if scores[col] == max(scores):
- 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):
- score = [50]*7 # 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(7): # For each column...
- if b.allowsMove(col) == False: # Base Case, if the column is full it should always have the value of -1
- score[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)
- score[col] = self.scoreBoard(b)
- elif self.ply == 0: # Base Case, ply 0 will always value every column (except full columns) at 50
- score[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
- score[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
- score[col] = 100 - max(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.
- b.delMove(col) # Delete the move from every column after done evaluating
- return score # Return the best decision(s)
- def nextMove(self, b):
- score = self.scoresFor(b, self.ox, self.ply)
- return self.tieBreakType(score)
- root = Tk()
- root.title('Connect 4')
- game = Connect4(6, 7, 100, root)
- aiPlayer = Player('O', 'Random', 3)
- game.hostGame(aiPlayer)
- root.mainloop()
- def main():
- pass
- if __name__ == '__main__':
- main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement