private static void CalculateActionLookups()
        {
            AdjacentMovementCalculations = new AdjacentMovementCalculation[ADJACENT_MOVEMENT_CALCULATION_LOOKUP_COUNT];

            int actionCalculationId = 0;

            Direction[] directions = (Direction[])Enum.GetValues(typeof(Direction));

            foreach (Direction movingOrFiringDirection in directions)
            {
                actionCalculationId = (byte)movingOrFiringDirection;
                foreach (Direction currentDir in directions)
                {
                    actionCalculationId |= ((int)currentDir) << 3;
                    foreach (SegmentState segState in Enum.GetValues(typeof(SegmentState)))
                    {
                        actionCalculationId |= ((int)segState) << 6;

                        AdjacentMovementCalculation calculation = new AdjacentMovementCalculation(
                            (byte)actionCalculationId, movingOrFiringDirection, currentDir, segState);
                        PerformMovementCalculationsToAdjacentCell(calculation);
                        PerformSnipingCalculationsToAdjacentCell(calculation);

                        AdjacentMovementCalculations[actionCalculationId] = calculation;
                    }
                }
            }
        }
        public static void PerformMovementCalculationsToAdjacentCell(AdjacentMovementCalculation calculation)
        {
            switch (calculation.MovingOrFiringDirection)
            {
            case Direction.DOWN:
            case Direction.LEFT:
            case Direction.RIGHT:
            case Direction.UP:
                switch (calculation.SegmentStateInMovementDirection)
                {
                case SegmentState.Clear:
                    // Move directly into the adjacent cell:
                    calculation.IsAdjacentCellReachable       = true;
                    calculation.TicksTakenToReachAdjacentCell = 1;
                    calculation.MovementActions = new TankAction[]
                    {
                        calculation.MovingOrFiringDirection.ToTankAction()
                    };
                    break;

                case SegmentState.ShootableWall:
                    calculation.IsAdjacentCellReachable = true;
                    if (calculation.CurrentDirectionOnSourceCell == calculation.MovingOrFiringDirection)
                    {
                        // Shoot the wall, then move into the adjacent cell:
                        calculation.TicksTakenToReachAdjacentCell = 2;
                        calculation.MovementActions = new TankAction[]
                        {
                            TankAction.FIRE,
                            calculation.MovingOrFiringDirection.ToTankAction()
                        };
                    }
                    else
                    {
                        // Move in direction to turn the tank, then shoot the wall, then move into the adjacent cell
                        calculation.TicksTakenToReachAdjacentCell = 3;
                        calculation.MovementActions = new TankAction[]
                        {
                            calculation.MovingOrFiringDirection.ToTankAction(),
                                TankAction.FIRE,
                                calculation.MovingOrFiringDirection.ToTankAction()
                        };
                    }
                    break;

                default:
                    // case SegmentState.OutOfBounds:
                    // case SegmentState.UnshootablePartialWall:
                    calculation.IsAdjacentCellReachable       = false;
                    calculation.TicksTakenToReachAdjacentCell = Constants.UNREACHABLE_DISTANCE;
                    calculation.MovementActions = new TankAction[] { };
                    break;
                }
                break;

            default:
                // case TankAction.NONE:
                // case TankAction.FIRE:
                calculation.IsAdjacentCellReachable       = false; // i.e. not with this action
                calculation.TicksTakenToReachAdjacentCell = Constants.UNREACHABLE_DISTANCE;
                calculation.MovementActions = new TankAction[] { calculation.MovingOrFiringDirection.ToTankAction() };
                // i.e. Just the current action (even though it won't actually reach adjacent cell)
                break;
            }
        }
        private static void PerformSnipingCalculationsToAdjacentCell(AdjacentMovementCalculation calculation)
        {
            switch (calculation.MovingOrFiringDirection)
            {
            case Direction.DOWN:
            case Direction.LEFT:
            case Direction.RIGHT:
            case Direction.UP:
                switch (calculation.SegmentStateInMovementDirection)
                {
                case SegmentState.Clear:
                    if (calculation.CurrentDirectionOnSourceCell == calculation.MovingOrFiringDirection)
                    {
                        // Already in position for sniping from the current cell:
                        calculation.IsSnipingPossibleWithThisAction     = true;
                        calculation.TicksTakenToMoveIntoSnipingPosition = 0;
                        calculation.SnipingActions = new TankAction[] { };
                    }
                    else
                    {
                        /* You can't turn to face in the right direction without moving off this cell:
                         *
                         * Note: In theory one could move off and back to the cell.
                         *       But this is an edge case, so only cater for it if a need arises.
                         */
                        calculation.IsSnipingPossibleWithThisAction     = false;
                        calculation.TicksTakenToMoveIntoSnipingPosition = Constants.UNREACHABLE_DISTANCE;
                        calculation.SnipingActions = new TankAction[] { };
                    }
                    break;

                case SegmentState.ShootableWall:
                case SegmentState.UnshootablePartialWall:
                    // The wall can be shot into or through, and it allows a change of direction:
                    calculation.IsSnipingPossibleWithThisAction = true;
                    if (calculation.CurrentDirectionOnSourceCell == calculation.MovingOrFiringDirection)
                    {
                        calculation.TicksTakenToMoveIntoSnipingPosition = 0;
                        calculation.SnipingActions = new TankAction[] { };
                    }
                    else
                    {
                        calculation.TicksTakenToMoveIntoSnipingPosition = 1;
                        calculation.SnipingActions = new TankAction[]
                        {
                            calculation.MovingOrFiringDirection.ToTankAction()
                        };
                    }
                    break;

                default:
                    // case SegmentState.OutOfBounds:
                    calculation.IsSnipingPossibleWithThisAction     = false;
                    calculation.TicksTakenToMoveIntoSnipingPosition = Constants.UNREACHABLE_DISTANCE;
                    calculation.SnipingActions = new TankAction[] { };
                    break;
                }
                break;

            default:
                // case Direction.NONE:
                calculation.IsSnipingPossibleWithThisAction     = false;  // i.e. not if you won't say what direction you want to snipe in!
                calculation.TicksTakenToMoveIntoSnipingPosition = Constants.UNREACHABLE_DISTANCE;
                calculation.SnipingActions = new TankAction[] { };
                break;
            }
        }