mirror of
https://github.com/python/cpython.git
synced 2024-12-14 04:17:19 +08:00
8450c53315
Since a new demo can now be loaded while one is running, adjust loadfile to do the same cleanup as stopIt.
227 lines
6.4 KiB
Python
227 lines
6.4 KiB
Python
""" 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()
|