public Dictionary <int, List <SystemTile> > GetClaims()
        {
            Dictionary <int, List <SystemTile> > allClaims = new Dictionary <int, List <SystemTile> >();

            foreach (Tuple <int, int> start in HSLocations)
            {
                SystemTile        startTile    = tiles[start.Item1][start.Item2];
                int               playerNum    = startTile.playerNum;
                List <SystemTile> claimedTiles = new List <SystemTile>();
                allClaims.Add(playerNum, claimedTiles);
            }

            for (int x = 0; x <= MaxRadius * 2; x++)
            {
                for (int y = 0; y <= MaxRadius * 2; y++)
                {
                    SystemTile tile = tiles[x][y];
                    foreach (int claimer in tile.contestedBy)
                    {
                        allClaims[claimer].Add(tile);
                    }
                }
            }

            return(allClaims);
        }
        public bool AnomaliesOK(
            Galaxy galaxy,
            bool allowAdjacentAnomalies = false
            )
        {
            int MaxRadius = galaxy.MaxRadius;

            SystemTile[][] tiles = galaxy.tiles;

            if (!allowAdjacentAnomalies)
            {
                for (int x = 0; x <= MaxRadius * 2; x++)
                {
                    for (int y = 0; y <= MaxRadius * 2; y++)
                    {
                        SystemTile tile = tiles[x][y];
                        if (tile.anomaly != Anomaly.None)
                        {
                            foreach (SystemTile adj in tile.adjacent)
                            {
                                if (adj.anomaly != Anomaly.None)
                                {
                                    return(false);
                                }
                            }
                        }
                    }
                }
            }

            return(true);
        }
        public void init(int maxRad, int people = 6)
        {
            players = people;
            int width = 2 * maxRad + 1;

            tiles = new SystemTile[width][];
            for (int i = 0; i <= 2 * maxRad; i++)
            {
                SystemTile[] row = new SystemTile[width];
                for (int j = 0; j <= 2 * maxRad; j++)
                {
                    row[j] = TilesetGenerator.GetBlankTile();
                }
                tiles[i] = row;
            }
            MaxRadius = maxRad;
        }
        public bool wormholesOK(
            Galaxy galaxy,
            bool bothHoles          = true,
            bool hardHoleLimit      = true,
            int HoleCount           = 2,
            bool allowAdjacentHoles = false)
        {
            int MaxRadius = galaxy.MaxRadius;

            SystemTile[][] tiles = galaxy.tiles;

            int alphaCount = 0;
            int betaCount  = 0;

            for (int x = 0; x <= MaxRadius * 2; x++)
            {
                for (int y = 0; y <= MaxRadius * 2; y++)
                {
                    if (tiles[x][y].wormholes == Wormhole.Alpha)
                    {
                        alphaCount++;
                    }
                    if (tiles[x][y].wormholes == Wormhole.Beta)
                    {
                        betaCount++;
                    }
                }
            }
            for (int x = 0; x <= MaxRadius * 2; x++)
            {
                for (int y = 0; y <= MaxRadius * 2; y++)
                {
                    if (tiles[x][y].adjacent.Count() != tiles[x][y].adjacent.Distinct().Count())
                    {
                        return(false);
                    }
                }
            }
            if (bothHoles && alphaCount == 0 && betaCount == 0)
            {
                return(false);
            }
            if (hardHoleLimit && alphaCount != HoleCount && betaCount != HoleCount)
            {
                return(false);
            }
            if (alphaCount < HoleCount || betaCount < HoleCount)
            {
                return(false);
            }
            if (!allowAdjacentHoles)
            {
                for (int x = 0; x <= MaxRadius * 2; x++)
                {
                    for (int y = 0; y <= MaxRadius * 2; y++)
                    {
                        SystemTile thisTile = tiles[x][y];
                        if (thisTile.wormholes != Wormhole.None)
                        {
                            foreach (SystemTile adjTile in thisTile.adjacent)
                            {
                                if (adjTile.wormholes != Wormhole.None && adjTile.wormholes != thisTile.wormholes)
                                {
                                    return(false);
                                }
                            }
                        }
                    }
                }
            }

            return(true);
        }
        public (List <int>, Dictionary <int, List <SystemTile> >, Dictionary <int, List <SystemTile> >) StakeClaims(
            Galaxy galaxy,
            int walk         = 10,
            int emptyWalk    = 11,
            int asteroidWalk = 13,
            int novaWalk     = 100,
            int nebulaWalk   = 50,
            int riftWalk     = 05,
            int wormWalk     = 12)
        {
            int MaxRadius = galaxy.MaxRadius;

            SystemTile[][]           tiles       = galaxy.tiles;
            List <Tuple <int, int> > HSLocations = galaxy.HSLocations;

            foreach (Tuple <int, int> start in HSLocations)
            {
                SystemTile startTile = tiles[start.Item1][start.Item2];
                int        playerNum = startTile.playerNum;
                startTile.claims[playerNum] = 0;
                SortedList <int, List <SystemTile> > adjacent = new SortedList <int, List <SystemTile> >();
                foreach (SystemTile tile in startTile.adjacent)
                {
                    int walkDist = walk;
                    if (tile.anomaly == Anomaly.Nova)
                    {
                        walkDist = novaWalk;
                    }
                    else if (tile.anomaly == Anomaly.Nebula)
                    {
                        walkDist = nebulaWalk;
                    }
                    else if (tile.anomaly == Anomaly.Asteroids)
                    {
                        walkDist = asteroidWalk;
                    }
                    else if (tile.anomaly == Anomaly.Rift)
                    {
                        walkDist = riftWalk;
                    }
                    else if (tile.planets.Count() == 0)
                    {
                        walkDist = emptyWalk;
                    }
                    if (!adjacent.ContainsKey(walkDist))
                    {
                        adjacent.Add(walkDist, new List <SystemTile>());
                    }
                    adjacent[walkDist].Add(tile);
                }

                while (adjacent.Count() > 0)
                {
                    IList <int>       keys        = adjacent.Keys;
                    int               firstKey    = keys.First();
                    List <SystemTile> firstList   = adjacent[firstKey];
                    SystemTile        closestTile = firstList.First();
                    firstList.Remove(closestTile);
                    if (firstList.Count() == 0)
                    {
                        adjacent.Remove(firstKey);
                    }
                    if (!closestTile.claims.ContainsKey(playerNum))
                    {
                        closestTile.claims.Add(playerNum, firstKey);
                        foreach (SystemTile next in closestTile.adjacent)
                        {
                            if (!next.claims.ContainsKey(playerNum))
                            {
                                int walkDist = firstKey;
                                if (next.anomaly == Anomaly.Nova)
                                {
                                    walkDist += novaWalk;
                                }
                                else if (next.anomaly == Anomaly.Nebula)
                                {
                                    walkDist += nebulaWalk;
                                }
                                else if (next.anomaly == Anomaly.Asteroids)
                                {
                                    walkDist += asteroidWalk;
                                }
                                else if (next.anomaly.HasFlag(Anomaly.Rift))
                                {
                                    walkDist += riftWalk;
                                }
                                else if ((closestTile.wormholes & next.wormholes) != Wormhole.None)
                                {
                                    walkDist += wormWalk;
                                }
                                else if (next.planets.Count() == 0)
                                {
                                    walkDist += emptyWalk;
                                }
                                else
                                {
                                    walkDist += walk;
                                }
                                if (!adjacent.ContainsKey(walkDist))
                                {
                                    adjacent.Add(walkDist, new List <SystemTile>());
                                }
                                adjacent[walkDist].Add(next);
                            }
                        }
                    }
                }
            }

            for (int x = 0; x <= MaxRadius * 2; x++)
            {
                for (int y = 0; y <= MaxRadius * 2; y++)
                {
                    SystemTile tile = tiles[x][y];
                    if (tile.claims.Count() > 0)
                    {
                        tile.bestClaim = tile.claims.Min(claim => claim.Value);
                        if (tile.claims.Count(claim => claim.Value > tile.bestClaim) > 0)
                        {
                            // TODO: no second best claim if > half players have best claim
                            // TODO: rework code so "top half" of claims are allowed
                            tile.secondBestClaim = tile.claims.Where(claim => claim.Value > tile.bestClaim).Min(claim => claim.Value);
                        }
                    }
                }
            }

            Dictionary <int, List <SystemTile> > sliceClaim   = new Dictionary <int, List <SystemTile> >();
            Dictionary <int, List <SystemTile> > sliceContest = new Dictionary <int, List <SystemTile> >();

            List <int> players = new List <int>();

            foreach (Tuple <int, int> start in HSLocations)
            {
                SystemTile startTile = tiles[start.Item1][start.Item2];
                int        playerNum = startTile.playerNum;
                players.Add(playerNum);
                List <SystemTile> sliceTiles     = new List <SystemTile>();
                List <SystemTile> contestedTiles = new List <SystemTile>();

                for (int x = 0; x <= MaxRadius * 2; x++)
                {
                    for (int y = 0; y <= MaxRadius * 2; y++)
                    {
                        SystemTile tile      = tiles[x][y];
                        int        bestClaim = tile.bestClaim;
                        if (tile.claims.Count() == 0)
                        {
                            continue;
                        }
                        int  playerClaim    = tile.claims[playerNum];
                        int  contesting     = tile.claims.Count(claim => claim.Value == bestClaim);
                        bool playerHasClaim = playerClaim == bestClaim;
                        bool contestedClaim = contesting > 1;
                        if (playerHasClaim)
                        {
                            tile.contestedBy.Add(playerNum);
                        }
                        if (playerHasClaim && contestedClaim)
                        {
                            contestedTiles.Add(tile);
                        }
                        if (playerHasClaim && !contestedClaim)
                        {
                            sliceTiles.Add(tile);
                        }
                    }
                }

                sliceClaim.Add(playerNum, sliceTiles);
                sliceContest.Add(playerNum, contestedTiles);
            }

            return(players, sliceClaim, sliceContest);
        }
        public double GetResourceScoreClaims(
            Galaxy galaxy,
            List <int> players,
            ResourceScoreMethod resMethod = ResourceScoreMethod.MaxVal,
            ContestValue contestMethod    = ContestValue.TopAndRunnerUp,
            double ResInfRatio            = 1.0,
            double ResScaling             = 2.0,
            double claimExponent          = -2.0)
        {
            Dictionary <int, double> resourceClaims = new Dictionary <int, double>();

            foreach (int pnum in players)
            {
                resourceClaims.Add(pnum, 0);
            }

            int MaxRadius = galaxy.MaxRadius;

            for (int x = 0; x <= 2 * MaxRadius; x++)
            {
                for (int y = 0; y <= 2 * MaxRadius; y++)
                {
                    SystemTile tile = galaxy.tiles[x][y];
                    if (tile.sysNum > 0 && tile.planets.Count() > 0)
                    {
                        Dictionary <int, double> claims = new Dictionary <int, double>();
                        foreach (int pnum in tile.claims.Keys)
                        {
                            switch (contestMethod)
                            {
                            case ContestValue.Slices:
                                if (claims[pnum] == tile.bestClaim)
                                {
                                    claims.Add(pnum, 1.0);
                                }
                                break;

                            case ContestValue.ClaimSize:
                                if (tile.bestClaim > 0)
                                {
                                    claims.Add(pnum, Math.Pow(tile.claims[pnum], claimExponent));
                                }
                                else if (claims[pnum] == tile.bestClaim)
                                {
                                    claims.Add(pnum, 1.0);
                                }
                                break;

                            case ContestValue.TopAndRunnerUp:
                                if (tile.bestClaim == 0 && claims[pnum] == tile.bestClaim)
                                {
                                    claims.Add(pnum, 1.0);
                                }
                                else if (claims[pnum] == tile.bestClaim || claims[pnum] == tile.secondBestClaim)
                                {
                                    claims.Add(pnum, Math.Pow(tile.claims[pnum], claimExponent));
                                }
                                break;
                            }
                        }
                        if (claims.Count() == 0)
                        {
                            throw new Exception("Every system should have at least one claim, right?");
                        }
                        double claimScale = claims.Sum(claim => claim.Value);
                        foreach (KeyValuePair <int, double> claim in claims)
                        {
                            double val = 0.0;
                            switch (resMethod)
                            {
                            case ResourceScoreMethod.DirectSum:
                                val = tile.GetResources() + ResInfRatio * tile.GetInfluence();
                                break;

                            case ResourceScoreMethod.Separate:
                                throw new Exception("Not supporting \"separate\" for claim method");

                            case ResourceScoreMethod.MaxVal:
                                val = tile.planets.Sum(planet => Math.Max(planet.resources, ResInfRatio * planet.influence));
                                break;
                            }
                            tile.adjClaims.Add(claim.Key, claim.Value / claimScale);
                            resourceClaims[claim.Key] += val * claim.Value / claimScale;
                        }
                    }
                }
            }

            double minSlice = resourceClaims.Min(claim => claim.Value);
            double maxSlice = resourceClaims.Max(claim => claim.Value);

            return(Math.Pow(minSlice / maxSlice, ResScaling));
        }