RosettaCodeData/Task/RPG-attributes-generator/Python/rpg-attributes-generator-4.py

118 lines
2.6 KiB
Python

'''RPG Attributes Generator'''
from itertools import islice
from random import randint
from operator import eq
# heroes :: Gen IO [(Int, Int, Int, Int, Int, Int)]
def heroes(p):
'''Non-finite list of heroes matching
the requirements of predicate p.
'''
while True:
yield tuple(
until(p)(character)([])
)
# character :: () -> IO [Int]
def character(_):
'''A random character with six
integral attributes.
'''
return [
sum(sorted(map(
randomRInt(1)(6),
enumFromTo(1)(4)
))[1:])
for _ in enumFromTo(1)(6)
]
# ------------------------- TEST --------------------------
# main :: IO ()
def main():
'''Test :: Sample of 10'''
# seventyFivePlusWithTwo15s :: [Int] -> Bool
def seventyFivePlusIncTwo15s(xs):
'''Sums to 75 or more,
and includes at least two 15s.
'''
return 75 <= sum(xs) and (
1 < len(list(filter(curry(eq)(15), xs)))
)
print('A sample of 10:\n')
print(unlines(
str(sum(x)) + ' -> ' + str(x) for x
in take(10)(heroes(
seventyFivePlusIncTwo15s
))
))
# ------------------------- GENERIC -------------------------
# curry :: ((a, b) -> c) -> a -> b -> c
def curry(f):
'''A curried function derived
from an uncurried function.
'''
return lambda x: lambda y: f(x, y)
# enumFromTo :: Int -> Int -> [Int]
def enumFromTo(m):
'''Enumeration of integer values [m..n]'''
return lambda n: range(m, 1 + n)
# randomRInt :: Int -> Int -> IO () -> Int
def randomRInt(m):
'''The return value of randomRInt is itself
a function. The returned function, whenever
called, yields a a new pseudo-random integer
in the range [m..n].
'''
return lambda n: lambda _: randint(m, n)
# take :: Int -> [a] -> [a]
# take :: Int -> String -> String
def take(n):
'''The prefix of xs of length n,
or xs itself if n > length xs.
'''
return lambda xs: (
xs[0:n]
if isinstance(xs, (list, tuple))
else list(islice(xs, n))
)
# unlines :: [String] -> String
def unlines(xs):
'''A single string formed by the intercalation
of a list of strings with the newline character.
'''
return '\n'.join(xs)
# until :: (a -> Bool) -> (a -> a) -> a -> a
def until(p):
'''The result of repeatedly applying f until p holds.
The initial seed value is x.
'''
def go(f, x):
v = x
while not p(v):
v = f(v)
return v
return lambda f: lambda x: go(f, x)
if __name__ == '__main__':
main()