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