from __future__ import generators import os, math, random import images, gamesrv from images import ActiveSprite from boards import CELL, HALFCELL, bget from mnstrmap import GreenAndBlue, Ghost from bonuses import Bonus LocalDir = os.path.basename(os.path.dirname(__file__)) localmap = { ('pac-lg', -1,0) : ('image1.ppm', ( 0, 0, 32, 32)), ('pac-sm', -1,0) : ('image1.ppm', (32, 0, 32, 32)), ('pac-lg', 0,-1) : ('image1.ppm', ( 0, 32, 32, 32)), ('pac-sm', 0,-1) : ('image1.ppm', (32, 32, 32, 32)), ('pac-lg', 1,0) : ('image1.ppm', ( 0, 64, 32, 32)), ('pac-sm', 1,0) : ('image1.ppm', (32, 64, 32, 32)), ('pac-lg', 0,1) : ('image1.ppm', ( 0, 96, 32, 32)), ('pac-sm', 0,1) : ('image1.ppm', (32, 96, 32, 32)), 'pac-black' : ('image2.ppm', ( 0, 0, 32, 32)), 'pac-dot' : ('image2.ppm', ( 0, 32, 8, 8)), } music = gamesrv.getmusic(os.path.join(LocalDir, 'music.wav')) class PacSprite(ActiveSprite): def __init__(self, ico, x, y): import boards if y < -2*CELL: y = -2*CELL elif y > boards.bheight: y = boards.bheight x = (x+HALFCELL) & -CELL y = (y+HALFCELL) & -CELL ActiveSprite.__init__(self, ico, x, y) self.wannadx = self.wannady = 0 def moving(self): import boards dx = dy = 0 turned = None was_clear = 0 while 1: if dx or dy: frontx = self.x+CELL+dx*(CELL+1) fronty = self.y+CELL+dy*(CELL+1) clear = (bget((frontx+dy)//CELL, (fronty-dx)//CELL) == ' ' and bget((frontx-dy)//CELL, (fronty+dx)//CELL) == ' ') if clear: blocked = 0 else: blocked = (was_clear or (self.x<=2*CELL and dx<0) or (self.x>=boards.bwidth-4*CELL and dx>0)) else: blocked = 1 if blocked: if turned: dx, dy = turned turned = None continue self.lastmove = None else: if turned: self.resetimages(dx, dy) turned = None self.lastmove = dx, dy self.step(2*dx, 2*dy) self.vertical_warp() was_clear = clear yield None if self.wannadx != dx or self.wannady != dy: if ((self.wannadx and not (self.y % CELL)) or (self.wannady and not (self.x % CELL))): turned = dx, dy dx = self.wannadx dy = self.wannady class Pac(PacSprite): no_hat = 1 def __init__(self, pacman, bubber, x, y, dcap): ico = GreenAndBlue.normal_bubbles[bubber.pn][1] PacSprite.__init__(self, images.sprget(('eyes', 0, 0)), x, y) self.bubble = ActiveSprite(images.sprget(ico), x, y) self.bubber = bubber self.pacman = pacman self.ready = 0 self.gen.append(self.playing()) self.pacman.pacs.append(self) self.dcap = dcap def resetimages(self, dx, dy): self.ready = 1 self.setimages(self.cyclic([('pac-lg', dx, dy), 'pac-black', ('pac-sm', dx, dy)], 5)) def to_front(self): self.bubble.to_front() ActiveSprite.to_front(self) def kill(self): from bubbles import Bubble self.play(images.Snd.Pop) self.bubble.gen = [self.bubble.die(Bubble.exploding_bubbles)] self.pacman.latestposition[self.bubber] = self.x, self.y try: self.bubber.dragons.remove(self) except ValueError: pass try: self.pacman.pacs.remove(self) except ValueError: pass ActiveSprite.kill(self) def playing(self): bubber = self.bubber for t in self.moving(): if self.pacman.ready: d = [(bubber.key_left, -1, 0), (bubber.key_right, 1, 0), (bubber.key_jump, 0,-1), (bubber.key_fire, 0, 1)] d.sort() if d[-1][0] > d[-2][0]: self.wannadx, self.wannady = d[-1][1:] self.bubble.move(self.x, self.y) yield None if self.ready: touching = images.touching(self.x+CELL-3, self.y+CELL-3, 6, 6) touching.reverse() for s in touching: if isinstance(s, Bonus): s.touched(self) elif isinstance(s, PacGhost): self.kill() return class PacGhost(PacSprite): def __init__(self, pacman, x, y): left = random.random() < 0.5 if left: ico = Ghost.left[0] else: ico = Ghost.right[0] PacSprite.__init__(self, images.sprget(ico), x, y) self.pacman = pacman self.gen.append(self.waiting()) if left: self.wannadx = -1 else: self.wannadx = 1 self.resetimages(self.wannadx, self.wannady) def resetimages(self, dx, dy): if dx > 0: self.setimages(self.cyclic(Ghost.right, 3)) elif dx < 0: self.setimages(self.cyclic(Ghost.left, 3)) #else: don't change image def waiting(self, delay=45): while not self.pacman.ready: yield None for i in range(delay): yield None self.gen.append(self.walking()) def walking(self): round = 0 self.touchable = 1 lastmove_x = 0 for t in self.moving(): if random.random() < 0.1: lastmove_x = self.wannadx if self.lastmove is None and random.random() < 0.75: round = 0 # try to move again immediately if self.lastmove is None or random.random() < 0.01: dragons = self.pacman.pacs or images.ActiveSprites distances = [(abs(dragon.x-self.x)+abs(dragon.y-self.y), dragon) for dragon in dragons] distance, dragon = min(distances) if lastmove_x: self.wannadx = 0 if (dragon.y < self.y) ^ (random.random() < 0.3): self.wannady = -1 else: self.wannady = 1 else: self.wannady = 0 if (dragon.x < self.x) ^ (random.random() < 0.3): self.wannadx = -1 else: self.wannadx = 1 else: lastmove_x = self.lastmove[0] ## for i in range(10): ## dragon = random.choice(dragons) ## dx = dragon.x - self.x ## dy = dragon.y - self.y ## if dx or dy: ## dist = math.sqrt(dx*dx+dy*dy) ## dx /= dist ## dy /= dist ## wx, wy = random.choice([(-1,0), (1,0), (0,-1), (0,1)]) ## ex = wx-dx ## ey = wy-dy ## if ex*ex + ey*ey < random.random()*3.14: ## break ## self.wannadx = wx ## self.wannady = wy if round == 0: # go just a bit faster than the players round = 6 else: round -= 1 yield None class FruitBonus(Bonus): pass class Pacman: def bgen(self, limittime = 45.1): # 0:45 import boards from player import BubPlayer self.ready = 0 monsters = BubPlayer.MonsterList[:] random.shuffle(monsters) keep = len([p for p in BubPlayer.PlayerList if p.isplaying()]) monsters = monsters[:2 + keep//2] for d in monsters: PacGhost(self, d.x, d.y) for t in boards.exit_board(0, repeatmusic=[music]): yield t for t in curboard.clean_gen_state(): yield t tc = boards.TimeCounter(limittime) self.dots = [] self.builddelay = {} self.latestposition = {} self.pacs = [] #finish = 0 for t in self.frame(): t = boards.normal_frame() self.build_pacs() yield t #if len(self.pacs) == 0: # finish += 1 # if finish == 20: # break #else: # finish = 0 tc.update(t) if tc.time == 0.0: break tc.restore() self.ready = 0 results = {} for b in self.dots: for d in b.taken_by: bubber = d.bubber results[bubber] = results.get(bubber, 0) + 1 for t in boards.result_ranking(results, len(self.dots)): self.remove_pacs() yield t for s in images.ActiveSprites[:]: if isinstance(s, Bonus): s.kill() def frame(self): import boards from bonuses import Fruits for t in self.digwalls(): yield t def anywall(x1,y1,x2,y2): for tx in range(x1, x2): for ty in range(y1, y2): if bget(tx, ty) == '#': return 1 return 0 dots = self.dots ico = images.sprget('pac-dot') for x in range(boards.width): for y in range(boards.height): if not anywall(x, y, x+2, y+2): if anywall(x-1, y-1, x+3, y+3): b = Bonus((x+1)*CELL - ico.w//2, (y+1)*CELL - ico.h//2, 'pac-dot', points=-100, falling=0) b.sound = 'Extra' b.timeout = 0 dots.append(b) for s in images.ActiveSprites: if isinstance(s, PacGhost): s.to_front() yield None self.ready = 1 for i in range(len([s for s in images.ActiveSprites if isinstance(s, PacGhost)])): for j in range(32): yield None for j in range(100): x = random.randrange(4, boards.width-6) y = random.randrange(3, boards.height-5) if not anywall(x-2, y-2, x+4, y+4): nimage, points = random.choice(Fruits.Fruits) points += 650 # boost to the range 750-1000 b = FruitBonus(x*CELL, y*CELL, nimage, points, falling=0) b.timeout = 0 break while dots: dots = [b for b in dots if b.alive] yield None def build_pacs(self): from player import BubPlayer for p in BubPlayer.PlayerList: dragons = [d for d in p.dragons if not isinstance(d, Pac)] if dragons and len(p.dragons) == len(dragons): if self.builddelay.get(p): self.builddelay[p] -= 1 else: self.builddelay[p] = 109 dragon = random.choice(dragons) if p in self.latestposition: dragon.move(*self.latestposition[p]) pac = Pac(self, p, dragon.x, dragon.y, dragon.dcap) p.dragons.append(pac) p.emotic(dragon, 4) for d in dragons: d.kill() def remove_pacs(self): from player import Dragon killclasses = (PacSprite, FruitBonus, Dragon) for s in images.ActiveSprites[:]: if isinstance(s, killclasses): s.kill() def digwalls(self): import boards holes = {} for x in range(2, boards.width-2): y = boards.height-1 if bget(x, 0) == '#' or bget(x, y) == '#': if bget(x, 0) == ' ': curboard.putwall(x, 0) if bget(x, y) == ' ': curboard.putwall(x, y) curboard.reorder_walls() for y in range(1, boards.height-1): if bget(x, y) == ' ': holes[x, y] = 0 if x % 7 == 0: yield None # 'holes' maps coordinates (x,y) to 0 (not processed) # or 1 (processed). # All processed holes are pacman-connected. def rdig(x1,y1,x2,y2, reversed, holes=holes): # digs the rectangle (x1,y1,x2,y2) and marks it as # processed. Also recursively mark as processed all existing # holes that are pacman-connected to the rectangle. xrange = range(x1,x2) yrange = range(y1,y2) if not reversed: xrange.reverse() yrange.reverse() if len(xrange) > len(yrange): xylist = [(x, y) for x in xrange for y in yrange] else: xylist = [(x, y) for y in yrange for x in xrange] t = 0 for x, y in xylist: if bget(x, y) == '#': curboard.killwall(x, y) if t == 0: yield None t = 2 else: t -= 1 holes.setdefault((x, y), 0) fill = [] for x in range(x1,x2-1): for y in range(y1,y2-1): fill.append((x, y)) for x, y in fill: if ((x, y) in holes and (x, y+1) in holes and (x+1, y) in holes and (x+1, y+1) in holes): if (holes[x, y] == 0 or holes[x, y+1] == 0 or holes[x+1, y] == 0 or holes[x+1, y+1] == 0): holes[x, y] = 1 holes[x, y+1] = 1 holes[x+1, y] = 1 holes[x+1, y+1] = 1 fill.append((x+1,y)) fill.append((x-1,y)) fill.append((x,y+1)) fill.append((x,y-1)) def joined(x1,y1,x2,y2, holes=holes, boards=boards): # returns # 1 if the rectangle (x1,y1,x2,y2) is pac-connected to # some already-processed holes # 0 if it is not # -1 if (x1,y1,x2,y2) is out of the screen if x1<2 or y1<1 or x2>boards.width-2 or y2>boards.height-1: return -1 accum1 = accum2 = 0 for x in range(x1,x2): if holes.get((x,y1-1)): accum1 += 1 if accum1 == 2: return 1 else: accum1 = 0 if holes.get((x,y2)): accum2 += 1 if accum2 == 2: return 1 else: accum2 = 0 accum1 = accum2 = 0 for y in range(y1,y2): if holes.get((x1-1,y)): accum1 += 1 if accum1 == 2: return 1 else: accum1 = 0 if holes.get((x2,y)): accum2 += 1 if accum2 == 2: return 1 else: accum2 = 0 return 0 if not holes: holes[boards.width//2, boards.height//2] = 0 holeslist = holes.keys() random.shuffle(holeslist) startx, starty = holeslist.pop() # make the hole larger (2x2) towards the center of the board if startx > boards.width//2: startx -= 1 if starty > boards.height//2: starty -= 1 # initial 2x2 hole for t in rdig(startx, starty, startx+2, starty+2, 0): yield t dlist = [ (0,0,1,0, 0,-1), (0,0,1,0, 0,0), # right (0,0,0,1, -1,0), (0,0,0,1, 0,0), # bottom (-1,0,0,0, -1,-1), (-1,0,0,0, -1,0), # left (0,-1,0,0, -1,-1), (0,-1,0,0, 0,-1), # top ] while holeslist: random.shuffle(dlist) pending = holeslist holeslist = [] progress = 0 for x, y in pending: if holes[x, y] != 0: continue for dx1, dy1, dx2, dy2, dx, dy in dlist: x1 = x + dx y1 = y + dy x2 = x1 + 2 y2 = y1 + 2 result = 0 while result == 0: result = joined(x1,y1,x2,y2) if result == 1: # rectangle (x1,y1,x2,y2) is good for t in rdig(x1, y1, x2, y2, dx1<0 or dy1<0 or dx2<0 or dy2<0): yield t progress = 1 break x1 += dx1 y1 += dy1 x2 += dx2 y2 += dy2 else: # rectangle (x1,y1,x2,y2) is too large for the screen # failure continue break else: # no successful direction found from this point holeslist.append((x, y)) # try again later if not progress: # deadlocked situation, add a new random hole x = random.randrange(2, boards.width-2) y = random.randrange(1, boards.height-1) holeslist.insert(0, (x, y)) holes.setdefault((x, y), 0) yield None # pattern transformation: # X. .. # ... --> ... # .X .X progress = 1 while progress: progress = 0 for y in range(1, boards.height-1): for x in range(3, boards.width-3): if (' ' == bget(x, y) == bget(x+1, y) == bget(x-1, y) == bget(x, y+1) == bget(x, y-1)): if '#' == bget(x-1, y-1) == bget(x+1, y+1): curboard.killwall(x-1, y-1) progress = 1 elif '#' == bget(x+1, y-1) == bget(x-1, y+1): curboard.killwall(x+1, y-1) progress = 1 yield None def run(): global curboard import boards from boards import curboard boards.replace_boardgen(Pacman().bgen()) def setup(): for key, (filename, rect) in localmap.items(): filename = os.path.join(LocalDir, filename) images.sprmap[key] = (filename, rect) setup()