Created
January 20, 2013 00:32
-
-
Save mstepniowski/3947539c9905898c8480 to your computer and use it in GitHub Desktop.
My little Python helper for http://adventure.cueup.com/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
######### | |
# LEVEL 1 | |
######### | |
RANDOM_SEQ = [6, 19, 16] | |
def vax_mth_random(seed): | |
"""VAX's MTH$RANDOM is a linear congruential generator. | |
It's form is: x_{n+1} = (a x_n + c) \mod m | |
where a = 69069, c = 1, m = 2^32 | |
See: | |
- http://en.wikipedia.org/wiki/Linear_congruential_generator | |
- gsl_rng_vax from http://www.gnu.org/software/gsl/manual/html_node/Other-random-number-generators.html | |
""" | |
n = seed | |
while True: | |
n = (69069 * n + 1) % 2**32 | |
yield n | |
# First three numbers from seed = 6 turned out to match the RANDOM_SEQ | |
######### | |
# LEVEL 2 | |
######### | |
### Parens | |
PARENS = """{{[{{{{}}{{}}}[]}[][{}][({[(( | |
{{[][()()]}}{[{{{}}}]}))][()] | |
{[[{((()))({}(())[][])}][]()] | |
}{()[()]}]})][]]}{{}[]}}""".replace('\n', '') | |
PAREN_PAIRS = {'}': '{', ')': '(', ']': '['} | |
def find_error(parens): | |
"""Finds the index of the first error in paren sequence.""" | |
s = [] | |
for idx, paren in enumerate(parens): | |
if paren in PAREN_PAIRS.values(): | |
s.append(paren) | |
else: | |
last_open_paren = s.pop() | |
if last_open_paren != PAREN_PAIRS[paren]: | |
return idx | |
### Map | |
MAP = """8 8 4 4 ^ | |
4 9 6 4 8 | |
8 6 4 1 2 | |
4 8 2 6 3 | |
* 6 8 8 4""".replace(' ', '').split('\n') | |
DIRECTIONS = {(0, 1): 'e', | |
(0, -1): 'w', | |
(1, 0): 's', | |
(-1, 0): 'n'} | |
def find_char(map, char): | |
for x, row in enumerate(map): | |
for y, c in enumerate(row): | |
if c == char: | |
return x, y | |
def find_neighbours(map, x, y, money, t): | |
result = [] | |
for i, j in DIRECTIONS.keys(): | |
if x + i < 0 or y + j < 0: | |
continue | |
try: | |
map[x + i][y + j] | |
except IndexError: | |
pass | |
else: | |
if money >= get_cost(map, x + i, y + j, t): | |
result.append((x + i, y + j, money - get_cost(map, x + i, y + j, t), t + 1)) | |
return result | |
def get_cost(map, x, y, t): | |
if map[x][y] == '^': | |
return 5 | |
elif map[x][y] == '*': | |
return 0 | |
else: | |
# Here t should be added only for bonus round calculations. | |
# For non-bonus rounds, remove t. | |
return int(map[x][y]) + t | |
def find_route(m, money): | |
"""Finds the route through the map m with cost equal to money. | |
Returns transition table of moves searched through. To get only | |
the moves leading to the goal, use follow_transitions. | |
""" | |
start = find_char(m, '*') | |
end = find_char(m, '^') | |
visited = set([(start[0], start[1], 35, 0)]) | |
moves = find_neighbours(m, start[0], start[1], money, 0) | |
transitions = {} | |
while len(moves) > 0: | |
x, y, money, t = moves.pop() | |
if (x, y) == end and money == 0: | |
# Reached goal | |
return transitions | |
else: | |
visited.add((x, y, money, t)) | |
possible_moves = [move for move in find_neighbours(m, x, y, money, t) | |
if move not in visited] | |
for move in possible_moves: | |
transitions[move] = (x, y, money, t) | |
moves.extend(possible_moves) | |
def follow_transitions(t, dest): | |
result = [dest] | |
while True: | |
if t.get(result[-1]): | |
result.append(t.get(result[-1])) | |
else: | |
return list(reversed(result)) | |
from itertools import tee, izip | |
def pairwise(iterable): | |
"""pairwise([1, 2, 3, 4]) -> ([1, 2], [2, 3], [3, 4])""" | |
a, b = tee(iterable) | |
next(b, None) | |
return izip(a, b) | |
def directions(moves): | |
for m1, m2 in pairwise(moves): | |
yield DIRECTIONS[(m2[0] - m1[0], m2[1] - m1[1])] | |
############# | |
# BONUS LEVEL | |
############# | |
### VAX's MTH$RANDOM | |
LONG_RANDOM_SEQ = [34, 27, 16, 1, 34, 31, 24, 17, 34, 35, 16, 13] | |
# Brute force! | |
def next_three(): | |
random = vax_mth_random(6) | |
results = [] | |
while results[-12:] != LONG_RANDOM_SEQ: | |
results.append(next(random) % 36) | |
if len(results) > 10000: | |
results = results[-12:] | |
return [next(random) for x in range(3)] | |
# Solution: [26, 19, 4] | |
### Map | |
LARGE_MAP = """* 8 1 7 8 8 5 2 9 5 9 5 | |
8 5 1 1 5 6 9 4 4 5 2 1 | |
7 2 3 5 2 9 2 6 9 3 9 4 | |
9 2 5 9 8 9 5 7 7 5 9 6 | |
2 4 6 7 1 4 2 6 6 2 5 8 | |
2 8 1 5 3 8 4 9 7 5 2 3 | |
2 9 3 5 6 7 2 4 9 4 2 5 | |
6 3 1 7 8 2 3 3 6 7 9 3 | |
2 5 7 4 2 7 8 5 5 3 5 8 | |
5 2 9 8 3 6 1 4 9 5 6 3 | |
4 6 9 8 5 4 9 7 6 4 6 8 | |
2 7 7 1 9 9 7 3 7 2 2 ^""".replace(' ', '').split('\n') | |
# Same algorithm as in the smaller case. Just add turn number to move cost. | |
# | |
# Solution: eeeeeeeeeeeweswssssessssss |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment