--- /dev/null
+""" turtle-example-suite:\r
+\r
+ tdemo_nim.py\r
+ \r
+Play nim against the computer. The player\r
+who takes the last stick is the winner.\r
+\r
+Implements the model-view-controller\r
+design pattern.\r
+"""\r
+\r
+\r
+import turtle\r
+import random\r
+import time\r
+\r
+SCREENWIDTH = 640\r
+SCREENHEIGHT = 480\r
+\r
+MINSTICKS = 7\r
+MAXSTICKS = 31\r
+\r
+HUNIT = SCREENHEIGHT // 12\r
+WUNIT = SCREENWIDTH // ((MAXSTICKS // 5) * 11 + (MAXSTICKS % 5) * 2)\r
+\r
+SCOLOR = (63, 63, 31)\r
+HCOLOR = (255, 204, 204)\r
+COLOR = (204, 204, 255)\r
+\r
+def randomrow():\r
+ return random.randint(MINSTICKS, MAXSTICKS)\r
+\r
+def computerzug(state):\r
+ xored = state[0] ^ state[1] ^ state[2]\r
+ if xored == 0:\r
+ return randommove(state)\r
+ for z in range(3):\r
+ s = state[z] ^ xored\r
+ if s <= state[z]:\r
+ move = (z, s)\r
+ return move\r
+\r
+def randommove(state):\r
+ m = max(state) \r
+ while True:\r
+ z = random.randint(0,2)\r
+ if state[z] > (m > 1):\r
+ break\r
+ rand = random.randint(m > 1, state[z]-1)\r
+ return z, rand\r
+\r
+\r
+class NimModel(object):\r
+ def __init__(self, game):\r
+ self.game = game\r
+\r
+ def setup(self):\r
+ if self.game.state not in [Nim.CREATED, Nim.OVER]:\r
+ return\r
+ self.sticks = [randomrow(), randomrow(), randomrow()]\r
+ self.player = 0\r
+ self.winner = None\r
+ self.game.view.setup()\r
+ self.game.state = Nim.RUNNING\r
+ \r
+ def move(self, row, col):\r
+ maxspalte = self.sticks[row]\r
+ self.sticks[row] = col\r
+ self.game.view.notify_move(row, col, maxspalte, self.player)\r
+ if self.game_over():\r
+ self.game.state = Nim.OVER\r
+ self.winner = self.player\r
+ self.game.view.notify_over()\r
+ elif self.player == 0:\r
+ self.player = 1\r
+ row, col = computerzug(self.sticks)\r
+ self.move(row, col)\r
+ self.player = 0\r
+ \r
+ def game_over(self):\r
+ return self.sticks == [0, 0, 0]\r
+\r
+ def notify_move(self, row, col):\r
+ if self.sticks[row] <= col:\r
+ return\r
+ self.move(row, col)\r
+\r
+\r
+class Stick(turtle.Turtle):\r
+ def __init__(self, row, col, game):\r
+ turtle.Turtle.__init__(self, visible=False)\r
+ self.row = row\r
+ self.col = col\r
+ self.game = game\r
+ x, y = self.coords(row, col)\r
+ self.shape("square")\r
+ self.shapesize(HUNIT/10.0, WUNIT/20.0)\r
+ self.speed(0)\r
+ self.pu()\r
+ self.goto(x,y)\r
+ self.color("white")\r
+ self.showturtle()\r
+ \r
+ def coords(self, row, col):\r
+ packet, remainder = divmod(col, 5)\r
+ x = (3 + 11 * packet + 2 * remainder) * WUNIT\r
+ y = (2 + 3 * row) * HUNIT\r
+ return x - SCREENWIDTH // 2 + WUNIT // 2, SCREENHEIGHT // 2 - y - HUNIT // 2\r
+ \r
+ def makemove(self, x, y):\r
+ if self.game.state != Nim.RUNNING:\r
+ return\r
+ self.game.controller.notify_move(self.row, self.col)\r
+\r
+\r
+class NimView(object):\r
+ def __init__(self, game):\r
+ self.game = game\r
+ self.screen = game.screen\r
+ self.model = game.model\r
+ self.screen.colormode(255)\r
+ self.screen.tracer(False)\r
+ self.screen.bgcolor((240, 240, 255))\r
+ self.writer = turtle.Turtle(visible=False)\r
+ self.writer.pu()\r
+ self.writer.speed(0)\r
+ self.sticks = {}\r
+ for row in range(3):\r
+ for col in range(MAXSTICKS):\r
+ self.sticks[(row, col)] = Stick(row, col, game)\r
+ self.display("... a moment please ...")\r
+ self.screen.tracer(True)\r
+\r
+ def display(self, msg1, msg2=None):\r
+ self.screen.tracer(False)\r
+ self.writer.clear()\r
+ if msg2 is not None:\r
+ self.writer.goto(0, - SCREENHEIGHT // 2 + 48)\r
+ self.writer.pencolor("red")\r
+ self.writer.write(msg2, align="center", font=("Courier",18,"bold"))\r
+ self.writer.goto(0, - SCREENHEIGHT // 2 + 20)\r
+ self.writer.pencolor("black")\r
+ self.writer.write(msg1, align="center", font=("Courier",14,"bold"))\r
+ self.screen.tracer(True)\r
+ \r
+\r
+ def setup(self):\r
+ self.screen.tracer(False)\r
+ for row in range(3):\r
+ for col in range(self.model.sticks[row]):\r
+ self.sticks[(row, col)].color(SCOLOR)\r
+ for row in range(3):\r
+ for col in range(self.model.sticks[row], MAXSTICKS):\r
+ self.sticks[(row, col)].color("white")\r
+ self.display("Your turn! Click leftmost stick to remove.")\r
+ self.screen.tracer(True)\r
+\r
+ def notify_move(self, row, col, maxspalte, player):\r
+ if player == 0:\r
+ farbe = HCOLOR\r
+ for s in range(col, maxspalte):\r
+ self.sticks[(row, s)].color(farbe)\r
+ else:\r
+ self.display(" ... thinking ... ")\r
+ time.sleep(0.5)\r
+ self.display(" ... thinking ... aaah ...")\r
+ farbe = COLOR\r
+ for s in range(maxspalte-1, col-1, -1):\r
+ time.sleep(0.2)\r
+ self.sticks[(row, s)].color(farbe)\r
+ self.display("Your turn! Click leftmost stick to remove.")\r
+\r
+ def notify_over(self):\r
+ if self.game.model.winner == 0:\r
+ msg2 = "Congrats. You're the winner!!!"\r
+ else:\r
+ msg2 = "Sorry, the computer is the winner."\r
+ self.display("To play again press space bar. To leave press ESC.", msg2)\r
+\r
+ def clear(self):\r
+ if self.game.state == Nim.OVER:\r
+ self.screen.clear()\r
+\r
+class NimController(object):\r
+\r
+ def __init__(self, game):\r
+ self.game = game\r
+ self.sticks = game.view.sticks\r
+ self.BUSY = False\r
+ for stick in self.sticks.values():\r
+ stick.onclick(stick.makemove)\r
+ self.game.screen.onkey(self.game.model.setup, "space")\r
+ self.game.screen.onkey(self.game.view.clear, "Escape")\r
+ self.game.view.display("Press space bar to start game")\r
+ self.game.screen.listen()\r
+\r
+ def notify_move(self, row, col):\r
+ if self.BUSY:\r
+ return\r
+ self.BUSY = True\r
+ self.game.model.notify_move(row, col)\r
+ self.BUSY = False\r
+ \r
+class Nim(object):\r
+ CREATED = 0\r
+ RUNNING = 1\r
+ OVER = 2\r
+ def __init__(self, screen):\r
+ self.state = Nim.CREATED \r
+ self.screen = screen\r
+ self.model = NimModel(self)\r
+ self.view = NimView(self)\r
+ self.controller = NimController(self)\r
+ \r
+\r
+mainscreen = turtle.Screen()\r
+mainscreen.mode("standard")\r
+mainscreen.setup(SCREENWIDTH, SCREENHEIGHT)\r
+\r
+def main():\r
+ nim = Nim(mainscreen)\r
+ return "EVENTLOOP!"\r
+\r
+if __name__ == "__main__":\r
+ main()\r
+ turtle.mainloop()\r
+ \r