//Update the current game state (on start of tick)
        private RiftGame UpdateGameState(RiftGame gameState)
        {
            Log("Updating gamestate");
            gameState.MyPlatinum = int.Parse(ReadLine());             // my available Platinum
            Log($"Current platinum: {gameState.MyPlatinum}");

            //Update all the zones
            for (int i = 0; i < gameState.AmountOfZones; i++)
            {
                string[] inputs   = ReadLine().Split(' ');
                int      zId      = int.Parse(inputs[0]);       // this zone's ID
                var      zone     = gameState.GetZone(zId);
                int?     oldOwner = zone.OwningPlayerID;

                zone.OwningPlayerID    = int.Parse(inputs[1]);                                 // the player who owns this zone (-1 otherwise)
                zone.OwningPlayerID    = zone.OwningPlayerID < 0 ? null : zone.OwningPlayerID; //Keep null
                zone.AmountPodsPlayer0 = int.Parse(inputs[2]);                                 // player 0's PODs on this zone
                zone.AmountPodsPlayer1 = int.Parse(inputs[3]);                                 // player 1's PODs on this zone
                zone.AmountPodsPlayer2 = int.Parse(inputs[4]);                                 // player 2's PODs on this zone (always 0 for a two player game)
                zone.AmountPodsPlayer3 = int.Parse(inputs[5]);                                 // player 3's PODs on this zone (always 0 for a two or three player game)

                if (zone.OwningPlayerID != oldOwner)
                {
                    Log($"Ownership of zone {zone.NodeIndex} changed from {oldOwner} to {zone.OwningPlayerID}");
                }
            }
            return(gameState);
        }
        private void LogRepresentation(RiftGame game)
        {
            var podNodes     = game.GetZonesWithMyPods().ToList();
            var controlNodes = game.GetZonesUnderMyControl().ToList();

            Log($"Current statistics: {podNodes.Count} with myNodes, {controlNodes.Count} under control");
        }
        // **** Status readers

        //Read game state/parameters (init)
        private RiftGame GetGameFromInitialInput()
        {
            var game = new RiftGame();

            string[] inputs = ReadLine().Split(' ');
            game.AmountOfPlayers   = int.Parse(inputs[0]);      // the amount of players (2 to 4)
            game.MyPlayerID        = int.Parse(inputs[1]);      // my player ID (0, 1, 2 or 3)
            game.AmountOfZones     = int.Parse(inputs[2]);      // the amount of zones on the map
            game.AmountOfZoneLinks = int.Parse(inputs[3]);      // the amount of links between all zones

            //Read nodes
            for (int i = 0; i < game.AmountOfZones; i++)
            {
                inputs = ReadLine().Split(' ');
                RiftZone rz = new RiftZone()
                {
                    NodeIndex      = int.Parse(inputs[0]),
                    PlatimumSource = int.Parse(inputs[1])
                };
                game.AddOrReplaceZone(rz);
            }

            //Link nodes
            for (int i = 0; i < game.AmountOfZoneLinks; i++)
            {
                inputs = ReadLine().Split(' ');
                var z1 = game.GetZone(int.Parse(inputs[0]));
                var z2 = game.GetZone(int.Parse(inputs[1]));
                z1.LinkedNodes.Add(z2);
                z2.LinkedNodes.Add(z1);
            }

            return(game);
        }
        private IEnumerable <PodMovement> GetMovements(RiftGame game)
        {
            var controlledZones = game.GetZonesUnderMyControl();

            foreach (var z in controlledZones)
            {
                var movableInZone = z.GetAmountMovable(game.MyPlayerID);
                Log($"Can move {movableInZone} from zone {z.NodeIndex}");
                if (!(movableInZone > 0))
                {
                    continue;
                }

                //Find the node to move to
                var moveTo = z.LinkedNodes.FirstOrDefault(zz => zz.OwningPlayerID != game.MyPlayerID)
                             ?? z.LinkedNodes.FirstOrDefault();
                if (moveTo != null)
                {
                    Log($"Moving {movableInZone} to {moveTo.NodeIndex}");
                    yield return(new PodMovement()
                    {
                        Amount = movableInZone.Value,
                        ZoneOrigin = z.NodeIndex,
                        ZoneDestination = moveTo.NodeIndex
                    });
                }
            }
        }
        private string GenerateMovementString(RiftGame game)
        {
            /*Rules for moving:
             *      A POD (or group of PODs) can only make one move per game round.
             *      A POD can only move from one zone to a contiguous zone – either neutral, already owned or owned by an enemy.
             *      A POD located on a zone where a fight is ongoing – meaning a zone with enemy PODs on it – can only move to a neutral zone or a zone he/she owns. Simply put, a POD that flees a fight can only retreat on a zone which does not belong to an enemy.
             */


            /*
             * Line 1: a series of movement commands. A movement command is composed of 3 integers podsCount zoneOrigin zoneDestination that indicate the number of PODs to move from one zone to another.
             * For example 4 2 1 3 2 6 = two commands: moving four PODs from zone 2 to zone 1 and moving three PODs from zone 2 to 6.
             * Just write WAIT if you do not wish to make any movements.
             */
            int maxPodToPurchase = game.MyPlatinum / 20;
            var movements        = GetMovements(game).ToList();

            if (!movements.Any())
            {
                Log("No movements"); return("WAIT");
            }

            Log($"Found {movements.Count()} movements");
            movements.ForEach(m => Log(m));
            return(string.Join(" ", movements.Select(p => p.GetOutputString())));
        }
        private IEnumerable <PodPurchase> GetPurchases(RiftGame game, int maxAmount)
        {
            int amountAvailable = maxAmount;

            Log($"Platimum to buy {amountAvailable} pods");

            //Populate neutral zones
            var neutralZones = GetNeutralZonesForMoves(game).ToList();

            Log($"Found {neutralZones.Count()} neutralzones");
            //While we can get nodes that are neutral
            while (amountAvailable > 0 && neutralZones.Any())
            {
                //Pick a random node, because otherwise we will start populating in the left top
                var neutralZone = GetRandomFromCollection(neutralZones);
                amountAvailable--;
                yield return(new PodPurchase()
                {
                    Amount = 1,
                    Zone = neutralZone.NodeIndex
                });

                neutralZone.AddPodsForPlayer(game.MyPlayerID, 1);                 //Update the temporary count

                //Check if there are still zones
                neutralZones = GetNeutralZonesForMoves(game).ToList();
            }

            if (amountAvailable > 0)
            {
                Log("Still platinum available after populating empty nodes to purchase!");
            }

            //Check if we need to strengthen our outer defenses (zones that are mine, but are next to empty or enemy nodes)
            var zonesToStrengthen = game.GetZonesUnderMyControl().Where(z => z.LinkedNodes.Any(l => l.OwningPlayerID != game.MyPlayerID));

            Log($"Found {zonesToStrengthen.Count()} zones to strengthen");
            foreach (var zoneToStrengthen in zonesToStrengthen)
            {
                if (amountAvailable <= 0)
                {
                    break;
                }
                amountAvailable--;
                yield return(new PodPurchase()
                {
                    Amount = 1,
                    Zone = zoneToStrengthen.NodeIndex
                });

                zoneToStrengthen.AddPodsForPlayer(game.MyPlayerID, 1);                 //Update the temporary count
            }
        }
        private string GeneratePurchaseString(RiftGame game)
        {
            /* Rules for buying:
             *      Each player can buy as many troops as their Platinum stock allows. A war POD costs 20 Platinum bars.
             *      A freshly bought POD can only be placed on a neutral zone or on a zone owned by the buyer.
             */

            /*
             * Line 2: a series of purchase commands. A purchase command is composed of 2 integers podsCount zoneDestination that indicate the number of PODs to add to a zone.
             * For example 2 32 1 11 = two commands: buy two PODs for zone 32 and buy one POD for zone 11.
             * Just write WAIT if you do not want to buy anything.
             */
            int maxPodToPurchase = game.MyPlatinum / 20;
            var purchases        = GetPurchases(game, maxPodToPurchase).ToList();

            if (!purchases.Any())
            {
                return("WAIT");
            }

            Log($"Found {purchases.Count()} purchases");
            purchases.ForEach(p => Log(p));
            return(string.Join(" ", purchases.Select(p => p.GetOutputString())));
        }
 private IEnumerable <RiftZone> GetNeutralZonesForMoves(RiftGame game)
 {
     //Get elements that are neutral and not yet have nodes of me (this can happen if the nodes were set by the randomize function,
     //this will not de-queue them from the default list, since ownership transfers after the tick)
     return(game.GetAllZones().Where(z => z.OwningPlayerID == null && z.GetAmountPodsForPlayer(game.MyPlayerID) <= 0));
 }