diff options
author | Diego Roversi <diegor@tiscali.it> | 2019-09-15 16:38:07 +0200 |
---|---|---|
committer | Diego Roversi <diegor@tiscali.it> | 2019-09-15 16:38:07 +0200 |
commit | 27342a12e0f75fdb692186f6efdb3c4ed8fed09d (patch) | |
tree | c0e832047fb19300c7a8f9b06fc1642fba11f2ae | |
parent | 799227d88f4110beaf61908d8f76e06de81a5648 (diff) |
renamed rnglevel in rnglevel.py for convenience
-rw-r--r-- | bubbob/levels/rnglevel.py | 1282 |
1 files changed, 1282 insertions, 0 deletions
diff --git a/bubbob/levels/rnglevel.py b/bubbob/levels/rnglevel.py new file mode 100644 index 0000000..7a3186b --- /dev/null +++ b/bubbob/levels/rnglevel.py @@ -0,0 +1,1282 @@ +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) |