import std; void main() { slurp!(char, ulong)("input", "%c%d").getManhattanDistanceForInstructions.writeln; } enum Direction { east = 0, north = 1, west = 2, south = 3 } alias Instruction = Tuple!(char, ulong); struct ShipState { immutable long posX = 0; immutable long posY = 0; immutable Direction direction = Direction.east; private auto calculateXMovement(Direction direction, ulong value) { switch (direction) with (Direction) { case east: return value; case west: return -value; default: return 0; } } private auto calculateYMovement(Direction direction, ulong value) { switch (direction) with (Direction) { case north: return value; case south: return -value; default: return 0; } } ShipState withInstructionApplied(Instruction instruction) { return [ 'E': (ulong value) => ShipState(posX + value, posY, direction), 'N': (ulong value) => ShipState(posX, posY + value, direction), 'W': (ulong value) => ShipState(posX - value, posY, direction), 'S': (ulong value) => ShipState(posX, posY - value, direction), 'L': (ulong value) => ShipState(posX, posY, ((direction + value / 90) % 4).to!Direction), 'R': (ulong value) => ShipState(posX, posY, ((direction - value / 90 + 4) % 4).to!Direction), 'F': (ulong value) => ShipState(posX + calculateXMovement(direction, value), posY + calculateYMovement(direction, value), direction) ][instruction[0]](instruction[1]); } ShipState withInstructionsApplied(Instruction[] instructions) { if (instructions.empty) { return this; } return withInstructionApplied(instructions[0]).withInstructionsApplied(instructions[1 .. $]); } } auto getManhattanDistanceForInstructions(Instruction[] instructions) { auto finalState = ShipState().withInstructionsApplied(instructions); return abs(finalState.posX) + abs(finalState.posY); } unittest { auto input = [ tuple('F', 10UL), tuple('N', 3UL), tuple('F', 7UL), tuple('R', 90UL), tuple('F', 11UL) ]; assert(input.getManhattanDistanceForInstructions == 25); }