"""MegaMek's hex geometry, ported exactly. Offset coordinates, odd columns shifted down, directions 0-5 clockwise from north. The two functions below are line-for-line ``Coords.xInDir`` and ``Coords.yInDir`` from ``megamek.common.board.Coords``; a bot that computes neighbours any other way will disagree with the server about where a step lands, and the disagreement shows up as illegal paths rather than as an error. This is the first piece of MegaMek's rules to be reimplemented outside the JVM. It will not be the last, and each one arrives with the same obligation: match the original exactly, and say where it came from. """ def x_in_dir(x: int, y: int, direction: int, distance: int = 1) -> int: if direction in (1, 2): return x + distance if direction in (4, 5): return x - distance return x def y_in_dir(x: int, y: int, direction: int, distance: int = 1) -> int: if direction == 0: return y - distance if direction in (1, 5): return y - ((distance + 1 - (x & 1)) // 2) if direction in (2, 4): return y + ((distance + (x & 1)) // 2) return y + distance def translated(x: int, y: int, direction: int, distance: int = 1) -> tuple[int, int]: """The hex `distance` away in `direction`.""" return x_in_dir(x, y, direction, distance), y_in_dir(x, y, direction, distance) def distance(ax: int, ay: int, bx: int, by: int) -> int: """Hex distance, via cube coordinates. MegaMek computes this with its own loop; cube distance is the same number for these coordinates and is easier to check by eye. """ return _cube_distance(_to_cube(ax, ay), _to_cube(bx, by)) def _to_cube(x: int, y: int) -> tuple[int, int, int]: # Odd-q offset to cube. Columns are x, and odd columns sit half a hex lower, # which is the (x & 1) term everywhere in this file. q = x r = y - ((x - (x & 1)) // 2) return q, r, -q - r def _cube_distance(a: tuple[int, int, int], b: tuple[int, int, int]) -> int: return max(abs(a[0] - b[0]), abs(a[1] - b[1]), abs(a[2] - b[2]))