Advertisement
Guest User

Untitled

a guest
Dec 8th, 2019
112
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 13.61 KB | None | 0 0
  1. import random
  2. from tkinter import *
  3.  
  4. class Connect4:
  5.     def __init__(self, rowCount, colCount, circleSize, window):
  6.         self.margin = 25
  7.         self.rowCount = rowCount
  8.         self.colCount = colCount
  9.         self.circleSize = circleSize
  10.         self.circleSpacing = 2
  11.         self.width = rowCount * circleSize + self.rowCount * (self.circleSpacing * 2)
  12.         self.height = colCount * circleSize + self.colCount * (self.circleSpacing * 2)
  13.         self.window = window
  14.         self.data = []
  15.         self.frame = Frame(window)
  16.         self.frame.pack()
  17.         self.messageSize = 15
  18.         self.diameter = circleSize
  19.         self.initialColor = 'white'
  20.         self.quitButton = Button(self.frame, text='Exit', command=self.quitGame)
  21.         self.quitButton.pack(side=TOP)
  22.         self.draw = Canvas(self.frame, height=self.height, width=self.width)
  23.         self.draw.bind('<Button-1>', self.mouseInput)
  24.         self.draw.pack(padx = self.margin, pady = self.margin)
  25.         self.circles = []
  26.         self.colors = []
  27.         y = 0
  28.         for row in range(self.rowCount):
  29.             circleRow = []
  30.             colorRow = []
  31.             x = 0
  32.             for col in range(self.colCount):
  33.                 circleRow += [self.draw.create_oval(x + self.circleSpacing, y + self.circleSpacing, x + self.diameter - self.circleSpacing, y + self.diameter - self.circleSpacing, fill=self.initialColor)]
  34.                 colorRow += [self.initialColor]
  35.                 x += self.diameter + self.circleSpacing*2
  36.             self.circles += [circleRow]
  37.             self.colors += [colorRow]
  38.             y += self.diameter + self.circleSpacing*2
  39.         self.message = self.draw.create_text(self.messageSize, self.height-self.messageSize, text='Welcome to Connect 4', anchor='w', font='Courier 24')
  40.         for row in range(self.rowCount):
  41.             boardRow = []
  42.             for col in range(self.colCount):
  43.                 boardRow += [' ']
  44.             self.data += [boardRow]
  45.  
  46.     def __repr__(self): # Sets the literal structure of the board so that it's easy to interpret for the players
  47.         s = ''
  48.         for row in range(self.height):
  49.             s += '|'
  50.             for col in range(self.width):
  51.                 s += self.data[row][col] + '|'
  52.             s += '\n'
  53.         s += '--'*self.width + '-\n'
  54.         for col in range(self.width):
  55.             s += ' ' + str(col%10)
  56.         s += '\n'
  57.         return s
  58.  
  59.     def mouseInput(self, event):
  60.         col = int(event.x/self.diameter)
  61.         row = int(event.y/self.diameter)
  62.         print('board[%s][%s]' % (row, col))
  63.         newColor = self.getNextColor(row, col)
  64.         self.draw.itemconfig(self.circles[row][col], fill=newColor)
  65.  
  66.     def getNextColor(self, row, col):
  67.         color = self.colors[row][col]
  68.         if color == 'white':
  69.             color = 'red'
  70.         elif color == 'red':
  71.             color = 'black'
  72.         else:
  73.             color = 'white'
  74.         self.colors[row][col] = color
  75.         return color
  76.    
  77.     def quitGame(self):
  78.         self.window.destroy()
  79.  
  80.     def clear(self): # Clears the current state of the board
  81.         for row in range(self.height):
  82.             for col in range(self.width): # For every value in the board
  83.                 self.data[row][col] = ' ' # Override whatever is there with a space (the original value each spot starts with)
  84.  
  85.     def addMove(self, col, ox): # Adds a "checker" to the column passed
  86.         if self.allowsMove(col): # Uses allowsMove to check if the column is available
  87.             for row in range(self.height):
  88.                 if self.data[row][col] != ' ': # If the spot is not empty...
  89.                     self.data[row-1][col] = ox # Add the checker to the spot above it
  90.                     return
  91.             self.data[self.height-1][col] = ox # Otherwise but the checker there
  92.  
  93.     def allowsMove(self, col): # Tests to see if a column has room for another move
  94.         if 0 <= col < self.width:
  95.             return self.data[0][col] == ' '
  96.         else:
  97.             return False
  98.  
  99.     def delMove(self, col): # Deletes the last move played in column passed to the function
  100.         for row in range(self.height): # For each row..
  101.             if self.data[row][col] != ' ': # If the indexed spot is not empty
  102.                 self.data[row][col] = ' ' # Override whatever is there with a space
  103.                 return
  104.         return
  105.  
  106.     def isFull(self): # Checks if the board is full or not
  107.         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).
  108.             if ' ' in values:
  109.                 return False
  110.         return True
  111.  
  112.     def winsFor(self, ox): # Checks to see if a checker ('X' or 'O') won the Connect 4.
  113.         for row in range(self.height): # Horizontal check
  114.             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.
  115.                 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:
  116.                     return True
  117.         for row in range(self.height - 3): # Vertical check
  118.             for col in range(self.width):
  119.                 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:
  120.                     return True
  121.         for row in range(self.height - 3): # SW>NE check
  122.             for col in range(self.width - 3):
  123.                 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:
  124.                     return True
  125.         for row in range(3, self.height): # SE>NW check
  126.             for col in range(self.width - 3):
  127.                 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:
  128.                     return True
  129.         return False
  130.  
  131.     def hostGame(self): # Makes the game more playable by asking the user their turn and checking for wins or ties automatically
  132.         move = 0 # This is used later so it will rotate back and forth between asking X to move and asking O to move
  133.         while self.isFull() == False and self.winsFor('OX') != True: # While the board is not full and nobody has won yet, continue the loop
  134.             if move == 0: # When move is 0, it's X's turn (X always goes first)
  135.                 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
  136.                 if self.allowsMove(X): # If that position is allowed...
  137.                     self.addMove(X, 'X') # Add that move for 'X'
  138.                     move += 1 # Increment move by 1 so that move won't be 0, and thus it will be O's turn
  139.                     if self.winsFor('X'): # Check to see if X won the game with that move
  140.                         print('Congratulations Player "X", you win!')
  141.                         return
  142.                 else:
  143.                     while self.allowsMove(X) == False: # While the user keeps putting invalid columns...
  144.                         X = int(input('Oops! Your response was invalid, try again: ')) # Keep asking until the user chooses a column that is allowed
  145.                     self.addMove(X, 'X')
  146.                     move += 1
  147.                     if self.winsFor('X'):
  148.                         print('Congratulations Player "X", you win!')
  149.                         return
  150.             else: # O's turn to play, same mechanics just for a different player
  151.                 O = int(input('Player "O", what column would you like to move? '))
  152.                 if self.allowsMove(O):
  153.                     self.addMove(O, 'O')
  154.                     move -= 1 # Decrements move so it will be 0 and thus will be X's turn again
  155.                     if self.winsFor('O'):
  156.                         print('Congratulations Player "O", you win!')
  157.                         return
  158.                 else:
  159.                     while self.allowsMove(O) == False:
  160.                         O = int(input('Oops! Your response was invalid, try again: '))
  161.                     move -= 1
  162.                     self.addMove(O, 'O')
  163.                     if self.winsFor('O'):
  164.                         print('Congratulations Player "O", you win!')
  165.                         return
  166.         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
  167.         return
  168.  
  169.     def playGameWith(self, aiPlayer):
  170.         move = 0 # This is used later so it will rotate back and forth between asking X to move and asking O to move
  171.         while self.isFull() == False and self.winsFor('OX') != True: # While the board is not full and nobody has won yet, continue the loop
  172.             if move == 0: # When move is 0, it's X's turn (X always goes first)
  173.                 print(b)
  174.                 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
  175.                 if self.allowsMove(X): # If that position is allowed...
  176.                     self.addMove(X, 'X') # Add that move for 'X'
  177.                     move += 1 # Increment move by 1 so that move won't be 0, and thus it will be O's turn
  178.                     if self.winsFor('X'): # Check to see if X won the game with that move
  179.                         print('Congratulations Player "X", you win!')
  180.                         return
  181.                 else:
  182.                     while self.allowsMove(X) == False: # While the user keeps putting invalid columns...
  183.                         X = int(input('Oops! Your response was invalid, try again: ')) # Keep asking until the user chooses a column that is allowed
  184.                     self.addMove(X, 'X')
  185.                     move += 1
  186.                     if self.winsFor('X'):
  187.                         print('Congratulations Player "X", you win!')
  188.                         return
  189.             else: # O's turn to play, same mechanics just for a different player
  190.                 print(b)
  191.                 O = aiPlayer.nextMove(self)
  192.                 self.addMove(O, 'O')
  193.                 move -= 1 # Decrements move so it will be 0 and thus will be X's turn again
  194.                 if self.winsFor('O'):
  195.                     print('Congratulations Player "O", you win!')
  196.                     return
  197.         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
  198.         return
  199.  
  200. class Player:
  201.     def __init__(self, ox, tbt, ply):
  202.         self.ox = ox
  203.         self.tbt = tbt
  204.         self.ply = ply
  205.  
  206.     def __repr__(self):
  207.         intro = 'Tiebreak Type' + self.tbt
  208.         intro += '\n' + 'Ply:' + str(self.ply)
  209.         return intro
  210.  
  211.     def opponent(self):
  212.         if self.ox == 'X':
  213.             return 'O'
  214.         else:
  215.             return 'X'
  216.  
  217.     def scoreBoard(self, b):
  218.         if b.winsFor(self.opponent()) == True:
  219.             return 0
  220.         elif b.winsFor(self.ox) == True:
  221.             return 100
  222.         else:
  223.             return 50
  224.  
  225.     def tieBreakType(self, scores):
  226.         eachCol = []
  227.         t = 0
  228.         for col in range(len(scores)):
  229.             if scores[col] == max(scores):
  230.                 t += 1
  231.                 eachCol += [col]
  232.         if t > 1:
  233.             if self.tbt == 'Random':
  234.                 return random.choice(eachCol)
  235.             elif self.tbt == 'Right':
  236.                 return max(eachCol)
  237.             elif self.tbt == 'Left':
  238.                 return min(eachCol)
  239.         else:
  240.             return eachCol[0]
  241.  
  242.     def scoresFor(self, b, ox, ply):
  243.         score = [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
  244.         for col in range(b.width): # For each column...
  245.             if b.allowsMove(col) == False: # Base Case, if the column is full it should always have the value of -1
  246.                 score[col] = -1
  247.             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)
  248.                 score[col] = self.scoreBoard(b)
  249.             elif self.ply == 0: # Base Case, ply 0 will always value every column (except full columns) at 50
  250.                 score[col] = 50
  251.             elif self.ply > 0: # Evaluates higher plys through recursion
  252.                 b.addMove(col, self.ox) # Add a move to every column and evaluate
  253.                 if self.scoreBoard == 0 or self.scoreBoard == 100: # Evaluate if the scoreBoard is a loss or a win
  254.                     score[col] = self.scoreBoard(b)
  255.                 else:
  256.                     p = Player(self.opponent(), self.tbt, self.ply-1) # Sets up the parameters of the game, subtracts 1 from ply to avoid loop
  257.                     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.
  258.                 b.delMove(col) # Delete the move from every column after done evaluating
  259.         return score # Return the best decision(s)
  260.  
  261.     def nextMove(self, b):
  262.         score = self.scoresFor(b, self.ox, self.ply)
  263.         return self.tieBreakType(score)
  264.  
  265. root = Tk()
  266. root.title('Connect 4')
  267. myScreen = Connect4(6, 7, 100, root)
  268. root.mainloop()
  269.  
  270. def main():
  271.     pass
  272.  
  273. if __name__ == '__main__':
  274.     main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement