public static List<Move> ParseMoves(Map fullMap, string line)
        {
            var result = new List<Move>();
            var moveInput = InputParser.Split(line);
            for (int i = 1; i < moveInput.Length; i++)
            {
                try
                {
                    Move move;
                    if (moveInput[i + 1].Equals("place_armies"))
                    {
                        Region region = fullMap.GetRegionByID(int.Parse(moveInput[i + 2]));
                        String playerName = moveInput[i];
                        int armies = int.Parse(moveInput[i + 3]);
                        move = new PlaceArmiesMove(playerName, region, armies);
                        i += 3;
                    }
                    else if (moveInput[i + 1].Equals("attack/transfer"))
                    {
                        Region fromRegion = fullMap.GetRegionByID(int.Parse(moveInput[i + 2]));
                        Region toRegion = fullMap.GetRegionByID(int.Parse(moveInput[i + 3]));

                        String playerName = moveInput[i];
                        int armies = int.Parse(moveInput[i + 4]);
                        move = new AttackTransferMove(playerName, fromRegion, toRegion, toRegion, armies, MoveReason.Unknown);
                        i += 4;
                    }
                    else
                    {
                        // Should never happen.
                        continue;
                    }

                    result.Add(move);
                }
                catch (Exception ex)
                {
                    Console.Error.WriteLine("Unable to parse moves: " + ex);
                }
            }

            return result;
        }
        public static void HandleAttack(AttackTransferMove move, GameState gameState)
        {
            var fromRegion = gameState.FullMap.GetRegionByID(move.FromRegion.ID);
            var toRegion = gameState.FullMap.GetRegionByID(move.ToRegion.ID);

            ConsoleExtensions.WriteColoredLine(ConsoleColor.Yellow, "Player '{0}' attacks region {1} ({2} armies) from region {3} ({4} armies) with {5} armies for {6}.", move.PlayerName, toRegion.ID, toRegion.Armies, fromRegion.ID, fromRegion.Armies, move.Armies, move.MoveReason);

            int attackers = move.Armies;
            int defenders = toRegion.Armies;

            int attackersLost;
            int defendersLost;

            if (toRegion.PlayerName == "neutral" && toRegion.Armies == 2)
            {
                // Not random to properly determine growth changes instead of relying on random.
                switch (move.Armies)
                {
                    case 1:
                        attackersLost = 1;
                        defendersLost = 0;
                        break;
                    case 2:
                        attackersLost = 1;
                        defendersLost = 1;
                        break;
                    case 3:
                        attackersLost = 2;
                        defendersLost = 2;
                        break;
                    case 4:
                        attackersLost = 1;
                        defendersLost = 2;
                        break;
                    default:
                        attackersLost = 0;
                        defendersLost = 2;
                        break;
                }
            }
            else
            {
                // Aaand this one is wrong, but it was the first I came up with without reading the rules.
                // TODO: it's 70% chance 1 defender kills an attacker and 60% chance 1 attacker kills 1 defender, but do surviving armies get another go if any opposing armies left?
                attackersLost = Math.Abs(defenders - (attackers / _random.Next(1, attackers)));
                defendersLost = Math.Abs(attackers - (defenders / _random.Next(1, defenders)));

                if (attackersLost > attackers)
                {
                    attackersLost = attackers;
                }
                if (defendersLost > defenders)
                {
                    defendersLost = defenders;
                }
            }

            attackers -= attackersLost;
            defenders -= defendersLost;

            if (defenders <= 0)
            {
                fromRegion.Armies -= move.Armies;
                fromRegion.ArmiesAvailable -= move.Armies;

                string defenderName = toRegion.PlayerName;

                // change ownership and transfer attackers
                toRegion.PlayerName = move.PlayerName;
                toRegion.Armies = move.Armies - attackersLost;

                if (toRegion.Armies < 1)
                {
                    toRegion.Armies = 1;
                }

                ConsoleExtensions.WriteColoredLine(ConsoleColor.Green, "Player '{0}' won and lost {1} armies, defender '{2}' lost {3}.", move.PlayerName, attackersLost, defenderName, defendersLost);
            }
            else
            {
                fromRegion.Armies -= attackersLost;
                fromRegion.ArmiesAvailable -= attackersLost;
                
                toRegion.Armies -= defendersLost;

                ConsoleExtensions.WriteColoredLine(ConsoleColor.Green, "Player '{0}' lost, losing {1} armies,  defender '{2}' lost {3}.", move.PlayerName, attackersLost, toRegion.PlayerName, defendersLost);
            }
        }
        protected virtual void Move(IGameplayEngine actingPlayer, AttackTransferMove move)
        {
            var fromRegion = _gameState.FullMap.GetRegionByID(move.FromRegion.ID);
            var toRegion = _gameState.FullMap.GetRegionByID(move.ToRegion.ID);

            if (move.Armies < 1)
            {
                ConsoleExtensions.WriteColoredLine(ConsoleColor.Red, "Player '{0}' tried to move {1} armies from region {2} to {3}, which is a bit silly. Move discarded.", move.PlayerName, move.Armies, fromRegion.ID, toRegion.ID, fromRegion.Armies);
                return;
            }
            if (fromRegion.PlayerName != move.PlayerName)
            {
                ConsoleExtensions.WriteColoredLine(ConsoleColor.Red, "Player '{0}' tried to move {1} armies from region {2} to {3}, but region {2} is not theirs. Move discarded.", move.PlayerName, move.Armies, fromRegion.ID, toRegion.ID);
                return;
            }
            if (move.Armies > fromRegion.ArmiesAvailable)
            {
                ConsoleExtensions.WriteColoredLine(ConsoleColor.Red, "Player '{0}' tried to move {1} armies from region {2} to {3}, but only {4} armies were remaining. Move discarded.", move.PlayerName, move.Armies, fromRegion.ID, toRegion.ID, fromRegion.Armies);
                return;
            }
            if (!fromRegion.Neighbors.Contains(toRegion))
            {
                ConsoleExtensions.WriteColoredLine(ConsoleColor.Red, "Player '{0}' tried to move {1} armies from region {2} to {3}, but the regions are no neighbors. Move discarded.", move.PlayerName, move.Armies, fromRegion.ID, toRegion.ID);
                return;
            }

            if (move.PlayerName == toRegion.PlayerName)
            {
                MoveArmies(move.PlayerName, fromRegion, toRegion, move.Armies, move.MoveReason);
            }
            else
            {
                AttackHandler.HandleAttack(move, _gameState);
            }

            ConsoleExtensions.WriteColoredLine(ConsoleColor.Magenta, "Region {0} ('{1}'): {2} armies, Region {3} ('{4}'): {5} armies",
                                                                       fromRegion.ID,
                                                                       fromRegion.PlayerName,
                                                                       fromRegion.Armies,
                                                                       toRegion.ID,
                                                                       toRegion.PlayerName,
                                                                       toRegion.Armies);
        }