Enable Javascript in your browser and then refresh this page, for a much enhanced experience.
Sometimes Visual Debugging helps the most. solution in Creative category for Find Enemy by TorinPena
#This is setup to where it can have custom values for rows and columns, but...
#the checks to ensure those new rows and columns are not implemented yet.
def find_enemy(you, dir, enemy, new_rows = None, new_cols = None,
rows = list(range(1, 10)), cols = "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
directions = {'N': 0, 'NE': 1, 'SE': 2, 'S': 3, 'SW': 4, 'NW': 5},
hexes = dict()):
#Check if the new scheme will work if new values are passed.
if new_rows is not None or new_cols is not None:
#This is a bonus feature. It might be implemented later.
hexes = assemble_hex_trans(rows, cols)
#Implemented separately for simplicity.
elif len(hexes) == 0:
hexes = assemble_hex_trans(rows, cols)
yc = hexes[you]
ec = hexes[enemy]
#The distance is equal to the maximum difference between matching coord points.
diff = [y-e for y,e in zip(yc,ec)]
dist = max(map(abs, diff))
#Then get directions.
facing = directions[dir]
#Gets relative axises, so any facing can use the exact same calculations.
def get_axis(d):
#print(d, diff[(facing+d)%3], (facing+d)//3%2)
return diff[(d-facing)%3]*(-1 if facing - 3 < d % 3 <= facing else 1)
rel_axis = tuple(get_axis(i) for i in range(3))
# It took a lot of fine tuning to get the math working properly
# and these statements helped a lot with that.
#hex_visual(rel_axis)
#hex_visual(diff)
#print(facing, diff, tuple(rel_axis))
#To get straight up or down, add the diagonal values.
front_to_back = (rel_axis[1]+rel_axis[2])
left_to_right = rel_axis[0]
#The second condition might be superfluous but it adds clarity.
if abs(front_to_back) > abs(left_to_right) or left_to_right == 0:
return ["F" if front_to_back > 0 else "B", dist]
else:
return ["R" if left_to_right > 0 else "L", dist]
# Translates hexes into 3 axises, which increase as you move up.
# N/S coord, NE/SW coord, and then NW/SE coord.
# Or vertical, forward diagonal, then backward diagonal.
# At one point I was going to have parallel coordinates too but then I got this working.
def assemble_hex_trans(rows, cols):
return {str(c)+str(r): (j, i - j // 2, i + (j + 1) // 2)
for i, r in enumerate(rows) for j, c in enumerate(cols)}
#A couple of visual debugging functions.
#Outputs a nicely formatted table of hexes with "*" being empty,
#"^" being the player facing up, and "E" being the enemy.
def hex_visual(rel_axis):
c, fd, bd = rel_axis
r = fd+bd
assert (c+r)%2 == 0, "Invalid input."
#chars = (" ", "*", "^", "E")
chars = ("*", "^", " ", "E")
#Front determines vertical placement of yourself and enemy.
#Right determines whether it's placed first or last in the line.
#Both use improvised cmp() functions.
front = (r > 0) - (r < 0)
right = (c > 0) - (c < 0)
#Composes and prints the middle lines.
def line_comp(first, r = r, c = c):
for i in range(front, r, front):
line = " ".join([chars[(j + first) % 2 * 2] for j in range(0, abs(c) + 1)])
print(str(r - i if front > 0 else i).rjust(3) + "| " + line + " |")
first = not first
#A header to make reading this nicer.
print("Relative Axis", rel_axis, "visualization:")
#print(c, r)
#And a numbered header with a check that puts a | down if line[3] is a space.
colrange = list(range(0,c+right, right)) if right != 0 else [0]
line = " " + "".join(map(lambda i: str(i).rjust(3),
colrange if right > 0 else reversed(colrange))) + " |"
if line[3] == " ": line = " |" + line[4:]
print(line)
separator = "---+-" + "--".join(["-"] * (abs(c) + 1)) + "-+"
print(separator)
#On a horizontal line.
if front == 0:
line = " ".join([chars[2 + right]] +
[chars[(i % 2) * 2] for i in range(1, abs(c))] +
[chars[2 - right]])
print(" 0| " + line + " |")
print(separator)
return
#Vertically aligned.
elif right == 0:
print(str(r if front > 0 else 0).rjust(3) + "| " + chars[2 + front] + " |")
line_comp(True)
print(str(0 if front > 0 else r).rjust(3) + "| " + chars[2 - front] + " |")
#Forwards diagonal between them.
elif front == right:
#The first line does not start empty if c is even.
first = c%2 == 1
#The first line always ends with the player or the enemy.
line = " ".join([chars[(i + (c % 2 == 1)) % 2 * 2] for i in range(0, abs(c))] +
[chars[2 + front]])
print(str(r if front > 0 else 0).rjust(3) + "| " + line + " |")
#Flip first to get whether the second line starts empty.
first = not first
line_comp(first)
#The last line always starts with the player or the enemy.
line = " ".join([chars[2 - front]] +
[chars[i % 2 * 2] for i in range(1, abs(c) + 1)])
print(str(0 if front > 0 else r).rjust(3) + "| " + line + " |")
#Backwards diagonal between them.
else:
#The first line always starts with the player or the enemy.
line = " ".join([chars[2 + front]] +
[chars[i % 2 * 2] for i in range(1, abs(c) + 1)])
print(str(r if front > 0 else 0).rjust(3) + "| " + line + " |")
#The line after that always starts empty, but we need the end value.
last = True
line_comp(last)
#The last line always ends with the player or the enemy.
line = " ".join([chars[(i + (c % 2 == 1)) % 2 * 2] for i in range(0, abs(c))] +
[chars[2 - front]])
print(str(0 if front > 0 else r).rjust(3) + "| " + line + " |")
print(separator)
# Had a possible_solutions function here but it was never fully implemented nor
# was it used that much for debugging.
#Outputs a simple visual of how each axis works with hexes. P is the player facing north.
#A very helpful function for debugging. Has function tests to debug rotating the axis.
#Never did get around to implementing the parallel coordinate system.
def sign_visual(pos_func = None, axis_func = None, per = True, hexes = dict()):
#This is the absolute minimum size of hexes to test assemble_hex_trans.
if len(hexes) == 0: hexes = assemble_hex_trans("TCB","LMR")
if axis_func is None: axis_func = lambda axis: axis % 3
if pos_func is None: pos_func = lambda axis: 1
hex_comp = lambda axis, h1, h2: (hexes[h1][axis] - hexes[h2][axis]) * pos_func(axis)
sign = lambda n: "0" if n == 0 else ("+" if n > 0 else "-")
#print(hexes)
if per:
pairs = ("LB", "LC", "MT", "RC", "RB", "MB", "LB", "LC", "MT", "RC", "RB", "MB")
#Only needing to do the below after switching fd and bd's values was a good sign.
pairs = tuple(reversed(pairs))
def hex_sign(i, j = 0): return sign(hex_comp(axis_func(i), pairs[i + j], "MC"))
align_test = lambda i: all(hex_sign(j - i, i) == hex_sign(j - i, i + 1)
for j in range(i, i + 6))
"""print([[(hex_sign(j - i, i), hex_sign(j - i, i + 1)) for j in range(i, i+6)]
for i in range(0, 6)])
print([[hex_sign(j - i, i) == hex_sign(j - i, i + 1) for j in range(i, i+6)]
for i in range(0, 6)])"""
start = list(filter(lambda i: align_test(i), range(0, 6)))
assert len(start) > 0, "No proper alignment exists."
#Adjust alignment to work.
pairs = pairs[start[0]:start[0]+6]
def test2(i):
c1 = hex_sign(i)
c2 = hex_sign(i + 3)
return c1 != c2 and c1 != "0" and c2 != "0"
for i in range(0, 3):
assert test2(i), "Axis on " + pairs[i] + " to " + pairs[i + 3] + " does not provide correct measurements."
#Gets the values and shows how they work.
indexes = [pairs.index("LB"), pairs.index("LC"), pairs.index("MT"),
pairs.index("RC"), pairs.index("RB"), pairs.index("MB")]
#print(indexes)
for i in indexes[:]: indexes[i] = hex_sign(i) + str(i)
l, tl, tr, r, br, bl = indexes
print(indexes)
#Done on three lines to make it clear what the structure is.
print(" ", tl, "", tr, "\n")
print(l, " PL ", r, "\n")
print(" ", bl, "" , br)
#And a final test.
hexes = assemble_hex_trans(range(1,6), "ABCDE")
#Testing column is useless. So test the diagonals.
from collections import defaultdict
fd = defaultdict(set)
bd = defaultdict(set)
fd_let = {0: "A", 1: "BC", 2: "DE"}
bd_let = {0: "AB", 1: "CD", 2: "E"}
for i in range(1, 6):
for j in range(0, 3):
fd[i + j] |= {c+str(i) for c in fd_let[j]}
bd[i - j] |= {c+str(i) for c in bd_let[j]}
print({k-1:{hexes[h][1] for h in v} for k, v in fd.items()})
print({k-1:{hexes[h][2] for h in v} for k, v in bd.items()})
assert all(len(set(map(lambda h: hexes[h][2], d))) <= 1 for d in fd.values()), "Forward diagonals do not calculate properly."
assert all(len(set(map(lambda h: hexes[h][1], d))) <= 1 for d in bd.values()), "Backward diagonal calculation error."
if __name__ == '__main__':
#Lots of visual debugging to ensure the rotations worked as intended.
sign_visual()
facing = 0
#axis_func = lambda axis: rotate[axis % 3]
axis_func = lambda axis: (axis + facing) % 3
#< makes col go from - to + while <= makes col go from + to -.
#sign_func = lambda axis: -1 if axis % 3 <= facing % 3 else 1
sign_func = lambda axis: -1 if facing - 3 < axis % 3 <= facing else 1
"""for facing in range(0, 6):
print("Facing:", facing)
#print([i-facing for i in range(0,6)])
#print([sign_func(i) for i in range(0,6)])
#This just confirms the first comparison is superfluous.
#print([facing % 3 - 3 < i % 3 for i in range(0, 6)])
#sign_visual(lambda a: 1, axis_func)
sign_visual(sign_func, axis_func)
rotate = rotate[1:] + rotate[0:1]"""
#And then the normal assertions.
assert find_enemy('G5', 'N', 'G4') == ['F', 1], "N-1"
assert find_enemy('G5', 'N', 'I4') == ['R', 2], "NE-2"
assert find_enemy('G5', 'N', 'J6') == ['R', 3], "SE-3"
assert find_enemy('G5', 'N', 'G9') == ['B', 4], "S-4"
assert find_enemy('G5', 'N', 'B7') == ['L', 5], "SW-5"
assert find_enemy('G5', 'N', 'A2') == ['L', 6], "NW-6"
assert find_enemy('G3', 'NE', 'C5') == ['B', 4], "[watch your six!]"
assert find_enemy('H3', 'SW', 'E2') == ['R', 3], "right"
assert find_enemy('A4', 'S', 'M4') == ['L', 12], "true left"
print("You are good to go!")
Nov. 10, 2017
Comments: