from random import * from math import * import boarddef from boarddef import LNasty, LMonky, LGhosty, LFlappy from boarddef import LSpringy, LOrcy, LGramy, LBlitzy from boarddef import RNasty, RMonky, RGhosty, RFlappy from boarddef import RSpringy, ROrcy, RGramy, RBlitzy def cmp(a, b): return (a > b) - (a < b) def flat(mean,var): return randrange(mean-var,mean+var+1) def dice(n,sides,orig=1): result = 0 for i in range(n): result += orig+randrange(sides) return result def fish(mu): def fact(n): r = 1. for i in range(1,n+1): r *= i return r scale = fact(0)/exp(-mu) dens = [] while 1: x = len(dens) dens.append(int(scale*exp(-mu)*pow(mu,x)/fact(x)+0.5)) if x > mu and dens[-1] == 0: break table = [] x = 0 for d in dens: for i in range(d): table.append(x) x += 1 return choice(table) class RandomLevel(boarddef.Level): WIDTH = 32 HEIGHT = 28 MAXTRY = 1000 # parameters of the 'mess generator' # mess_prob : the probability that a cell turn into a wall def __init__(self,num): if hasattr(self.__class__, 'walls'): #print 'Reusing previously generated level' #print self.__class__.walls self.walls = self.__class__.walls boarddef.Level.__init__(self,num) return #print 'Generating a new level' self.reset(fill=False) self.windmap = [ [' ' for x in range(self.WIDTH)] for y in range(self.HEIGHT) ] if hasattr(self, 'auto'): self.generate() self.do_bonuses() for gw in self.genwalls: gw[0](self,*gw[1:]) if hasattr(self, 'mlist'): self.do_monsters() self.dig_vertical_walls() self.do_walls() self.walls = self.__class__.walls #print self.walls self.do_winds() self.winds = self.__class__.winds boarddef.Level.__init__(self,num) def reset(self, fill=False): if fill: w = '#' f = 0 else: w = ' ' f = 1 # map for the walls self.wmap = [ [w for x in range(self.WIDTH)] for y in range(self.HEIGHT) ] # map of the free cells self.fmap = [ [f for x in range(self.WIDTH)] for y in range(self.HEIGHT) ] def setw(self,x,y,c='#'): if x > self.WIDTH-1 or x < 0 or y > self.HEIGHT-1 or y < 0: return if self.fmap[y][x]: self.wmap[y][x] = c self.fmap[y][x] = 0 def getw(self,x,y): if x > self.WIDTH-1 or x < 0 or y > self.HEIGHT-1 or y < 0: return '#' return self.wmap[y][x] def clrw(self,x,y): if x > self.WIDTH-1 or x < 0 or y > self.HEIGHT-1 or y < 0: return self.wmap[y][x] = ' ' self.fmap[y][x] = 1 def lockw(self,x,y,c=0): if x > self.WIDTH-1 or x < 0 or y > self.HEIGHT-1 or y < 0: return self.fmap[y][x] = c def setwind(self,x,y,c=' '): if x > self.WIDTH-1 or x < 0 or y > self.HEIGHT-1 or y < 0: return self.windmap[y][x] = c def getwind(self,x,y): if x > self.WIDTH-1 or x < 0 or y > self.HEIGHT-1 or y < 0: return ' ' return self.windmap[y][x] def wind_rect(self,x,y,w,h,ccw=0): "Set a wind in a rectangle which will move the bubbles cw or ccw" if w < 1 or h < 1: return if ccw == 1: for dx in range(w): self.setwind(x+dx+1, y, '<') self.setwind(x+dx, y+h, '>') for dy in range(h): self.setwind(x, y+dy, 'v') self.setwind(x+w, y+dy+1, '^') else: for dx in range(w): self.setwind(x+dx, y, '>') self.setwind(x+dx+1, y+h, '<') for dy in range(h): self.setwind(x, y+dy+1, '^') self.setwind(x+w, y+dy, 'v') def mirror(self): "Mirror the level vertically." for y in range(self.HEIGHT): for x in range(self.WIDTH//2): self.wmap[y][x] = self.wmap[y][self.WIDTH-x-1] def dig_well_until_space(self, x=1, yadj=1): "Digs a well either up or down and stops when it encounters first empty wall space." if yadj == 1: y = 0 else: yadj = -1 y = self.HEIGHT-1 while (y < self.HEIGHT) and (y >= 0): self.clrw(x,y) self.clrw(x+1,y) y += yadj if ((self.getw(x,y) == ' ') and (self.getw(x+1,y) == ' ')): break def enlarge_tiny_holes(self): "Makes one-block size holes wider." for x in range(self.WIDTH): for y in range(self.HEIGHT): if self.wmap[y][x] == ' ': single = 0 for dx in range(x-1,x+2): for dy in range(y-1,y+2): if self.getw(dy,dx) == '#': single = single + 1 if single == 8: if x > (self.WIDTH // 2): self.clrw(x-1,y) else: self.clrw(x+1,y) def make_space(self, gens=-1): "Removes walls from a level, to make it more playable." if gens == -1: gens = randint(0,62)+1 if gens & 1: # top for x in range(self.WIDTH): self.clrw(x,1) if random() < 0.5: self.clrw(x,2) if gens & 2: # bottom for x in range(self.WIDTH): self.clrw(x,self.HEIGHT-1) if random() < 0.5: self.clrw(x,self.HEIGHT-2) if gens & 4: # middle y = randint(0,self.HEIGHT//10) + (self.HEIGHT//2) for x in range(self.WIDTH): self.clrw(x,y) if random() < 0.5: self.clrw(x,y-1) if random() < 0.5: self.clrw(x,y+1) if gens & 8: # left x = randint(0,self.WIDTH//4) self.dig_well_until_space(x, 1) self.dig_well_until_space(x, -1) if gens & 16: # right x = randint(0,self.WIDTH//4) self.dig_well_until_space(self.WIDTH-x-2, 1) self.dig_well_until_space(self.WIDTH-x-2, -1) if gens & 32: # center self.dig_well_until_space(self.WIDTH//2, 1) self.dig_well_until_space(self.WIDTH//2, -1) def generate_wind1(self, rndchoice=1, choices=[' ',' ',' ','x','>','<','^','^','v'], xsize=-1,ysize=-1): """Makes a random wind pattern. Parameters: 0: if 1=randomly select from choices, else select in order 1: a list of the choices that are allowed. 2: horizontal size of wind blocks 3: vertical size of wind blocks """ choicenum = 0 if xsize == -1: xsize = randint(1, self.WIDTH) if ysize == -1: ysize = randint(1, self.HEIGHT) if xsize < 1: xsize = 1 elif xsize > self.WIDTH: xsize = self.WIDTH if ysize < 1: ysize = 1 elif ysize > self.HEIGHT: ysize = self.HEIGHT for x in range((self.WIDTH//xsize)+1): for y in range((self.HEIGHT//ysize)+1): if rndchoice == 1: wdir = choice(choices) else: wdir = choices[choicenum] choicenum = (choicenum + 1) % len(choices) for dx in range(xsize+1): for dy in range(ysize+1): self.setwind(x*xsize+dx,y*ysize+dy,wdir) # make sure that the special bubbles can come into screen for x in range(self.WIDTH): self.setwind(x, 0, ' ') self.setwind(x, self.HEIGHT-1, ' ') def wind_sidewalls(self): """Make sure the left and side walls have updraft next to them """ for y in range(self.HEIGHT): self.setwind(0,y,'^') self.setwind(self.WIDTH-1,y,'^') def wind_wallblocking(self, winddirs): """Sets up wind depending on the number of walls around each place. winddirs is an array of 16 wind chars. directions with walls count as: 1=N, 2=E, 4=S, 8=W 16th place is used if there is wall at the position. """ for x in range(self.WIDTH): for y in range(self.HEIGHT): walld = 0 if self.getw(x,y) == '#': walld = 16 else: if self.getw(x,y-1) == '#': walld = walld + 1 if self.getw(x+1,y) == '#': walld = walld + 2 if self.getw(x,y+1) == '#': walld = walld + 4 if self.getw(x-1,y) == '#': walld = walld + 8 wnd = winddirs[walld] self.setwind(x,y,wnd) def wind_wallblocking256(self, winddirs): """Sets up wind depending on the number of walls around each position. winddirs is an array of 257 wind chars (one of ' x<>^v-'), where '-' means to use '>' or '<', pointing towards center of level. directions with walls count as: 1=N, 2=NE, 4=E, 8=SE, 16=S, 32=SW, 64=W, 128=NW 257th place is use if there is wall at the position. """ mdirs = [(0, -1), (1, -1), (1, 0), (1, 1), (0, 1), (-1, 1), (-1, 0), (-1, -1)] for x in range(self.WIDTH): for y in range(self.HEIGHT): windd = 0 if self.getw(x,y) == '#': windd = 256 else: for d in range(8): dx = x + mdirs[d][0] dy = y + mdirs[d][1] if self.getw(dx, dy) == '#': windd = (1 << d) wd = choice(winddirs[windd]) if wd == '-': if x < self.WIDTH // 2: wd = '>' else: wd = '<' self.setwind(x,y, wd) def generate_wind(self, gens = -1): """Chooses one of the wind pattern generators and uses that to generate the winds. 0: choose what generator to use. """ if gens == -1: gens = choice([1,1,2,3,4,4,4,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19]) if gens == 1: # totally random pattern self.generate_wind1() elif gens == 2: # wind "layers" self.generate_wind1(0, ['x','^','^','^'],self.WIDTH,1) self.wind_sidewalls() elif gens == 3: # "wiggly" winds self.generate_wind1(1, ['^','<','^','>'],1,1) self.wind_sidewalls() elif gens == 4: # "normal" wind pattern self.wind_sidewalls() dx = (self.WIDTH//2) if random() < 0.7: dy = 1 # usual height where bubbles collect else: dy = randint(1, self.HEIGHT-1) for x in range(dx-3, dx+3): self.setwind(x,dy,'x') for x in range(dx-2): self.setwind(x,dy,'>') self.setwind(self.WIDTH-x-1,dy,'<') elif gens == 5: # bubbles are stopped by horizontal walls self.wind_sidewalls() for x in range(self.WIDTH): for y in range(self.HEIGHT-2): if self.getw(x,y) == '#': if self.getw(x,y+1) == ' ': self.setwind(x,y+1,'x') elif gens == 6: # bubbles move next the walls, rotating cw or ccw if random() < 0.5: #clockwise winddirs = [' ','>','v','v','<',' ','<','<','^','>',' ','v','^','>','^','x','x'] else: winddirs = [' ','<','^','<','>',' ','^','<','v','v',' ','v','>','>','^','x','x'] self.wind_wallblocking(winddirs) elif gens == 7: # bubbles move up in column(s) that zig-zag left and right wid = choice([self.WIDTH, randint(2, self.WIDTH), randint(2, self.WIDTH)]) xofs = (self.WIDTH % wid) // 2 ofs = choice([0,1]) for dx in range(0, self.WIDTH-wid+1, wid): for x in range(wid): for y in range(self.HEIGHT): if (y+ofs) & 1: self.setwind(x+dx+xofs,y,'<') if x == 0: self.setwind(x+dx+xofs,y,'^') else: self.setwind(x+dx+xofs,y,'>') if x == wid-1: self.setwind(x+dx+xofs,y,'^') elif gens == 8: # bubbles move towards the map centerline at random height for x in range(self.WIDTH): y = randint(1, self.HEIGHT-1) if x < (self.WIDTH//2): self.setwind(x,y,'>') else: self.setwind(x,y,'<') self.setwind(x,0,'v') self.setwind(x,self.HEIGHT-1,'^') for y in range(self.HEIGHT): self.setwind(self.WIDTH//2,y,'x') elif gens == 9: # bubbles move towards the side walls at random height for x in range(self.WIDTH): y = randint(1, self.HEIGHT-1) if y & 1: self.setwind(x,y,'>') else: self.setwind(x,y,'<') self.setwind(x,0,'v') self.setwind(x,self.HEIGHT-1,'^') for y in range(self.HEIGHT): self.setwind(0,y,'x') self.setwind(self.WIDTH-1,y,'x') elif gens == 10: # bubbles move up-down ofs = choice([0,1]) dir_l = choice(['>', '>', '<']) dir_r = choice(['<', '<', '>']) for x in range(self.WIDTH): if x < (self.WIDTH // 2): self.setwind(x, 0, dir_r) self.setwind(x,self.HEIGHT-1,dir_l) else: self.setwind(x, 0, dir_l) self.setwind(x,self.HEIGHT-1,dir_r) for x in range(self.WIDTH): for y in range(self.HEIGHT): if (x+ofs) & 1: self.setwind(x,y+1,'^') else: self.setwind(x,y-1,'v') elif gens == 11: # bubbles rotate self.wind_sidewalls() for z in range(20): wid = randint(2,self.WIDTH//2) hei = randint(2,self.HEIGHT//2) y = randint(1, self.HEIGHT - hei - 1) x = randint(1, self.WIDTH - wid - 1) ok = 1 for dx in range(wid): if self.getwind(x+dx+1, y) != ' ': ok = 0 if self.getwind(x+dx, y+hei) != ' ': ok = 0 for dy in range(hei): if self.getwind(x, y+dy) != ' ': ok = 0 if self.getwind(x+wid, y+dy+1) != ' ': ok = 0 if ok == 1: self.wind_rect(x,y,wid,hei, random() < 0.5) elif gens == 12: # bubbles gravitate towards a certain spot dx = randint(1,self.WIDTH-1) dy = randint(1,self.HEIGHT-1) for x in range(self.WIDTH): for y in range(self.HEIGHT): ax = abs(dx - x) ay = abs(dy - y) sx = cmp(dx - x, 0) sy = cmp(dy - y, 0) winds = [' ',' ',' '] if ax < 2 and ay < 2: winds = ['x'] else: if sx < 0: winds += ['<'] elif sx > 0: winds += ['>'] else: if sy > 0: winds = ['v'] elif sy < 0: winds = ['^'] else: winds = ['x'] if sy < 0: winds += ['^'] elif sy > 0: winds += ['v'] else: if sx > 0: winds = ['>'] elif sx < 0: winds = ['<'] else: winds = ['x'] self.setwind(x,y,choice(winds)) elif gens == 13: # bubbles stop at some random positions self.generate_wind1(1, [' ',' ',' ',' ',' ',' ',' ','x'],1,1) self.wind_sidewalls() elif gens == 14: # bubbles move cw and ccw in alternating rectangles m = max(self.WIDTH // 2, self.HEIGHT // 2) cwofs = choice([0,1]) thk = choice([1,1,2,2,3,4,randint(1,m//2)]) for dx in range(m): cw = ((dx // thk) + cwofs) % 2 self.wind_rect(dx,dx, self.WIDTH-(dx*2), self.HEIGHT-(dx*2), cw) elif gens == 15: # bubbles move cw or ccw in rectangles m = max(self.WIDTH // 2, self.HEIGHT // 2) cw = choice([0,1]) for dx in range(m): self.wind_rect(dx,dx, self.WIDTH-(dx*2), self.HEIGHT-(dx*2), cw) elif gens == 16: xs = randint(2, (self.WIDTH//2)-1) ys = randint(2, (self.HEIGHT//2)-1) rx = (self.WIDTH // xs) + 1 ry = (self.HEIGHT // ys) + 1 cwchanges = choice([0,0,0,0,0,0,0,0,1,1,1,1,2,2,3]) if cwchanges == 0: cw = random() < 0.5 for x in range(rx): if cwchanges == 1: cw = random() < 0.5 for y in range(ry): if cwchanges == 2: cw = random() < 0.5 maxd = max((xs // 2), (ys // 2)) for d in range(maxd): if cwchanges == 3: cw = random() < 0.5 self.wind_rect(xs*x+d, ys*y+d, xs-2*d-1, ys-2*d-1, cw) elif gens == 17: # bubbles bounce between walls if random() < 0.5: # horizontal winddirs = [' ',' ','<','<',' ',' ','<','<','>','>',' ',' ','>','>',' ',' ','x'] else: # vertical winddirs = [' ','v',' ','v','^',' ','^',' ',' ','v',' ','v','^',' ','^',' ','x'] self.wind_wallblocking(winddirs) elif gens == 18: # generate winds based on a random 3x3 matrix ruleset winddirs = [] for z in range(257): winddirs.append(choice([' ',' ',' ','x','^','v','-'])) winddirs[0] = ' ' winddirs[256] = choice(['x',' ']) self.wind_wallblocking256(winddirs) elif gens == 19: # bubbles will move downwards in a zig-zag pattern y = 0 x1 = randint(0, self.WIDTH-1) while y < self.HEIGHT: x2 = randint(0, self.WIDTH-1) if x1 < x2: self.setwind(x1,y, '>') else: self.setwind(x1,y, '<') dy = choice([1,1,1,2]) self.setwind(x2,y, 'v') y += dy x1 = x2 def smooth(self, threshold, rev): """Remove wall blocks that are surrounded by 4 empty places. 0: probability which a wall cell is turned into space 1: smooth away walls or smooth away empty spaces? """ # make a copy of self.wmap and adds '#' at the end of line, for # the overflowing indexing below: [x-1] and [x+1] tmpwmap = [ line + ['#'] for line in self.wmap ] if rev == 0: chr = ' ' else: chr = '#' for x in range(self.WIDTH): for y in range(1,self.HEIGHT-1): count = 0 if tmpwmap[y+1][x] == chr: count = count + 1 if tmpwmap[y-1][x] == chr: count = count + 1 if tmpwmap[y][x+1] == chr: count = count + 1 if tmpwmap[y][x-1] == chr: count = count + 1 if (count >= 4) and (random() < threshold): if rev == 0: self.clrw(x,y) else: self.setw(x,y) def mess(self, threshold): """Random fill of the board with walls. Only one argument, the probability that a cell turns out to be a wall. """ for x in range(self.WIDTH): for y in range(self.HEIGHT): if random() < threshold: self.setw(x,y) def zigzag_lr(self): """Generate the level with random left-right zig-zags. """ first = 1 self.reset(fill=False) y = choice([0,0,2,3,3,4]) while y < (self.HEIGHT-2): if first == 1: first = 0 x1 = x2 = randint(2, self.WIDTH-3) else: x2 = randint(2, self.WIDTH-3) while (x2 > (x1-3)) and (x2 < (x1+3)): x2 = randint(2, self.WIDTH-3) for dx in range(min(x1,x2+1), max(x1,x2+1)): self.setw(dx,y) dy = choice([2,2,3,3,3,4]) for dy in range(dy+1): self.setw(x2,y+dy) y = y + dy x1 = x2 def zigzag_ud(self): """Generate the level with random up-down zig-zags. """ first = 1 self.reset(fill=False) x = -1 while x < self.WIDTH: if first == 1: first = 0 y1 = y2 = randint(2, self.HEIGHT-1) else: y2 = randint(2, self.HEIGHT-1) while (y2 > (y1-2)) and (y2 < (y1+2)): y2 = randint(2, self.HEIGHT-1) for dy in range(min(y1,y2+1), max(y1,y2+1)): self.setw(x,dy) dx = choice([3,4,4,4,5,6]) for dx in range(dx+1): self.setw(x+dx,y2) x = x + dx y1 = y2 def zigzag(self): """Generate a level with a random zig-zag form. """ if random() < 0.5: self.zigzag_lr() else: self.zigzag_ud() def platforms(self, xxx_todo_changeme, xxx_todo_changeme1, full=1): """Place random platforms. args is a tuple with the following fields: 0: a tuple containing the number of platforms and the minum space between two platforms, 1: a tuple indicating in order: - the rng for the number of holes per platform - the rng for the width of the holes, 2: a flag indicating whether the platform should cross the whole level or not. """ (nplat, space) = xxx_todo_changeme (rng_holes, rng_width) = xxx_todo_changeme1 plat = [] for i in range(nplat): ntry = 100 while ntry: y = randint(0,self.HEIGHT-1) found = 0 for old in plat: if abs(old-y) <= space: found = 1 break if not found: plat.append(y) break ntry -= 1 if not ntry: continue # ignore platform if full: x = 0 w = self.WIDTH else: x = randint(0,self.WIDTH-1) w = randint(0,self.WIDTH-1) s = choice([-1,1]) if s == -1: w = min(w,x) x -= w else: w = min(w,self.WIDTH-x) for x1 in range(x,x+w): self.setw(x1,y) for i in range(rng_holes()): hx = randint(x,x+w) hw = rng_width() for h in range(hx-hw//2,hx+hw//2): self.clrw(h,y) def remove_joined_blocks(self): """Removes randomly placed, random sized blocks of walls. The blocks are usually joined, or if not, they're offset so that it's possible to move from one block to another by jumping. """ self.reset(fill=True) nrooms = randint(1, 4) while nrooms: nrooms -= 1; x = 0 while x < self.WIDTH: wid = randint(2,8) hei = randint(2,6) y = randint(2, self.HEIGHT - hei - 1) for dx in range(wid): for dy in range(hei): self.clrw(x+dx, y+dy) x += wid + choice([-2,-2,-1,-1,0]); def discrete_blocks(self, blocks = -1): """Put certain size blocks randomly, but so that they don't touch each other. """ self.reset(fill=False) if blocks == -1: if random() < 0.75: blocks = [(4,2),(2,4)] # CompactLevels, level 16 if random() < 0.30: blocks.append((2,2)) if random() < 0.20: blocks.append((6,2)) if random() < 0.10: blocks.append((8,2)) else: blocks = [] while len(blocks) == 0: for bz in range(10): if random() < 0.3: blocks.append((bz+1,1)) ntry = 300 while ntry: ntry -= 1 doput = 1 block = choice(blocks) wid = block[0] hei = block[1] x = randint(0,self.WIDTH-wid-2) y = randint(1,self.HEIGHT-hei-3) for dx in range(x,x+wid+2): for dy in range(y,y+hei+2): if self.getw(dx,dy) == '#': doput = 0 if doput: for dx in range(x+1,x+wid+1): for dy in range(y+1,y+hei+1): self.setw(dx,dy) def lines(self, rng_len, nlines, rng_angle=None): """Generate a set of lines in any direction. It takes three arguments, a rng for the length the lines, the number of lines, and a rng for the angle. """ if rng_angle is None: rng_angle = lambda : choice([0]+[pi/i for i in range(3,21)]+[-pi/i for i in range(3,21)]) for i in range(nlines): len = rng_len() angle = rng_angle() ntry = self.MAXTRY while ntry: sx = randint(0,self.WIDTH-1) sy = randint(0,self.HEIGHT-1) dx = int(sx + len*cos(angle) + 0.5) dy = int(sy + len*sin(angle) + 0.5) if dx < self.WIDTH and dy < self.HEIGHT and dx >= 0 and dy >= 0: break ntry -= 1 if ntry == 0: break if abs(dx-sx) > abs(dy-sy): for x in range(dx-sx+1): y = (2*(dy-sy)*x//(dx-sx)+1)//2 self.setw(sx+x,sy+y) else: for y in range(dy-sy+1): x = (2*(dx-sx)*y//(dy-sy)+1)//2 self.setw(sx+x,sy+y) def rooms(self, rng_radius, rng_e, n_rooms): """Generate rooms. It takes the following arguments: 0: the rng for the radius of the room 1: the rng for the excentricity of the room 2: the number of rooms """ for i in range(n_rooms): cx = randint(0,self.WIDTH-1) cy = randint(0,self.HEIGHT-1) r = rng_radius() e = rng_e()*1.0 left = cx-int(r*e+0.5) right = cx+int(r*e+0.5) top = cy-int(r/e+0.5) bottom = cy+int(r/e+0.5) for x in range(left,right+1): self.setw(x,top) self.setw(x,bottom) for y in range(top,bottom+1): self.setw(left,y) self.setw(right,y) for x in range(left+1,right): for y in range(top+1,bottom): self.lockw(x,y) def holes(self, rng_radius, rng_e, n_holes, rng_rect): """Generate a set of holes in the level. It takes four args: 0: the rng for the radius of the holes 1: the rng for the excentricity of the holes 2: the number of holes 3: the rng for the shape of the hole 0 for circular, 1 for rectangular """ for i in range(n_holes): cx = randint(0,self.WIDTH-1) cy = randint(0,self.HEIGHT-1) r = rng_radius() e = rng_e()*1.0 rect = rng_rect() for x in range(cx-int(r*e+0.5),cx+int(r*e+0.5)+1): for y in range(cy-int(r/e+0.5),cy+int(r/e+0.5)+1): if not rect and (((x-cx)/e)**2+((y-cy)*e)**2) > r**2: continue self.clrw(x,y) def grids(self, horizchance, vertchance): """Generate a level with a grid of horizontal and vertical lines 0: gaussian chance of each horizontal line part 1: gaussian chance of each vertical line part """ self.reset(fill=False) xsize = choice([3,3,3,4,4,4,4,5,6]) ysize = choice([2,3,3,4,4,4,4,5]) xofs = choice([-1,0,1]) yofs = choice([-1,0,1]) for x in range((self.WIDTH//xsize)+1): for y in range((self.HEIGHT//ysize)+1): dx = x*xsize + xofs dy = y*ysize + yofs if gauss(0,1) > horizchance: for i in range(0,xsize+1): self.setw(dx+i,dy) if gauss(0,1) > vertchance: for i in range(0,ysize+1): self.setw(dx,dy+i) def pegs(self, pegchance, posadj, thick): """Generate a level by putting pegs 0: gaussian level of a peg appearance 1: gaussian level of peg position adjustment """ self.reset(fill=False) xdist = choice([3,3,3,4,4,5]) # distance between pegs ydist = choice([2,3,3,3,4,5]) # distance between pegs if not thick: xdist = xdist - randint(0,1) ydist = ydist - randint(0,1) xadj = randint(0,4) - 2 yadj = randint(0,4) - 2 for x in range(self.WIDTH // xdist): for y in range(self.HEIGHT // ydist): if gauss(0,1) > pegchance: dx = x * xdist + xadj dy = y * ydist + yadj if gauss(0,1) > posadj: dx = dx + randint(0,2) - 1 dy = dy + randint(0,2) - 1 self.setw(dx,dy) if thick: self.setw(dx+1,dy) self.setw(dx,dy+1) self.setw(dx+1,dy+1) def mondrian(self, x1=2,y1=2,x2=-1,y2=-1, horiz=-1, mindepth=3): """Generate a level that looks a bit like a Piet Mondrian painting, or different sized rectangles stacked on top of each other. 0-3: the size of the area to be split 4: whether the first split is horizontal or vertical 5: minimum number of splits to do """ if horiz == -1: horiz = choice([0,1]) if x2 == -1: x2 = self.WIDTH-2 if y2 == -1: y2 = self.HEIGHT-2 if (abs(x2-x1) < 6) or (abs(y2-y1) < 5): return mindepth = mindepth - 1 if horiz == 1: horiz = 0 dy = randint(y1+2,y2-2) for dx in range(min(x1,x2),max(x1,x2)): self.setw(dx,dy) if (random() < 0.75) or (mindepth > 0): self.mondrian(x1,y1,x2,dy, horiz, mindepth) if (random() < 0.75) or (mindepth > 0): self.mondrian(x1,dy,x2,y2, horiz, mindepth) else: horiz = 1 dx = randint(x1+3,x2-3) for dy in range(min(y1,y2),max(y1,y2)): self.setw(dx,dy) if (random() < 0.75) or (mindepth > 0): self.mondrian(x1,y1,dx,y2, horiz, mindepth) if (random() < 0.75) or (mindepth > 0): self.mondrian(dx,y1,x2,y2, horiz, mindepth) def bouncers(self, length, diradj, rev): """Generate a level using a down and left or right moving walker 0: how many steps does the walker take 1: gaussian level, how often to change moving from left to right 2: fill empty level with wall or reverse? """ if rev == 0: self.reset(fill=True) else: self.reset(fill=False) x = randint(0,self.WIDTH-2) y = randint(0,self.HEIGHT-2) lorr = choice([1, -1]) # move left or right for i in range(length): if rev == 0: self.clrw(x,y) self.clrw(x+1,y) self.clrw(x,y+1) self.clrw(x+1,y+1) else: self.setw(x,y) self.setw(x+1,y) x = x + lorr y = y + 1 if y > self.HEIGHT: y = 0 if x > self.WIDTH - 2: x = self.WIDTH - 2 lorr = -lorr elif x < 0: x = 0 lorr = -lorr if gauss(0,1) > diradj: lorr = -lorr def walkers(self, length, minturn, maxturn, isbig): """Generate a level with a walker 0: length of the walker: how many steps it walks 1: minimum length it walks straight, before turning 2: maximum length it walks straight, before turning 3: is the trail is 1 or 2 blocks high """ # We start from a full wall self.reset(fill=True) x = randint(0,self.WIDTH-2) y = randint(0,self.HEIGHT-2) dir = randint(0,4) dlen = 0 for i in range(length): self.clrw(x,y) self.clrw(x+1,y) if isbig == 1: self.clrw(x,y+1) self.clrw(x+1,y+1) dlen = dlen + 1 if dir == 0: x = x - 2 if x < 0: x = self.WIDTH-2 elif dir == 1: y = y - 1 if y < 0: y = self.HEIGHT elif dir == 2: x = x + 2 if x > (self.WIDTH - 2): x = 0 else: y = y + 1 if y > self.HEIGHT: y = 0 if dlen > randint(minturn, maxturn): # turn 90 degrees dir = (dir + choice([1,3])) % 4 dlen = 0 def rivers(self, n_flow, side_threshold, side_shift): """Generate flow paths by digging a big wall. The arguments are: 0: the number of parallel flow to dig in the wall 1: side_threshold is a gausian level for doing a step aside 2: side_shift is the maximal size of the side step. """ # We start from a full wall self.reset(fill=True) for x in [0, self.WIDTH-2]+[randint(3,self.WIDTH-5) for f in range(max(0, n_flow-2))]: for y in range(self.HEIGHT): self.clrw(x,y) self.clrw(x+1,y) g = gauss(0,1) if abs(g) > side_threshold: # We want to move aside, let's find which side is the best: if self.WIDTH//4 < x < 3*self.WIDTH//4: side = random() > 0.5 t = random() if t > x*4/self.WIDTH: side = 1 elif t > (self.WIDTH-x)*4/self.WIDTH: side = -1 side_step = randint(1,side_shift) if side > 0: for i in range(x+2, min(x+2+side_step,self.WIDTH-1)): self.clrw(i,y) x = max(0,min(x+side_step, self.WIDTH-2)) else: for i in range(max(x-side_step,0),x): self.clrw(i,y) x = max(x-side_step, 0) def platforms_reg(self): """Generate random platforms at regular y-intervals. """ self.reset(fill=False) yadjs = [-2,-1,0,0,0,0,0,0,0,0,1,2] y = randint(2,4) yinc = randint(2,6) yincadj = choice(yadjs) ymax = self.HEIGHT-choice([1,1,1,1,1,2,2,2,3,3,4])-1 while y < ymax: holes = randint(choice([0,1,1,1,1]),7) for x in range(0, self.WIDTH): self.setw(x,y) for i in range(holes): x = randint(0, self.WIDTH-2) self.clrw(x,y) self.clrw(x+1,y) y = y + yinc yinc = yinc + yincadj if yinc < 2: yinc = 2 yincadj = choice(yadjs) if yinc > 6: yinc = 6 yincadj = choice(yadjs) def startplatform(self): "Make sure there's free space with wall underneath for dragon start positions" hei = choice([1,1,1,2,2,3]) lft = choice([0,1]) wid = choice([3,3,3,4,5]) for x in range(lft, wid): self.setw(x,self.HEIGHT-1) self.setw(self.WIDTH-x-1,self.HEIGHT-1) for y in range(hei+1): self.clrw(x,self.HEIGHT-2-y) self.clrw(self.WIDTH-x-1,self.HEIGHT-2-y) def openstartway(self): "Make sure there is a way from the starting position to the center of the level. Reduces player frustrations." gen = choice([0,0,0,1]) if gen == 0: # horizontal open space to middle of level ypos = choice([1,1,1,1,1,2,2,3,4,randint(1,self.HEIGHT//2)]) hei = choice([1,1,1,2]) for x in range(self.WIDTH//2): for y in range(hei): self.clrw(x, self.HEIGHT-1-ypos-y) ypos = choice([1,1,1,1,1,2,2,3,4,randint(1,self.HEIGHT//2)]) hei = choice([1,1,1,2]) for x in range(self.WIDTH//2): for y in range(hei): self.clrw(self.WIDTH-x-1, self.HEIGHT-1-ypos-y) elif gen == 1: # open way diagonally to NW or NS, third of a way to the level width ypos = choice([1,1,1,1,1,2,2,3,4]) wid = choice([2,2,2,2,2,3,3,4]) for x in range(self.WIDTH//3): for z in range(wid): self.clrw(x+z, self.HEIGHT-1-x-ypos) ypos = choice([1,1,1,1,1,2,2,3,4]) wid = choice([2,2,2,2,2,3,3,4]) for x in range(self.WIDTH//2): for z in range(wid): self.clrw(self.WIDTH-x-1-z, self.HEIGHT-1-x-ypos) def close(self): "Just close the level with floor and roof" for x in range(self.WIDTH): self.setw(x,0) self.setw(x,self.HEIGHT) def largest_vertical_hole(self, x): "Returns the (start, stop) of the largest range of holes in column x." if not (0 <= x < self.WIDTH): return (0, 0) ranges = [] best = 0 length = 0 for y in range(self.HEIGHT+1): if y < self.HEIGHT and self.getw(x,y) == ' ': length += 1 elif length > 0: if length > best: del ranges[:] best = length if length == best: ranges.append((y-length, y)) length = 0 return choice(ranges or [(0, 0)]) def dig_vertical_walls(self): "Check that no vertical wall spans the whole height of the level" vwall = [] for x in range(self.WIDTH): spaces = 0 for y in range(self.HEIGHT-1): # ignore bottom line spaces spaces += self.getw(x,y) == ' ' if spaces == 0 or (random() < 0.4**spaces): vwall.append(x) shuffle(vwall) for x in vwall: # look for the longest continuous space in each of the two # adjacent columns, and extend these to the current column def dig(y1, y2): for y in range(y1, y2): self.clrw(x, y) return y1 < y2 and y1 < self.HEIGHT-1 progress = False for col in [x-1, x+1]: y1, y2 = self.largest_vertical_hole(col) progress |= dig(y1, y2) while not progress: progress |= dig(randint(0, self.HEIGHT-1), randint(0, self.HEIGHT-1)) def prevent_straight_fall(self): """Make platforms that prevent falling straight from top to bottom, but still leave space for moving. """ falls = [] for x in range(self.WIDTH): for y in range(self.HEIGHT): if self.getw(x,y) == '#': break else: falls = falls + [x] y = oldy = -10 for x in falls: while (y < oldy+2) and (y > oldy-2): y = randint(2, self.HEIGHT-2) for dy in range(y-1,y+2): for dx in range(x-3, x+4): self.clrw(dx,dy) self.setw(x-1,y) self.setw(x+1,y) self.setw(x,y) oldy = y def do_monsters(self): """Create monsters based on the requested settings. mlist is a list of monster setting. Each item is a tuple with: 0: the list of monster to uses (each item might be a tuple) 1: the rng for the number of monsters to pick in the list. """ current = 'a' for ms in self.mlist: n_monsters = ms[1]() for idx in range(n_monsters): setattr(self,current,choice(ms[0])) # self.__class__.__dict__[current] = choice(ms[0]) ntry = self.MAXTRY while ntry: x = randint(0,self.WIDTH-2) y = randint(0,self.HEIGHT-1) if self.getw(x,y) == self.getw(x+1,y) == ' ': self.wmap[y][x] = current break ntry -= 1 current = chr(ord(current)+1) def do_walls(self): "Build the actual walls map for the game." self.__class__.walls = '' for y in range(self.HEIGHT-1): self.__class__.walls += '##' for x in range(self.WIDTH): self.__class__.walls += self.wmap[y][x] self.__class__.walls += '##\n' self.__class__.walls += '##' for x in range(self.WIDTH): if self.getw(x,0) == '#' or self.getw(x,self.HEIGHT-1) == '#': self.__class__.walls += '#' else: self.__class__.walls += ' ' self.__class__.walls += '##\n' def do_winds(self): "Build the actual wind map for the game." self.__class__.winds = '' for y in range(self.HEIGHT): self.__class__.winds += '>>' for x in range(self.WIDTH): self.__class__.winds += self.windmap[y][x] self.__class__.winds += '<<' + '\n' def do_bonuses(self): self.__class__.letter = choice([0,1]) self.__class__.fire = choice([0,1]) self.__class__.lightning = choice([0,1]) self.__class__.water = choice([0,1]) self.__class__.top = choice([0,1]) def generate(self): "Generate random level settings." assert 0, "--- THIS IS NO LONGER REALLY USED ---" self.mlist = [([ LNasty, LMonky, LGhosty, LFlappy, LSpringy, LOrcy, LGramy, LBlitzy, RNasty, RMonky, RGhosty, RFlappy, RSpringy, ROrcy, RGramy, RBlitzy, ],lambda : flat(12,4))] gens = choice([512,512,256,256,128,128,64,64,32,32,16,16,16,16,16,16,20,20,8,8,8,8,4,4,4,4,2,2,2,2,1,1,3,5,6,7]) self.genwalls = [] if gens & 512: print('Using grids generator') self.genwalls.append((RandomLevel.grids, uniform(0.0, 0.1), uniform(0.0, 0.1))) if gens & 256: # generate using pegs print('Using the pegs generator') self.genwalls.append((RandomLevel.pegs, uniform(0.1,0.2), uniform(0.45,0.7), choice([0,1,1,1]))) if gens & 128: # generate using a bouncer nr = choice([0,0,1]) print('Using the bouncer generator') self.genwalls.append((RandomLevel.bouncers, dice(1, 100) + 250 - nr*200, # length uniform(0.7, 1.7), nr)) if gens & 64: # generate using a walker print('Using the walker generator') nr = dice(1, 3) + 2 self.genwalls.append((RandomLevel.walkers, dice(2, 100) + 100, # length nr, nr + dice(2, 3), # straight walk min, max len choice([0,1]))) if gens & 32: # generate rivers print('Using the rivers generator') self.genwalls.append((RandomLevel.rivers, randrange(2,(self.WIDTH-4)/5), # the number of rivers uniform(0.7, 1.7), # the side stepping threshold 6)) # the max side stepping size if gens & 16: # generate rooms print('Using the romms generator') nr = choice([1,2,2,2,3,3,4,5]) self.genwalls.append((RandomLevel.rooms, lambda : flat(9-nr,2), # the half size of the room lambda : uniform(0.8,1.2), # the excentricity of the room nr)) # the number of rooms if gens & 8: # generate a holes generator # as this is interesting only if the level is filled somehow print('Using the holes generator') self.genwalls.append((RandomLevel.mess,1-uniform(0.2,0.5))) nh = choice([1,1,2,2,2,3,3,3,4,5]) self.genwalls.append((RandomLevel.holes, lambda : flat(9-nh,2), # radius of the holes lambda : uniform(0.9,1.1), # excentricity nh, # number of holes lambda : choice([0,0,0,1]))) # circle or rectangle if gens & 4: # generate a lines generator print('Using the lines generator') self.genwalls.append((RandomLevel.lines, lambda : dice(7,3), # line length dice(2,3))) # number of lines if gens & 2: # generate a platforms generator print('Using the platforms generator') nplat = dice(2,4,0) if nplat: space = flat((self.HEIGHT-1)/nplat/2,(self.HEIGHT-1)/nplat/2-1) else: space = 1 nholes = lambda : dice(1,3,0) wholes = lambda : dice(2,3) full = randrange(2) self.genwalls.append((RandomLevel.platforms, (nplat,space), # number of platform and spacing (nholes,wholes), # number of holes and width full)) # full width platform if gens & 1: # generate a mess generator print('Using the mess generator') if gens & ~2: offset = 0 scale = 0.05 else: offset = 0.05 scale = 0.10 self.genwalls.append((RandomLevel.mess,offset+random()*scale)) if random() < 0.2: self.genwalls.append((RandomLevel.close,)) if random() < 0.90: self.genwalls.append((RandomLevel.startplatform,)) self.genwalls.append((RandomLevel.generate_wind, )) Levels = [] for i in range(25): class level(RandomLevel): auto = 1 Levels.append(level) class levelfinal(RandomLevel): genwalls = [(RandomLevel.platforms,(4,3),(lambda:flat(1,1),lambda:flat(4,2)),1)] Levels.append(levelfinal)