mirror of https://github.com/dirtbags/moth.git
126 lines
3.4 KiB
Python
126 lines
3.4 KiB
Python
|
import random
|
||
|
import math
|
||
|
|
||
|
# Not defined in older Python's math libs
|
||
|
def multiply(a, b):
|
||
|
return a * b
|
||
|
|
||
|
def factorial(n):
|
||
|
return reduce(multiply, range(1, n+1))
|
||
|
|
||
|
OPS = [lambda a, b: a + b,
|
||
|
lambda a, b: a - b,
|
||
|
lambda a, b: a * b,
|
||
|
lambda a, b: a // b,
|
||
|
lambda a, b: a % b,
|
||
|
lambda a, b: a ^ b,
|
||
|
lambda a, b: a | b,
|
||
|
lambda a, b: a & b,
|
||
|
lambda a, b: max(a,b),
|
||
|
lambda a, b: min(a,b),
|
||
|
lambda a, b: a+b//2,
|
||
|
lambda a, b: ~b,
|
||
|
lambda a, b: a + b + 3,
|
||
|
lambda a, b: max(a,b)//2,
|
||
|
lambda a, b: min(a,b)*3,
|
||
|
lambda a, b: a % 2,
|
||
|
lambda a, b: int(math.degrees(b + a)),
|
||
|
lambda a, b: ~(a & b),
|
||
|
lambda a, b: ~(a ^ b),
|
||
|
lambda a, b: a + b - a%b,
|
||
|
lambda a, b: (a > 0) and (factorial(a)//factorial(a-b)) or 0,
|
||
|
lambda a, b: (b%a) * (a%b),
|
||
|
lambda a, b: factorial(a)%b,
|
||
|
lambda a, b: int(math.sin(a)*b),
|
||
|
lambda a, b: b + a%2,
|
||
|
lambda a, b: a - 1 + b%3,
|
||
|
lambda a, b: a & 0xaaaa,
|
||
|
lambda a, b: a == b and 5 or 6,
|
||
|
lambda a, b: b % 17,
|
||
|
lambda a, b: int( cos( math.radians(b) ) * a )]
|
||
|
|
||
|
SYMBOLS = '.,<>?/!@#$%^&*()_+="~|;:'
|
||
|
MAX = 100
|
||
|
|
||
|
PLAYER_DIR = ''
|
||
|
|
||
|
def mkPuzzle(lvl):
|
||
|
"""Make a puzzle. The puzzle is a simple integer math equation. The trick
|
||
|
is that the math operators don't do what you might expect, and what they do
|
||
|
is randomized each time (from a set list of functions). The equation is
|
||
|
evaluated left to right, with no other order of operations.
|
||
|
|
||
|
The level determins both the length of the puzzle, and what functions are
|
||
|
enabled. The number of operators is half the level+2, and the number of
|
||
|
functions enabled is equal to the level.
|
||
|
|
||
|
returns the key, puzzle, and the set of numbers used.
|
||
|
"""
|
||
|
|
||
|
ops = OPS[:lvl + 1]
|
||
|
length = (lvl + 2)//2
|
||
|
|
||
|
key = {}
|
||
|
|
||
|
bannedNums = set()
|
||
|
|
||
|
puzzle = []
|
||
|
for i in range(length):
|
||
|
num = random.randint(1,MAX)
|
||
|
bannedNums.add(num)
|
||
|
puzzle.append( num )
|
||
|
symbol = random.choice(SYMBOLS)
|
||
|
if symbol not in key:
|
||
|
key[symbol] = random.randint(0, len(ops) - 1)
|
||
|
puzzle.append( symbol )
|
||
|
|
||
|
num = random.randint(1,MAX)
|
||
|
bannedNums.add(num)
|
||
|
puzzle.append( num )
|
||
|
|
||
|
return key, puzzle, bannedNums
|
||
|
|
||
|
def parse(puzzle):
|
||
|
"""Parse a puzzle string. If the string contains symbols not in
|
||
|
SYMBOLS, a ValueError is raised."""
|
||
|
|
||
|
parts = [puzzle]
|
||
|
for symbol in SYMBOLS:
|
||
|
newParts = []
|
||
|
for part in parts:
|
||
|
if symbol in part:
|
||
|
terms = part.split(symbol)
|
||
|
newParts.append( terms.pop(0))
|
||
|
while terms:
|
||
|
newParts.append(symbol)
|
||
|
newParts.append( terms.pop(0) )
|
||
|
else:
|
||
|
newParts.append(part)
|
||
|
parts = newParts
|
||
|
|
||
|
finalParts = []
|
||
|
for part in parts:
|
||
|
part = part.strip()
|
||
|
if part in SYMBOLS:
|
||
|
finalParts.append( part )
|
||
|
else:
|
||
|
try:
|
||
|
finalParts.append( int(part) )
|
||
|
except:
|
||
|
raise ValueError("Invalid symbol: %s" % part)
|
||
|
|
||
|
return finalParts
|
||
|
|
||
|
def solve(key, puzzle):
|
||
|
|
||
|
puzzle = list(puzzle)
|
||
|
stack = puzzle.pop(0)
|
||
|
|
||
|
while puzzle:
|
||
|
symbol = puzzle.pop(0)
|
||
|
nextVal = puzzle.pop(0)
|
||
|
op = OPS[key[symbol]]
|
||
|
stack = op(stack, nextVal)
|
||
|
|
||
|
return stack
|