Advertisement
Guest User

Untitled

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