Advertisement
Guest User

Untitled

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