annotate CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/python3.8/turtledemo/nim.py @ 69:33d812a61356

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