nim.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. """ turtle-example-suite:
  2. tdemo_nim.py
  3. Play nim against the computer. The player
  4. who takes the last stick is the winner.
  5. Implements the model-view-controller
  6. design pattern.
  7. """
  8. import turtle
  9. import random
  10. import time
  11. SCREENWIDTH = 640
  12. SCREENHEIGHT = 480
  13. MINSTICKS = 7
  14. MAXSTICKS = 31
  15. HUNIT = SCREENHEIGHT // 12
  16. WUNIT = SCREENWIDTH // ((MAXSTICKS // 5) * 11 + (MAXSTICKS % 5) * 2)
  17. SCOLOR = (63, 63, 31)
  18. HCOLOR = (255, 204, 204)
  19. COLOR = (204, 204, 255)
  20. def randomrow():
  21. return random.randint(MINSTICKS, MAXSTICKS)
  22. def computerzug(state):
  23. xored = state[0] ^ state[1] ^ state[2]
  24. if xored == 0:
  25. return randommove(state)
  26. for z in range(3):
  27. s = state[z] ^ xored
  28. if s <= state[z]:
  29. move = (z, s)
  30. return move
  31. def randommove(state):
  32. m = max(state)
  33. while True:
  34. z = random.randint(0,2)
  35. if state[z] > (m > 1):
  36. break
  37. rand = random.randint(m > 1, state[z]-1)
  38. return z, rand
  39. class NimModel(object):
  40. def __init__(self, game):
  41. self.game = game
  42. def setup(self):
  43. if self.game.state not in [Nim.CREATED, Nim.OVER]:
  44. return
  45. self.sticks = [randomrow(), randomrow(), randomrow()]
  46. self.player = 0
  47. self.winner = None
  48. self.game.view.setup()
  49. self.game.state = Nim.RUNNING
  50. def move(self, row, col):
  51. maxspalte = self.sticks[row]
  52. self.sticks[row] = col
  53. self.game.view.notify_move(row, col, maxspalte, self.player)
  54. if self.game_over():
  55. self.game.state = Nim.OVER
  56. self.winner = self.player
  57. self.game.view.notify_over()
  58. elif self.player == 0:
  59. self.player = 1
  60. row, col = computerzug(self.sticks)
  61. self.move(row, col)
  62. self.player = 0
  63. def game_over(self):
  64. return self.sticks == [0, 0, 0]
  65. def notify_move(self, row, col):
  66. if self.sticks[row] <= col:
  67. return
  68. self.move(row, col)
  69. class Stick(turtle.Turtle):
  70. def __init__(self, row, col, game):
  71. turtle.Turtle.__init__(self, visible=False)
  72. self.row = row
  73. self.col = col
  74. self.game = game
  75. x, y = self.coords(row, col)
  76. self.shape("square")
  77. self.shapesize(HUNIT/10.0, WUNIT/20.0)
  78. self.speed(0)
  79. self.pu()
  80. self.goto(x,y)
  81. self.color("white")
  82. self.showturtle()
  83. def coords(self, row, col):
  84. packet, remainder = divmod(col, 5)
  85. x = (3 + 11 * packet + 2 * remainder) * WUNIT
  86. y = (2 + 3 * row) * HUNIT
  87. return x - SCREENWIDTH // 2 + WUNIT // 2, SCREENHEIGHT // 2 - y - HUNIT // 2
  88. def makemove(self, x, y):
  89. if self.game.state != Nim.RUNNING:
  90. return
  91. self.game.controller.notify_move(self.row, self.col)
  92. class NimView(object):
  93. def __init__(self, game):
  94. self.game = game
  95. self.screen = game.screen
  96. self.model = game.model
  97. self.screen.colormode(255)
  98. self.screen.tracer(False)
  99. self.screen.bgcolor((240, 240, 255))
  100. self.writer = turtle.Turtle(visible=False)
  101. self.writer.pu()
  102. self.writer.speed(0)
  103. self.sticks = {}
  104. for row in range(3):
  105. for col in range(MAXSTICKS):
  106. self.sticks[(row, col)] = Stick(row, col, game)
  107. self.display("... a moment please ...")
  108. self.screen.tracer(True)
  109. def display(self, msg1, msg2=None):
  110. self.screen.tracer(False)
  111. self.writer.clear()
  112. if msg2 is not None:
  113. self.writer.goto(0, - SCREENHEIGHT // 2 + 48)
  114. self.writer.pencolor("red")
  115. self.writer.write(msg2, align="center", font=("Courier",18,"bold"))
  116. self.writer.goto(0, - SCREENHEIGHT // 2 + 20)
  117. self.writer.pencolor("black")
  118. self.writer.write(msg1, align="center", font=("Courier",14,"bold"))
  119. self.screen.tracer(True)
  120. def setup(self):
  121. self.screen.tracer(False)
  122. for row in range(3):
  123. for col in range(self.model.sticks[row]):
  124. self.sticks[(row, col)].color(SCOLOR)
  125. for row in range(3):
  126. for col in range(self.model.sticks[row], MAXSTICKS):
  127. self.sticks[(row, col)].color("white")
  128. self.display("Your turn! Click leftmost stick to remove.")
  129. self.screen.tracer(True)
  130. def notify_move(self, row, col, maxspalte, player):
  131. if player == 0:
  132. farbe = HCOLOR
  133. for s in range(col, maxspalte):
  134. self.sticks[(row, s)].color(farbe)
  135. else:
  136. self.display(" ... thinking ... ")
  137. time.sleep(0.5)
  138. self.display(" ... thinking ... aaah ...")
  139. farbe = COLOR
  140. for s in range(maxspalte-1, col-1, -1):
  141. time.sleep(0.2)
  142. self.sticks[(row, s)].color(farbe)
  143. self.display("Your turn! Click leftmost stick to remove.")
  144. def notify_over(self):
  145. if self.game.model.winner == 0:
  146. msg2 = "Congrats. You're the winner!!!"
  147. else:
  148. msg2 = "Sorry, the computer is the winner."
  149. self.display("To play again press space bar. To leave press ESC.", msg2)
  150. def clear(self):
  151. if self.game.state == Nim.OVER:
  152. self.screen.clear()
  153. class NimController(object):
  154. def __init__(self, game):
  155. self.game = game
  156. self.sticks = game.view.sticks
  157. self.BUSY = False
  158. for stick in self.sticks.values():
  159. stick.onclick(stick.makemove)
  160. self.game.screen.onkey(self.game.model.setup, "space")
  161. self.game.screen.onkey(self.game.view.clear, "Escape")
  162. self.game.view.display("Press space bar to start game")
  163. self.game.screen.listen()
  164. def notify_move(self, row, col):
  165. if self.BUSY:
  166. return
  167. self.BUSY = True
  168. self.game.model.notify_move(row, col)
  169. self.BUSY = False
  170. class Nim(object):
  171. CREATED = 0
  172. RUNNING = 1
  173. OVER = 2
  174. def __init__(self, screen):
  175. self.state = Nim.CREATED
  176. self.screen = screen
  177. self.model = NimModel(self)
  178. self.view = NimView(self)
  179. self.controller = NimController(self)
  180. def main():
  181. mainscreen = turtle.Screen()
  182. mainscreen.mode("standard")
  183. mainscreen.setup(SCREENWIDTH, SCREENHEIGHT)
  184. nim = Nim(mainscreen)
  185. return "EVENTLOOP"
  186. if __name__ == "__main__":
  187. main()
  188. turtle.mainloop()