"""A new version of Daleks, based on the actual rules of the game: - The board is 21x21 - There are 5 Daleks per level - A sonic screwdriver kills all adjacent Daleks for double score - You get one sonic screwdriver per level - Score: 10 per Dalek killed; 20 per Dalek killed by screwdriver There is a score list (maintained by a CGI script) """ import random WIN = 'WIN' DIE = 'DIE' DSCORE = 10 SSCORE = 20 class Piece: def __init__(self, loc, game): self.setloc(loc) def getloc(self): return self.loc def setloc(self, loc): self.loc = loc def destroy(self): pass def is_dustpile(self): return 0 def is_dalek(self): return 0 def is_player(elf): return 0 class Dalek(Piece): def is_dalek(self): return 1 class Player(Piece): def is_player(self): return 1 class Dustpile(Piece): def is_dustpile(self): return 1 class DaleksGame: DalekClass = Dalek PlayerClass = Player DustpileClass = Dustpile dscore = DSCORE sscore = SSCORE def __init__(self, cols=0, rows=0, number=5, width=0, height=0): # Static state # (Hack around param name change and b/w compatibility) self.width = cols or width or 21 # Width of board self.height = rows or height or 21 # Height of the board self.number = number # Number of Daleks per level self.setup_board() self.restart("Click or Space to start") def setup_board(self): pass def cleanup(self): self.board = {} for d in self.daleks: d.destroy() self.daleks = [] for dp in self.dustpiles: dp.destroy() self.dustpiles = [] if self.player: self.player.destroy() self.player = None def restart(self, message=""): self.score = 0 self.level = 1 self.screwdrivers = 1 self.setup_level(message) def new_level(self, message=""): self.level = self.level + 1 self.screwdrivers = self.screwdrivers + 1 self.setup_level(message) def setup_level(self, message=""): ndaleks = self.level * self.number self.daleks = [] self.dustpiles = [] self.board = {} for i in range(ndaleks): loc = self.random_empty_loc() dalek = self.DalekClass(loc, self) self.daleks.append(dalek) self.board[loc] = dalek loc = self.random_empty_loc() self.board[loc] = self.player = self.PlayerClass(loc, self) def teleport(self): loc = self.random_empty_loc() return self.move(loc) def screwdriver(self): if self.screwdrivers <= 0: raise RuntimeError, "out of screwdrivers" self.screwdrivers = self.screwdrivers - 1 loc = self.player.getloc() x, y = loc for dx in range(x-1, x+2): for dy in range(y-1, y+2): dloc = (dx, dy) if not self.empty(dloc): it = self.board[dloc] if it.is_dalek(): del self.board[dloc] self.daleks.remove(it) it.destroy() self.score = self.score + self.sscore def move(self, loc): # Player moves to loc to any empty loc; daleks move in response # Return: # WIN -- Player wins # DIE -- Player dies # None -- Game continues x, y = loc # Check for valid move if not (0 <= x < self.width and 0 <= y < self.height): raise IndexError, "loc %s is outside board" % str(loc) if loc != self.player.getloc() and not self.empty(loc): raise IndexError, "loc %s is occupied" % str(loc) # Move the player self.player.setloc(loc) # Create a new board, initially filled with the player and dustpiles self.board = {loc: self.player} for dp in self.dustpiles: self.board[dp.getloc()] = dp # Move all daleks to their new location, # turning them into dustpiles or killing the player if necessary, # and adding to the score for this move. # The score only counts if the player doesn't get killed. # For consistency, if more than one dalek kills the player, # they turn into dustpiles. score = 0 for dalek in self.daleks[:]: dx, dy = dalek.getloc() if x < dx: dx = dx-1 elif dx < x: dx = dx+1 if y < dy: dy = dy-1 elif y > dy: dy = dy+1 dloc = dx, dy if self.empty(dloc): # New, empty location dalek.setloc(dloc) self.board[dloc] = dalek continue # Hit something -- what is it? it = self.board[dloc] if it.is_dustpile(): # Hit a dustpile -- die self.daleks.remove(dalek) dalek.destroy() score = score + self.dscore continue if it.is_dalek(): # Hit another dalek -- kill it and die del self.board[dloc] self.daleks.remove(dalek) self.daleks.remove(it) dalek.destroy() it.destroy() score = score + 2*self.dscore # Surrect a dustpile in our place dp = self.DustpileClass(dloc, self) self.dustpiles.append(dp) self.board[dloc] = dp continue if it.is_player(): # Killed the player! if it is not self.player: raise RuntimeError, "hit another player??!!??" self.player = None del self.board[dloc] it.destroy() continue # What is it??? raise RuntimeError, "hit a mysterious object??!!??" # Time to decide the outcome if not self.player: return DIE self.score = self.score + score if not self.daleks: return WIN else: return None def random_empty_loc(self): while 1: x = random.randint(0, self.width-1) y = random.randint(0, self.height-1) loc = (x, y) if self.empty(loc): return loc def empty(self, loc): return not self.board.has_key(loc) from Tkinter import * import string # For Grail 0.1 GROOVE = 'groove' LEFT = 'left' RIGHT = 'right' PW = 26 # Piece width PH = 26 # Piece height SCORE_URL = "http://monty.cnri.reston.va.us/cgi-bin/dalekscore.py" class TkPiece: bitmap = "" def __init__(self, loc, game): self.game = game self.loc = loc x, y = self.game.transform(self.loc) self.tag = self.game.canvas.create_bitmap(x, y, bitmap=self.bitmap) def setloc(self, loc): self.loc = loc x, y = self.game.transform(loc) self.game.canvas.coords(self.tag, x, y) def destroy(self): self.game.canvas.delete(self.tag) class TkDalek(TkPiece, Dalek): bitmap = "hourglass" class TkPlayer(TkPiece, Player): bitmap = "questhead" class TkDustpile(TkPiece, Dustpile): bitmap = "info" class TkDaleksGame(DaleksGame): DalekClass = TkDalek PlayerClass = TkPlayer DustpileClass = TkDustpile def __init__(self, parent, minscore=250, **kw): self.parent = parent self.templabel = None self.minscore = minscore apply(DaleksGame.__init__,(self,), kw) self.show_score() def setup_board(self): width = self.width * PW height = self.height * PH self.canvas = Canvas(self.parent, width=width, height=height, borderwidth=2, relief=GROOVE) self.canvas.pack() self.canvas.bind('<1>', self.click) self.score_label = Label(self.parent) self.score_label.pack(side=LEFT) self.teleport_button = Button(self.parent, text="Teleport (t)", command = self.teleport_command) self.teleport_button.pack(side=LEFT) self.screwdriver_label = Label(self.parent) self.screwdriver_label.pack(side=RIGHT) self.screwdriver_button = Button(self.parent, text="Screwdriver (s)", command = self.screwdriver_command) self.screwdriver_button.pack(side=RIGHT) self.canvas.bind("", self.teleport_command) self.canvas.bind("", self.teleport_command) self.canvas.bind("", self.screwdriver_command) self.canvas.bind("", self.screwdriver_command) self.canvas.bind("", self.finish_command) for key in self.directions.keys(): try: self.canvas.bind(""%key, self.arrow_key) except TclError: pass self.canvas.focus_set() directions = { # 'Keysym' : (dx, dy) 'KP_Home' : (-1, -1), 'KP_Up' : ( 0, -1), 'KP_Prior' : ( 1, -1), 'KP_Left' : (-1, 0), 'KP_Begin' : ( 0, 0), 'KP_Right' : ( 1, 0), 'KP_End' : (-1, 1), 'KP_Down' : ( 0, 1), 'KP_Next' : ( 1, 1), } def arrow_key(self, event): try: dx, dy = self.directions[event.keysym] except KeyError: return x, y = loc = self.player.getloc() if 0 <= x+dx < self.width: x = x+dx if 0 <= y+dy < self.width: y = y+dy newloc = (x, y) if newloc != loc and not self.empty(newloc): self.parent.bell() return self.play(newloc) def finish_command(self, event=None): loc = self.player.getloc() while 1: if self.play(loc): break def teleport_command(self, event=None): if not self.player: self.parent.bell() return loc = self.random_empty_loc() self.play(loc) def screwdriver_command(self, event=None): if self.screwdrivers <= 0 or not self.player: self.parent.bell() return self.screwdriver() self.play(self.player.getloc()) def click(self, event): self.canvas.focus_set() if not self.player: self.parent.bell() return mx, my = event.x/PW, event.y/PH x, y = loc = self.player.getloc() if x < mx < self.width: x = x+1 elif 0 <= mx < x: x = x-1 if y < my < self.height: y = y+1 elif 0 <= my < y: y = y-1 newloc = (x, y) if newloc != loc and not self.empty(newloc): self.parent.bell() return self.play(newloc) def play(self, loc): result = self.move(loc) if result: self.cleanup() if result == WIN: self.new_level( "You win this level!\nClick or Space to continue") elif result == DIE: if self.record_score(): return self.restart("You die!\nClick or Space to restart") self.show_score() return result def record_score(self): if self.score <= self.minscore: return self.show_message( "You die!\nPlease wait while I record your score...", 0) user = getenv("USER") or getenv("LOGNAME") or "unknown" record = "?score=%d&level=%d&width=%d&height=%d&number=%d&user=%s" % ( self.score, self.level, self.width, self.height, self.number, user) url = SCORE_URL + record import urllib f = urllib.urlopen(url) lines = f.readlines() f.close() for line in lines: if line[:9] == "position=": pos = string.atoi(line[9:-1]) break else: pos = -1 if 1 <= pos <= 20: import Dialog dlg = Dialog.Dialog(self.parent, text="You hit the high score list!", title="High scores", bitmap='questhead', default=0, strings=("So what?", "Show me!"), ) if dlg.num == 1: url = SCORE_URL + "?position=%d&seed=%g" % ( pos, random.random()) self.parent.grail_browser.load(url) return 1 # I.e. don't continue playing def show_score(self): text = "Level: %d; Score: %d" % (self.level, self.score) self.score_label.config(text=text) text="(%d left)" % self.screwdrivers self.screwdriver_label.config(text=text) def transform(self, loc): x, y = loc x = x*PW + PW/2 + 2 y = y*PH + PH/2 + 2 return x, y def setup_level(self, message=""): self.show_message(message) DaleksGame.setup_level(self, message) def show_message(self, message="", click=1): if not self.templabel: self.templabel = Button(self.parent, text=message) else: self.templabel.config(text=message) if click: self.templabel.config(command=self.remove_message) self.templabel.bind("", self.remove_message) self.templabel.focus_set() else: self.templabel.config(command='') self.templabel.bind("", '') self.templabel.place(relx=0, rely=0, relwidth=1.0, relheight=1.0) self.templabel.update_idletasks() def remove_message(self, event=None): tl = self.templabel self.templabel = None if tl: tl.destroy() self.canvas.focus_set() daleks = TkDaleksGame # For applet usage def getenv(name): try: import os return os.environ[name] except (ImportError, KeyError): return None if __name__ == '__main__': # For stand-alone usage root = Tk() ##game = TkDaleksGame(root, width=10,height=10,number=3) game = TkDaleksGame(root) root.mainloop()