moth/ctf/badmath.py

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