1
0
mirror of https://github.com/abakh/nbsdgames.git synced 2025-02-02 15:07:27 -05:00
nbsdgames/bubbob/levels/RandomLevels.py.bak
2022-02-03 13:46:22 +03:30

363 lines
12 KiB
Python

#
# Second try at automatically generating levels that are
# a bit more related to each other instead of being completely independent.
#
from __future__ import generators
import sys, random, math
from random import uniform, choice, randrange
class Parameter(object):
def __init__(self, name, rng):
self.name = name
self.rng = rng
def __get__(self, instance, cls):
assert self.name not in instance.__dict__
value = self.rng()
setattr(instance, self.name, value)
return value
class ChoiceParameter(Parameter):
def __init__(self, name, list):
Parameter.__init__(self, name, lambda : choice(list))
class BoolParameter(Parameter):
def __init__(self, name, prob=0.5):
Parameter.__init__(self, name, lambda : random.random() < prob)
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 fork(choice1, prob, choice2):
if random.random() < prob:
return choice1()
else:
return choice2()
MnstrCategory = {
"Nasty": 0,
"Monky": 0,
"Ghosty": 1,
"Flappy": 1,
"Springy": 2,
"Orcy": 0,
"Gramy": 0,
"Blitzy": 2}
MnstrNames = MnstrCategory.keys()
Bonuses = ['letter', 'fire', 'lightning', 'water', 'top']
def mnstrclslist(name):
import boarddef
classname1 = 'L' + name
classname2 = 'R' + name
return [getattr(boarddef, classname1), getattr(boarddef, classname2)]
class Shape:
basemnstr = ChoiceParameter('basemnstr', MnstrNames)
extramnstr = ChoiceParameter('extramnstr', range(4))
samemnstr = BoolParameter('samemnstr')
baseshape = ChoiceParameter('baseshape', ' ODBGMPRWZS')
rooms = BoolParameter('rooms')
holes = BoolParameter('holes')
lines = ChoiceParameter('lines', ' -/|')
platforms = BoolParameter('platforms')
platholes = BoolParameter('platholes')
platfull = BoolParameter('platfull')
mess = ChoiceParameter('mess', ' ....!')
closed = BoolParameter('closed', 0.95)
bonuses = ChoiceParameter('bonuses', xrange(3**len(Bonuses)))
smooth = ChoiceParameter('smooth', range(4))
startplats = BoolParameter('startplats', 0.98)
makespace = BoolParameter('makespace', 0.8)
straightfall = BoolParameter('straightfall', 0.8)
mirrored = BoolParameter('mirrored', 0.4)
enlargeholes = BoolParameter('enlargeholes', 0.9)
all_parameters = [name for name in locals().keys()
if not name.startswith('_')]
def __init__(self, shape=None):
if shape:
self.__dict__.update(shape.__dict__)
self.modified = 0
def set_gens(self, rooms=0, platforms=0, holes=0, smooth=0, mess=' ', lines=' '):
self.rooms = rooms
self.platforms = platforms
self.holes = holes
self.smooth = smooth
self.mess = mess
self.lines = lines
def reset(self, attrname=None):
if attrname:
try:
del self.__dict__[attrname]
except KeyError:
pass
else:
self.__dict__.clear()
self.modified = 1
def __eq__(self, other):
return (self.__class__ is other.__class__ and
self.__dict__ == other.__dict__)
def test_similar_parameters(self, prevlist):
similarity = 0
rprevlist = prevlist[:]
rprevlist.reverse()
for param in Shape.all_parameters:
accum = 0
for prev in rprevlist:
if getattr(self, param) != getattr(prev, param):
break
else:
accum += 1
similarity += accum
minimum = min(4*len(prevlist), 7)
if not (minimum <= similarity <= 17):
self.reset()
def test_not_too_often(self, prevlist):
for param, bad_value, delay in [
('mess', '.', 2),
('mess', '!', 11),
('holes', 1, 1),
]:
if getattr(self, param) == bad_value:
for prev in prevlist[-delay:]:
if getattr(prev, param) == bad_value:
self.reset(param)
def test_mess_hole(self, prevlist):
if self.mess == '!':
self.holes = 1
all_tests = [value for (name, value) in locals().items()
if name.startswith('test_')]
def accept(self, lvl):
f = lambda d=self.difficulty : randrange(3, 4+int(9*d))
lvl.mlist = [(mnstrclslist(self.basemnstr), f)]
repeat = choice([2,2,3]) - self.extramnstr
if repeat > 1:
lvl.mlist *= repeat
if self.extramnstr:
othermnstr = [name for name in MnstrNames if name!=self.basemnstr]
if self.samemnstr:
othermnstr = [name for name in othermnstr
if MnstrCategory[name]==MnstrCategory[self.basemnstr]]
random.shuffle(othermnstr)
for name in othermnstr[:self.extramnstr]:
lvl.mlist.append((mnstrclslist(name), f))
lvl.genwalls = []
if self.baseshape == 'G':
lvl.genwalls.append((RandomLevel.grids,
uniform(0.7,0.8),
uniform(0.7,0.8)))
self.set_gens()
if self.baseshape == 'P':
lvl.genwalls.append((RandomLevel.pegs,
uniform(0.1,0.2),
uniform(0.45,0.7),
choice([0,1,1,1])))
self.set_gens(smooth=3)
self.closed = random.random() < 0.80
if self.baseshape == 'B':
nr = choice([0,0,1])
lvl.genwalls.append((RandomLevel.bouncers,
dice(1, 100) + 250 - nr*200, # length
uniform(0.7, 1.7),
nr))
self.set_gens(smooth=3)
if self.baseshape == 'W':
nr = dice(1, 3) + 2
lvl.genwalls.append((RandomLevel.walkers,
dice(2, 100) + 100, # length
nr, nr + dice(2, 3),
choice([0,1])))
self.set_gens()
if self.baseshape == 'R':
lvl.genwalls.append((RandomLevel.rivers,
randrange(3,(lvl.WIDTH-4)/4), # the number of rivers
uniform(0.3, 1.4), # the side stepping threshold
10)) # the max side stepping size
self.set_gens()
if self.baseshape == 'Z':
lvl.genwalls.append((RandomLevel.zigzag,))
self.set_gens()
if self.baseshape == 'M':
lvl.genwalls.append((RandomLevel.mondrian,))
self.set_gens()
if self.baseshape == 'O':
lvl.genwalls.append((RandomLevel.remove_joined_blocks,))
self.set_gens()
if self.baseshape == 'S':
lvl.genwalls.append((RandomLevel.platforms_reg,))
self.set_gens()
self.makespace = random.random() < 0.1
if self.closed:
self.startplats = 0
if self.baseshape == 'D':
lvl.genwalls.append((RandomLevel.discrete_blocks,))
self.set_gens()
self.makespace = random.random() < 0.1
if self.closed:
self.startplats = 0
if self.rooms:
nr = dice(2, 6)
lvl.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 self.lines != ' ':
rng_angle = {
'-': lambda : 0,
'/': None, # default
'|': lambda : math.pi/2,
}
lvl.genwalls.append((RandomLevel.lines,
lambda : dice(8,3), # line length
dice(2,4), # number of lines
rng_angle[self.lines]))
if self.platforms:
nplat = dice(2,4,0)
if nplat: space = flat((lvl.HEIGHT-1)/nplat/2,(lvl.HEIGHT-1)/nplat/2-1)
else: space = 1
if self.platholes:
nholes = lambda : dice(1,3)
else:
nholes = lambda : 0
wholes = lambda : dice(2,3)
full = self.platfull
lvl.genwalls.append((RandomLevel.platforms,
(nplat,space), # number of platform and spacing
(nholes,wholes), # number of holes and width
full)) # full width platform
if self.mess != ' ':
threshold = {
'.': 0.02 + 0.08*random.random(), # normal
'!': 0.25 + 0.2 *random.random(), # super-filled
}
lvl.genwalls.append((RandomLevel.mess, threshold[self.mess]))
if self.holes:
nh = choice([1,1,2,2,2,3,3,3,4,5])
lvl.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 self.closed:
lvl.genwalls.append((RandomLevel.close,))
if self.smooth > 0:
# smooth away all lone empty spaces
lvl.genwalls.append((RandomLevel.smooth, 1.0, 1))
# possibly smooth away some lone bricks
if self.smooth == 2:
lvl.genwalls.append((RandomLevel.smooth, 0.25, 0))
elif self.smooth == 3:
lvl.genwalls.append((RandomLevel.smooth, 0.75, 0))
if self.startplats:
lvl.genwalls.append((RandomLevel.startplatform, ))
lvl.genwalls.append((RandomLevel.openstartway, ))
if self.makespace:
lvl.genwalls.append((RandomLevel.make_space, ))
if self.straightfall:
lvl.genwalls.append((RandomLevel.prevent_straight_fall, ))
if self.mirrored:
lvl.genwalls.append((RandomLevel.mirror, ))
if self.enlargeholes:
lvl.genwalls.append((RandomLevel.enlarge_tiny_holes, ))
lvl.genwalls.append((RandomLevel.generate_wind, ))
b = self.bonuses
for name in Bonuses:
setattr(lvl, name, (b % 3) == 1)
b = b // 3
lvl.autogen_shape = self
def generate_shape(prevlist):
tests = Shape.all_tests
s = Shape()
for i in range(100):
s1 = Shape(s)
random.shuffle(tests)
for test in tests:
test(s1, prevlist)
if not s1.modified and s1 == s:
break
s = s1
# else:
# sys.stdout.write('*')
del s.modified
return s
def makeshapes(nblevels=25):
shapelist = []
for i in range(nblevels):
s = generate_shape(shapelist)
s.difficulty = float(i+1)/nblevels
yield s
shapelist.append(s)
if len(shapelist) == 10:
del shapelist[:]
def GenerateLevels():
# print 'generating levels',
Levels = []
for s in makeshapes():
class level(RandomLevel):
WIDTH = 28
HEIGHT = 23
def enter(self, *args, **kw):
result = RandomLevel.enter(self, *args, **kw)
params = self.autogen_shape.__dict__.items()
params.sort()
# for keyvalue in params:
# print '%20s: %s' % keyvalue
return result
s.accept(level)
Levels.append(level)
# sys.stdout.write('.')
# sys.stdout.flush()
# print
class levelfinal(RandomLevel):
WIDTH = level.WIDTH
HEIGHT = level.HEIGHT
genwalls = [(RandomLevel.platforms,(5,3),(lambda:flat(2,1),lambda:flat(6,2)),1),
(RandomLevel.close,)]
Levels.append(levelfinal)
return Levels
def GenerateSingleLevel(width, height):
[s] = makeshapes(1)
class level(RandomLevel):
WIDTH = width
HEIGHT = height
s.accept(level)
return level
if __name__ == '__main__':
for s in makeshapes():
print s.__dict__
else:
rnglevel = {}
execfile('levels/rnglevel', rnglevel)
RandomLevel = rnglevel['RandomLevel']