1+ using System ;
12using C7GameData ;
23using System . Collections . Generic ;
34using System . Linq ;
@@ -27,15 +28,27 @@ public static Dictionary<Tile, float> GetScoredSettlerCandidates(Tile start, Pla
2728
2829 private static Dictionary < Tile , float > AssignTileScores ( Tile startTile , Player player , IEnumerable < Tile > candidates , List < MapUnit > playerSettlers ) {
2930 Dictionary < Tile , float > scores = new ( ) ;
31+ var memo = new Dictionary < string , float > ( ) ;
32+
3033 candidates = candidates . Where ( t => ! SettlerAlreadyMovingTowardsTile ( t , playerSettlers ) && t . IsAllowCities ( ) ) ;
34+
3135 foreach ( Tile t in candidates ) {
32- float score = GetTileYieldScore ( t , player ) ;
33- //For simplicity's sake, I'm only going to look at immediate neighbors here, but
34- //a lot more things should be considered over time.
35- foreach ( Tile nt in t . neighbors . Values ) {
36- score += GetTileYieldScore ( nt , player ) ;
36+ float score = GetTileYieldScore ( t , player , memo ) ;
37+
38+ // Consider all tiles within the BFC for total score.
39+ // Score contribution decreases linearly with distance, by 1/R with each step:
40+ // e.g., with four ranks of workable tiles, R=4:
41+ // city | 100% | 75% | 50% | 25% | 0% | 0% | ..
42+ var maxRank = player . rules . MaxRankOfWorkableTiles ;
43+ foreach ( Tile workable in t . GetTilesWithinRankDistance ( maxRank ) ) {
44+ if ( workable == Tile . NONE )
45+ continue ;
46+ var rank = t . rankDistanceTo ( workable ) ;
47+ if ( rank <= 0 )
48+ continue ;
49+ var adjustment = Math . Max ( 0 , ( maxRank - rank + 1f ) / maxRank ) ;
50+ score += GetTileYieldScore ( workable , player , memo ) * adjustment ;
3751 }
38- //TODO #802: Also look at the next ring out, with lower weights.
3952
4053 //Prefer hills for defense, and coast for boats and such.
4154 if ( t . baseTerrainType . Key == "hills" ) {
@@ -45,6 +58,9 @@ private static Dictionary<Tile, float> AssignTileScores(Tile startTile, Player p
4558 score += player . civilization . Adjustments . WaterBonus ;
4659 }
4760
61+ // Let defensibility play a role
62+ score += ( float ) t . baseTerrainType . defenseBonus . amount * 20.0f ;
63+
4864 //Lower scores if they are far away
4965 float preDistanceScore = score ;
5066 int distance = startTile . distanceTo ( t ) ;
@@ -63,7 +79,11 @@ private static Dictionary<Tile, float> AssignTileScores(Tile startTile, Player p
6379 return scores ;
6480 }
6581
66- private static float GetTileYieldScore ( Tile t , Player owner ) {
82+ private static float GetTileYieldScore ( Tile t , Player owner , Dictionary < string , float > memo ) {
83+ var key = $ "Tile_{ t . XCoordinate } _{ t . YCoordinate } ";
84+ if ( memo . TryGetValue ( key , out var value ) )
85+ return value ;
86+
6787 float score = owner . civilization . Adjustments . FoodYieldBonus * t . foodYield ( owner ) . yield ;
6888 score += owner . civilization . Adjustments . ProductionYieldBonus * t . productionYield ( owner ) . yield ;
6989 score += owner . civilization . Adjustments . CommerceYieldBonus * t . commerceYield ( owner ) . yield ;
@@ -74,13 +94,14 @@ private static float GetTileYieldScore(Tile t, Player owner) {
7494 score += owner . civilization . Adjustments . LuxuryResourceBonus ;
7595 }
7696 }
97+
98+ memo [ key ] = score ;
7799 return score ;
78100 }
79101
80102 private static bool IsInvalidCityLocation ( Tile tile ) {
81- if ( tile . HasCity ) {
103+ if ( tile == Tile . NONE || tile . HasCity )
82104 return true ;
83- }
84105 foreach ( Tile neighbor in tile . neighbors . Values ) {
85106 if ( neighbor . HasCity ) {
86107 return true ;
0 commit comments