Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 8 additions & 9 deletions C7Engine/AI/StrategicAI/ExpansionPriority.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public override void CalculateWeightAndMetadata(Player player) {
if (player.cities.Count < 2) {
this.calculatedWeight = 1000;
} else {
int score = UtilityCalculations.CalculateAvailableLandScore(player);
float score = UtilityCalculations.CalculateAvailableLandScore(player);
score = ApplyEarlyGameMultiplier(score);
score = ApplyNationTraitMultiplier(score, player);

Expand Down Expand Up @@ -58,18 +58,17 @@ public override string ToString() {
return "ExpansionPriority";
}

private int ApplyEarlyGameMultiplier(int score) {
private float ApplyEarlyGameMultiplier(float score) {
//If it's early game, multiply this score.
//TODO: We haven't implemented the part for "how many turns does the game have?" yet. So this is hard-coded.
int gameTurn = EngineStorage.gameData.turn;
int percentOfGameFinished = (gameTurn * 100) / TEMP_GAME_LENGTH;
if (percentOfGameFinished < EARLY_GAME_CUTOFF) {
score = score * (EARLY_GAME_CUTOFF - percentOfGameFinished) / 5;
}
return score;
var gameTurn = EngineStorage.gameData.turn;
var percentOfGameFinished = (gameTurn * 100) / TEMP_GAME_LENGTH;
var isEarlyGame = percentOfGameFinished < EARLY_GAME_CUTOFF;
var multiplier = isEarlyGame ? (EARLY_GAME_CUTOFF - percentOfGameFinished) / 5.0f : 1.0f;
return score * multiplier;
}

private int ApplyNationTraitMultiplier(int score, Player player) {
private float ApplyNationTraitMultiplier(float score, Player player) {
// TODO: The "Expansionist" trait should give a higher priority to this strategic priority.
return score;
}
Expand Down
15 changes: 7 additions & 8 deletions C7Engine/AI/StrategicAI/UtilityCalculations.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@

using System.Collections.Generic;
using System.Linq;
using C7GameData;

namespace C7Engine.AI.StrategicAI {
Expand All @@ -11,16 +12,14 @@ namespace C7Engine.AI.StrategicAI {
public class UtilityCalculations {

private static readonly int PossibleCityLocationScore = 2; //how much weight to give to each possible city location
private static readonly int TileScoreDivider = 10; //how much to divide each location's tile score by
private static readonly float TileScoreDivider = 10f; //how much to divide each location's tile score by

public static int CalculateAvailableLandScore(Player player) {
public static float CalculateAvailableLandScore(Player player) {
//Figure out if there's land to settle, and how much
Dictionary<Tile, float> possibleLocations = SettlerLocationAI.GetScoredSettlerCandidates(player.cities[0].location, player);
int score = possibleLocations.Count * PossibleCityLocationScore;
foreach (int i in possibleLocations.Values) {
score += i / TileScoreDivider;
}
return score;
var possibleLocations = SettlerLocationAI.GetScoredSettlerCandidates(player.cities[0].location, player);
var availableLand = possibleLocations.Count * PossibleCityLocationScore;
var settlementQuality = possibleLocations.Values.Sum(i => i / TileScoreDivider);
return settlementQuality + availableLand;
}
}
}
2 changes: 1 addition & 1 deletion C7Engine/AI/StrategicAI/WarPriority.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public override void CalculateWeightAndMetadata(Player player) {
}
}

bool outOfLandToExpandTo = UtilityCalculations.CalculateAvailableLandScore(player) == 0;
bool outOfLandToExpandTo = UtilityCalculations.CalculateAvailableLandScore(player) < 1;

// Don't go to war if there's still land we should be expanding to.
if (!outOfLandToExpandTo) {
Expand Down
13 changes: 10 additions & 3 deletions C7Engine/AI/UnitAI/SettlerLocationAI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,16 @@ private static Dictionary<Tile, float> AssignTileScores(Tile startTile, Player p
candidates = candidates.Where(t => !SettlerAlreadyMovingTowardsTile(t, playerSettlers) && t.IsAllowCities());
foreach (Tile t in candidates) {
float score = GetTileYieldScore(t, player);
//For simplicity's sake, I'm only going to look at immediate neighbors here, but
//a lot more things should be considered over time.

// Consider the immediate neighbors
foreach (Tile nt in t.neighbors.Values) {
score += GetTileYieldScore(nt, player);
}
//TODO #802: Also look at the next ring out, with lower weights.

// Consider the outer ring of the BFC
foreach (var ot in t.GetBFCOuterRing()) {
score += GetTileYieldScore(ot, player) / 3;
}

//Prefer hills for defense, and coast for boats and such.
if (t.baseTerrainType.Key == "hills") {
Expand All @@ -45,6 +49,9 @@ private static Dictionary<Tile, float> AssignTileScores(Tile startTile, Player p
score += player.civilization.Adjustments.WaterBonus;
}

// Let defensibility play a role
score += (float)t.baseTerrainType.defenseBonus.amount * 20.0f;

//Lower scores if they are far away
float preDistanceScore = score;
int distance = startTile.distanceTo(t);
Expand Down
36 changes: 36 additions & 0 deletions C7Engine/C7GameData/Tile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,42 @@ public List<Tile> GetTilesWithinRankDistance(int rank) {
return result;
}

private List<Tile> _outerRing;

public List<Tile> GetBFCOuterRing() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I have some reservations regarding this.

The BFC is not necessarily up to 2 tiles, as we have made this configurable in the rules

public int MaxRankOfWorkableTiles;

So I think it would be preferable if we added another method like

public List<Tile> GetTilesWithinRankDistance(int rank) {

excluding anything but the rank we want, to get the tiles

Unless this method is doing something extra which I am not immediatly getting.

if (_outerRing != null)
return _outerRing;

// List of all the BFC tiles NOT including direct Neighbors
// Essentially, this is every outer tile except North->North, South->South, West->West,East->East
(TileDirection direction1, TileDirection direction2)[] outerRingDirections =
{
(TileDirection.NORTHWEST, TileDirection.NORTH),
(TileDirection.NORTHWEST, TileDirection.NORTHWEST),
(TileDirection.NORTHWEST, TileDirection.WEST),
(TileDirection.SOUTHWEST, TileDirection.WEST),
(TileDirection.SOUTHWEST, TileDirection.SOUTHWEST),
(TileDirection.SOUTHWEST, TileDirection.SOUTH),
(TileDirection.SOUTHEAST, TileDirection.EAST),
(TileDirection.SOUTHEAST, TileDirection.SOUTHEAST),
(TileDirection.SOUTHEAST, TileDirection.SOUTH),
(TileDirection.NORTHEAST, TileDirection.NORTH),
(TileDirection.NORTHEAST, TileDirection.NORTHEAST),
(TileDirection.NORTHEAST, TileDirection.EAST)
};

Dictionary<(TileDirection,TileDirection), Tile> outerRing = new Dictionary<(TileDirection,TileDirection), Tile>();
foreach ((TileDirection dir1, TileDirection dir2) directions in outerRingDirections) {
if (!neighbors.TryGetValue(directions.dir1, out var inner))
continue;

if (inner != NONE && inner.neighbors.TryGetValue(directions.dir2, out var outer) && outer != NONE)
outerRing[(directions.dir1, directions.dir2)] = outer;
}
_outerRing = outerRing.Values.ToList();
return _outerRing;
}

public MapUnit FindTopDefender(MapUnit opponent) {
if (unitsOnTile.Count > 0) {
IEnumerable<MapUnit> potentialDefenders = unitsOnTile.Where(u => u.CanDefendAgainst(opponent));
Expand Down