annotate CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/python3.8/turtledemo/nim.py @ 68:5028fdace37b

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