Improve AI city settling, science game, and belief picking (#12256)

* AI behaviour changes

* Update Automation.kt

* Update Automation.kt

* Update Automation.kt

* Update Automation.kt

* Update Automation.kt

* Update ConstructionAutomation.kt

* Update Automation.kt

* Reverting some changes

* Changes

* revert changes

* revert changes

* revert changes

* revert changes

* Update CityLocationTileRanker.kt

* Citizen assignment for stat conversion

* Update CityLocationTileRanker.kt

* Reduce AI settling

* Avoid AI building units when in negative Supply

* Update CityLocationTileRanker.kt

* Update CityLocationTileRanker.kt

* Update CityLocationTileRanker.kt

* Update ConstructionAutomation.kt

* Update build.gradle.kts

* Update gradle-wrapper.properties

* Update CityLocationTileRanker.kt

* Update CityLocationTileRanker.kt

* Update ConstructionAutomation.kt

* Update CityLocationTileRanker.kt

* AI changes for humans

* Fix puppet focus

* Update Automation.kt

* Puppet focus

* Update Automation.kt

* Update Automation.kt

* Update Automation.kt

* Update Automation.kt

* Update Automation.kt

* Update Automation.kt

* Update Automation.kt

* Update Automation.kt

* Update Automation.kt

* Update Stats.kt

* Update CityTurnManager.kt

* Remove specialist science modifier

* Update ReligionAutomation.kt

* Update ReligionAutomation.kt

* Update ReligionAutomation.kt

* Update CivilianUnitAutomation.kt

* Update ReligionAutomation.kt

* Worker prioritization

Workers are valuable in expand cities.

* Update ConstructionAutomation.kt

Food always important, it's rarely good to skip e.g. granary if we're on 6 pop.

* Update ConstructionAutomation.kt

Should achieve about the same with less lines of code.

* Update Automation.kt

* Update ConstructionAutomation.kt

* Update Policies.json

* Update Policies.json

* Update Policies.json

* Update ConstructionAutomation.kt

* Update Policies.json

* Update ReligionAutomation.kt

* Update ReligionAutomation.kt

* Update ReligionAutomation.kt

* Update ReligionAutomation.kt

* Rename Crop Yield to Growth

* Update worker usage

* Update UnitAutomation.kt

* Tutorials update

* Update Tutorials.json

* Fix spelling error

* Update Tutorials.json

* Update Tutorials.json

* Update Tutorials.json

* Update Tutorials.json

* Update Tutorials.json

* Update Tutorials.json

* Update Tutorials.json

* Update Tutorials.json

* AI tech and policy choices

* Update Techs.json

* Update Policies.json

* Update ConstructionAutomation.kt

* Update UnitPromotions.json

* Update

* Update Policies.json

* Update Tutorials.json

* ReligionAutomation bugfix

* Update ReligionAutomation.kt

* Update ReligionAutomation.kt

* Update ReligionAutomation.kt

* Update ReligionAutomation.kt

* Update ReligionAutomation.kt

* Update ReligionAutomation.kt

* Update ReligionAutomation.kt

* Update AI city settling and science game

* Update ReligionAutomation.kt

* Maybe revert this now the belief picking has improved

* Update ReligionAutomation.kt
This commit is contained in:
EmperorPinguin
2024-10-06 15:46:46 +02:00
committed by GitHub
parent 5d1c05d8e4
commit 6f4d8fd7b5
6 changed files with 64 additions and 60 deletions

View File

@ -38,6 +38,7 @@
"name": "Archery", "name": "Archery",
"row": 7, "row": 7,
"prerequisites": ["Agriculture"], "prerequisites": ["Agriculture"],
"uniques": ["[-50]% weight to this choice for AI decisions"],
"quote": "'The haft of the arrow has been feathered with one of the eagle's own plumes, we often give our enemies the means of our own destruction' - Aesop" "quote": "'The haft of the arrow has been feathered with one of the eagle's own plumes, we often give our enemies the means of our own destruction' - Aesop"
}, },
{ {
@ -101,6 +102,7 @@
"name": "Bronze Working", "name": "Bronze Working",
"row": 10, "row": 10,
"prerequisites": ["Mining"], "prerequisites": ["Mining"],
"uniques": ["[+50]% weight to this choice for AI decisions"],
"quote": "'Here Hector entered, with a spear eleven cubits long in his hand; the bronze point gleamed in front of him, and was fastened to the shaft of the spear by a ring of gold.' - Homer" "quote": "'Here Hector entered, with a spear eleven cubits long in his hand; the bronze point gleamed in front of him, and was fastened to the shaft of the spear by a ring of gold.' - Homer"
} }
] ]
@ -124,6 +126,7 @@
"name": "Horseback Riding", "name": "Horseback Riding",
"row": 5, "row": 5,
"prerequisites": ["Trapping","The Wheel"], "prerequisites": ["Trapping","The Wheel"],
"uniques": ["[+50]% weight to this choice for AI decisions"],
"quote": "'A Horse! A Horse! My kingdom for a horse!' - Shakespeare (Richard III)" "quote": "'A Horse! A Horse! My kingdom for a horse!' - Shakespeare (Richard III)"
}, },
{ {
@ -178,6 +181,7 @@
"name": "Iron Working", "name": "Iron Working",
"row": 10, "row": 10,
"prerequisites": ["Bronze Working"], "prerequisites": ["Bronze Working"],
"uniques": ["[-50]% weight to this choice for AI decisions"],
"quote": "'Do not wait to strike til the iron is hot, but make it hot by striking.' - William Butler Yeats" "quote": "'Do not wait to strike til the iron is hot, but make it hot by striking.' - William Butler Yeats"
} }
] ]
@ -213,6 +217,7 @@
"name": "Metal Casting", "name": "Metal Casting",
"row": 9, "row": 9,
"prerequisites": ["Engineering","Iron Working"], "prerequisites": ["Engineering","Iron Working"],
"uniques": ["[+100]% weight to this choice for AI decisions"],
"quote": "'When pieces of bronze or gold or iron break, the metal-smith welds them together again in the fire, and the bond is established.' - Sri Guru Granth Sahib" "quote": "'When pieces of bronze or gold or iron break, the metal-smith welds them together again in the fire, and the bond is established.' - Sri Guru Granth Sahib"
} }
] ]
@ -229,6 +234,7 @@
"cost": 375, "cost": 375,
"row": 1, "row": 1,
"prerequisites": ["Optics","Theology"], "prerequisites": ["Optics","Theology"],
"uniques": ["[-50]% weight to this choice for AI decisions"],
"quote": "'I find the great thing in this world is not so much where we stand, as in what direction we are moving.' - Oliver Wendell Holmes" "quote": "'I find the great thing in this world is not so much where we stand, as in what direction we are moving.' - Oliver Wendell Holmes"
}, },
{ {
@ -262,6 +268,7 @@
"name": "Steel", "name": "Steel",
"row": 10, "row": 10,
"prerequisites": ["Metal Casting"], "prerequisites": ["Metal Casting"],
"uniques": ["[-50]% weight to this choice for AI decisions"],
"quote": "'John Henry said to his Captain, / 'A man ain't nothin' but a man, / And before I'll let your steam drill beat me down, / I'll die with the hammer in my hand.'' - Anonymous: The Ballad of John Henry, the Steel-Drivin' Man" "quote": "'John Henry said to his Captain, / 'A man ain't nothin' but a man, / And before I'll let your steam drill beat me down, / I'll die with the hammer in my hand.'' - Anonymous: The Ballad of John Henry, the Steel-Drivin' Man"
} }
] ]
@ -276,7 +283,7 @@
{ {
"name": "Astronomy", "name": "Astronomy",
"row": 2, "row": 2,
"uniques": ["[+1] Movement <for [Embarked] units>","Enables [Embarked] units to enter ocean tiles"], "uniques": ["[+1] Movement <for [Embarked] units>","Enables [Embarked] units to enter ocean tiles","[+50]% weight to this choice for AI decisions"],
"prerequisites": ["Compass","Education"], "prerequisites": ["Compass","Education"],
"quote": "'Joyfully to the breeze royal Odysseus spread his sail, and with his rudder skillfully he steered.' - Homer" "quote": "'Joyfully to the breeze royal Odysseus spread his sail, and with his rudder skillfully he steered.' - Homer"
}, },
@ -305,6 +312,7 @@
"name": "Gunpowder", "name": "Gunpowder",
"row": 10, "row": 10,
"prerequisites": ["Physics","Steel"], "prerequisites": ["Physics","Steel"],
"uniques": ["[-50]% weight to this choice for AI decisions"],
"quote": "'The day when two army corps can annihilate each other in one second, all civilized nations, it is to be hoped, will recoil from war and discharge their troops.' - Alfred Nobel" "quote": "'The day when two army corps can annihilate each other in one second, all civilized nations, it is to be hoped, will recoil from war and discharge their troops.' - Alfred Nobel"
} }
] ]
@ -332,18 +340,21 @@
"name": "Economics", "name": "Economics",
"row": 6, "row": 6,
"prerequisites": ["Banking","Printing Press"], "prerequisites": ["Banking","Printing Press"],
"uniques": ["[+50]% weight to this choice for AI decisions"],
"quote": "'Compound interest is the most powerful force in the universe.' - Albert Einstein" "quote": "'Compound interest is the most powerful force in the universe.' - Albert Einstein"
}, },
{ {
"name": "Metallurgy", "name": "Metallurgy",
"row": 9, "row": 9,
"prerequisites": ["Printing Press","Gunpowder"], "prerequisites": ["Printing Press","Gunpowder"],
"uniques": ["[-50]% weight to this choice for AI decisions"],
"quote": "'There never was a good knife made of bad steel.' - Benjamin Franklin" "quote": "'There never was a good knife made of bad steel.' - Benjamin Franklin"
}, },
{ {
"name": "Chemistry", "name": "Chemistry",
"row": 10, "row": 10,
"prerequisites": ["Gunpowder"], "prerequisites": ["Gunpowder"],
"uniques": ["[+100]% weight to this choice for AI decisions"],
"quote": "'Wherever we look, the work of the chemist has raised the level of our civilization and has increased the productive capacity of the nation.' - Calvin Coolidge" "quote": "'Wherever we look, the work of the chemist has raised the level of our civilization and has increased the productive capacity of the nation.' - Calvin Coolidge"
} }
] ]
@ -359,6 +370,7 @@
"name": "Archaeology", "name": "Archaeology",
"row": 2, "row": 2,
"prerequisites": ["Navigation","Architecture"], "prerequisites": ["Navigation","Architecture"],
"uniques": ["[+50]% weight to this choice for AI decisions"],
"quote": "'Those who cannot remember the past are condemned to repeat it.' - George Santayana" "quote": "'Those who cannot remember the past are condemned to repeat it.' - George Santayana"
}, },
{ {
@ -372,12 +384,14 @@
"name": "Industrialization", "name": "Industrialization",
"row": 6, "row": 6,
"prerequisites": ["Economics"], "prerequisites": ["Economics"],
"uniques": ["[+50]% weight to this choice for AI decisions"],
"quote": "'Industrialization based on machinery, already referred to as a characteristic of our age, is but one aspect of the revolution that is being wrought by technology.' - Emily Greene Balch" "quote": "'Industrialization based on machinery, already referred to as a characteristic of our age, is but one aspect of the revolution that is being wrought by technology.' - Emily Greene Balch"
}, },
{ {
"name": "Rifling", "name": "Rifling",
"row": 7, "row": 7,
"prerequisites": ["Economics","Metallurgy"], "prerequisites": ["Economics","Metallurgy"],
"uniques": ["[-50]% weight to this choice for AI decisions"],
"quote": "'It is well that war is so terrible, or we should grow too fond of it.' - Robert E. Lee" "quote": "'It is well that war is so terrible, or we should grow too fond of it.' - Robert E. Lee"
}, },
{ {
@ -442,19 +456,21 @@
"name": "Refrigeration", "name": "Refrigeration",
"row": 2, "row": 2,
"prerequisites": ["Biology", "Electricity"], //todo: offshore platform should need this "prerequisites": ["Biology", "Electricity"], //todo: offshore platform should need this
"uniques": ["[-50]% weight to this choice for AI decisions"], "uniques": ["[-75]% weight to this choice for AI decisions"],
"quote": "'And homeless near a thousand homes I stood, and near a thousand tables pined and wanted food.' - William Wordsworth" "quote": "'And homeless near a thousand homes I stood, and near a thousand tables pined and wanted food.' - William Wordsworth"
}, },
{ {
"name": "Radio", "name": "Radio",
"row": 3, "row": 3,
"prerequisites": ["Electricity"], "prerequisites": ["Electricity"],
"uniques": ["[+100]% weight to this choice for AI decisions"],
"quote": "'The whole country was tied together by radio. We all experienced the same heroes and comedians and singers. They were giants.' - Woody Allen" "quote": "'The whole country was tied together by radio. We all experienced the same heroes and comedians and singers. They were giants.' - Woody Allen"
}, },
{ {
"name": "Replaceable Parts", "name": "Replaceable Parts",
"row": 4, "row": 4,
"prerequisites": ["Electricity","Steam Power"], "prerequisites": ["Electricity","Steam Power"],
"uniques": ["[+100]% weight to this choice for AI decisions"],
"quote": "'Nothing is particularly hard if you divide it into small jobs.' - Henry Ford" "quote": "'Nothing is particularly hard if you divide it into small jobs.' - Henry Ford"
}, },
{ {
@ -483,7 +499,7 @@
"name": "Plastics", "name": "Plastics",
"row": 3, "row": 3,
"prerequisites": ["Radio","Replaceable Parts"], "prerequisites": ["Radio","Replaceable Parts"],
"uniques": ["[+100]% weight to this choice for AI decisions"], "uniques": ["[+200]% weight to this choice for AI decisions"],
"quote": "'Ben, I want to say one word to you, just one word: plastics.' - Buck Henry and Calder Willingham, The Graduate" "quote": "'Ben, I want to say one word to you, just one word: plastics.' - Buck Henry and Calder Willingham, The Graduate"
}, },
{ {
@ -531,6 +547,7 @@
"name": "Radar", "name": "Radar",
"row": 6, "row": 6,
"prerequisites": ["Ballistics","Electronics"], "prerequisites": ["Ballistics","Electronics"],
"uniques": ["[+100]% weight to this choice for AI decisions"],
"quote": "'Vision is the art of seeing things invisible.' - Jonathan Swift" "quote": "'Vision is the art of seeing things invisible.' - Jonathan Swift"
}, },
{ {
@ -604,7 +621,7 @@
"name": "Satellites", "name": "Satellites",
"row": 6, "row": 6,
"prerequisites": ["Rocketry"], "prerequisites": ["Rocketry"],
"uniques": ["Reveals the entire map","[+100]% weight to this choice for AI decisions"], "uniques": ["Reveals the entire map","[+200]% weight to this choice for AI decisions"],
"quote": "'Now, somehow, in some new way, the sky seemed almost alien.' - Lyndon B. Johnson" "quote": "'Now, somehow, in some new way, the sky seemed almost alien.' - Lyndon B. Johnson"
}, },
{ {

View File

@ -259,7 +259,7 @@ class ConstructionAutomation(val cityConstructions: CityConstructions) {
if (!civInfo.hasUnique(UniqueType.EnablesConstructionOfSpaceshipParts)) return if (!civInfo.hasUnique(UniqueType.EnablesConstructionOfSpaceshipParts)) return
val spaceshipPart = (nonWonders + units).filter { it.name in spaceshipParts }.filterBuildable().firstOrNull() val spaceshipPart = (nonWonders + units).filter { it.name in spaceshipParts }.filterBuildable().firstOrNull()
?: return ?: return
val modifier = 3f * personality.modifierFocus(PersonalityValue.Science, .4f) val modifier = 20f //We're weighing Apollo program according to personality. If we decided to invest in that, we might as well commit to it.
addChoice(relativeCostEffectiveness, spaceshipPart.name, modifier) addChoice(relativeCostEffectiveness, spaceshipPart.name, modifier)
} }

View File

@ -367,17 +367,9 @@ object NextTurnAutomation {
if (greatPeople.isEmpty()) return if (greatPeople.isEmpty()) return
var greatPerson = greatPeople.random() var greatPerson = greatPeople.random()
val scienceGP = greatPeople.firstOrNull { it.uniques.contains("Great Person - [Science]") }
if (civInfo.wantsToFocusOn(Victory.Focus.Culture)) { if (scienceGP != null) greatPerson = scienceGP
val culturalGP = // Humans would pick a prophet or engineer, but it'd require more sophistication on part of the AI - a scientist is the safest option for now
greatPeople.firstOrNull { it.uniques.contains("Great Person - [Culture]") }
if (culturalGP != null) greatPerson = culturalGP
}
if (civInfo.wantsToFocusOn(Victory.Focus.Science)) {
val scientificGP =
greatPeople.firstOrNull { it.uniques.contains("Great Person - [Science]") }
if (scientificGP != null) greatPerson = scientificGP
}
civInfo.units.addUnit(greatPerson, civInfo.cities.firstOrNull { it.isCapital() }) civInfo.units.addUnit(greatPerson, civInfo.cities.firstOrNull { it.isCapital() })

View File

@ -33,16 +33,12 @@ object ReligionAutomation {
if (civInfo.religionManager.remainingFoundableReligions() == 0 ) { if (civInfo.religionManager.remainingFoundableReligions() == 0 ) {
buyGreatPerson(civInfo) buyGreatPerson(civInfo)
return
}
// We don't have a religion and no more change of getting it :(
if (civInfo.religionManager.religionState <= ReligionState.Pantheon) {
tryBuyAnyReligiousBuilding(civInfo) tryBuyAnyReligiousBuilding(civInfo)
return return
} }
// If we don't have majority in all our own cities, build missionaries and inquisitors to solve this // If we don't have majority in all our own cities, build missionaries and inquisitors to solve this
//TODO: in late game, this may not be worth it
val citiesWithoutOurReligion = civInfo.cities.filter { it.religion.getMajorityReligion() != civInfo.religionManager.religion!! } val citiesWithoutOurReligion = civInfo.cities.filter { it.religion.getMajorityReligion() != civInfo.religionManager.religion!! }
// The original had a cap at 4 missionaries total, but 1/4 * the number of cities should be more appropriate imo // The original had a cap at 4 missionaries total, but 1/4 * the number of cities should be more appropriate imo
if (citiesWithoutOurReligion.count() > if (citiesWithoutOurReligion.count() >
@ -59,7 +55,6 @@ object ReligionAutomation {
return return
} }
// Get an inquisitor to defend our holy city // Get an inquisitor to defend our holy city
val holyCity = civInfo.religionManager.getHolyCity() val holyCity = civInfo.religionManager.getHolyCity()
if (holyCity != null if (holyCity != null
@ -70,23 +65,13 @@ object ReligionAutomation {
buyInquisitorNear(civInfo, holyCity) buyInquisitorNear(civInfo, holyCity)
return return
} }
// Buy religious buildings in cities if possible
val citiesWithMissingReligiousBuildings = civInfo.cities.filter { city ->
city.religion.getMajorityReligion() != null
&& !city.cityConstructions.isAllBuilt(city.religion.getMajorityReligion()!!.buildingsPurchasableByBeliefs)
}
if (citiesWithMissingReligiousBuildings.any()) {
tryBuyAnyReligiousBuilding(civInfo)
return
}
// Just buy missionaries to spread our religion outside of our civ // Just buy missionaries to spread our religion outside of our civ
if (civInfo.units.getCivUnits().count { it.hasUnique(UniqueType.CanSpreadReligion) } < 4) { if (civInfo.units.getCivUnits().count { it.hasUnique(UniqueType.CanSpreadReligion) } < 4) {
buyMissionaryInAnyCity(civInfo) buyMissionaryInAnyCity(civInfo)
return return
} }
// Todo: buy inquisitors for defence of other cities // Todo: declare war if enemy missionaries enter our civ without permission
} }
private fun tryBuyAnyReligiousBuilding(civInfo: Civilization) { private fun tryBuyAnyReligiousBuilding(civInfo: Civilization) {
@ -289,17 +274,17 @@ object ReligionAutomation {
UniqueType.StatsFromObject -> UniqueType.StatsFromObject ->
when { when {
ruleSet.buildings.containsKey(unique.params[1]) -> { ruleSet.buildings.containsKey(unique.params[1]) -> {
unique.stats.values.sum() / unique.stats.values.sum() *
if (ruleSet.buildings[unique.params[1]]!!.isWonder) 2f if (ruleSet.buildings[unique.params[1]]!!.isNationalWonder) 0.5f //there's at most 1 copy of each of these in our empire
else 1f else 1.5f //yields for buildings are usually more desireable than faith-purchased buildings, as we might not need an upfront investment
} }
ruleSet.specialists.containsKey(unique.params[1]) -> { ruleSet.specialists.containsKey(unique.params[1]) -> {
unique.stats.values.sum() * unique.stats.values.sum() *
if (city.population.population > 8f) 3f if (city.population.population > 8f) 2f
else 1f else 1f
} }
else -> 0f else -> unique.stats.values.sum() * 1f //should account for most edge cases here
} }
UniqueType.StatsFromTradeRoute -> UniqueType.StatsFromTradeRoute ->
unique.stats.values.sum() * unique.stats.values.sum() *
@ -309,7 +294,7 @@ object ReligionAutomation {
min(unique.params[0].toFloat() * city.population.population, unique.params[2].toFloat()) min(unique.params[0].toFloat() * city.population.population, unique.params[2].toFloat())
UniqueType.StatsPerCity -> UniqueType.StatsPerCity ->
if (city.matchesFilter(unique.params[1])) if (city.matchesFilter(unique.params[1]))
unique.stats.values.sum() unique.stats.values.sum() * 2f //free yields
else 0f else 0f
else -> 0f else -> 0f
} }
@ -359,7 +344,7 @@ object ReligionAutomation {
// Some city-filters are modified by personality (non-enemy foreign cities) // Some city-filters are modified by personality (non-enemy foreign cities)
score += modifier * when (unique.type) { score += modifier * when (unique.type) {
UniqueType.KillUnitPlunderNearCity -> UniqueType.KillUnitPlunderNearCity ->
unique.params[0].toFloat() * 4f * unique.params[0].toFloat() * 0.5f * //can be very strong, but a low weight for now as the AI currently isn't farming barb camp
if (civInfo.wantsToFocusOn(Victory.Focus.Military)) 2f if (civInfo.wantsToFocusOn(Victory.Focus.Military)) 2f
else 1f else 1f
UniqueType.BuyUnitsForAmountStat, UniqueType.BuyBuildingsForAmountStat -> UniqueType.BuyUnitsForAmountStat, UniqueType.BuyBuildingsForAmountStat ->
@ -368,39 +353,39 @@ object ReligionAutomation {
) 0f ) 0f
// This is something completely different from the original, but I have no idea // This is something completely different from the original, but I have no idea
// what happens over there // what happens over there
else civInfo.stats.statsForNextTurn[Stat.valueOf(unique.params[2])] * 5f / unique.params[1].toFloat() else civInfo.stats.statsForNextTurn[Stat.valueOf(unique.params[2])] * 200f / unique.params[1].toFloat() //the costs of these are probably similar to the baseUnitBuyCost
UniqueType.BuyUnitsWithStat, UniqueType.BuyBuildingsWithStat -> UniqueType.BuyUnitsWithStat, UniqueType.BuyBuildingsWithStat ->
if (civInfo.religionManager.religion != null if (civInfo.religionManager.religion != null
&& civInfo.religionManager.religion!!.followerBeliefUniqueMap.getUniques(unique.type).any() && civInfo.religionManager.religion!!.followerBeliefUniqueMap.getUniques(unique.type).any()
) 0f ) 0f
// This is something completely different from the original, but I have no idea // This is something completely different from the original, but I have no idea
// what happens over there // what happens over there
else civInfo.stats.statsForNextTurn[Stat.valueOf(unique.params[1])] * 10f / civInfo.getEra().baseUnitBuyCost else civInfo.stats.statsForNextTurn[Stat.valueOf(unique.params[1])] * 200f / civInfo.getEra().baseUnitBuyCost //baseUnitBuyCost is 200 in Standard speed pre-Renaissance
UniqueType.BuyUnitsByProductionCost -> UniqueType.BuyUnitsByProductionCost ->
15f * if (civInfo.wantsToFocusOn(Victory.Focus.Military)) 2f else 1f 0f //Holy Warriors is a waste if we don't buy units with it, and if we buy units with it'll cost us great persons
UniqueType.StatsWhenSpreading -> UniqueType.StatsWhenSpreading ->
unique.params[0].toFloat() / 5f unique.params[0].toFloat() / 10f
UniqueType.StatsWhenAdoptingReligion -> UniqueType.StatsWhenAdoptingReligion ->
unique.stats.values.sum() / 50f unique.stats.values.sum() / 50f
UniqueType.RestingPointOfCityStatesFollowingReligionChange -> UniqueType.RestingPointOfCityStatesFollowingReligionChange ->
if (civInfo.wantsToFocusOn(Victory.Focus.CityStates)) if (civInfo.wantsToFocusOn(Victory.Focus.CityStates))
unique.params[0].toFloat() / 3.5f unique.params[0].toFloat() / 4f
else else
unique.params[0].toFloat() / 7f unique.params[0].toFloat() / 8f
UniqueType.StatsFromGlobalCitiesFollowingReligion -> UniqueType.StatsFromGlobalCitiesFollowingReligion ->
unique.stats.values.sum() unique.stats.values.sum() * 2f //free yields that are potentially more than our own number of cities would allow
UniqueType.StatsFromGlobalFollowers -> UniqueType.StatsFromGlobalFollowers ->
4f * (unique.stats.values.sum() / unique.params[1].toFloat()) 10f * (unique.stats.values.sum() / unique.params[1].toFloat())
UniqueType.Strength -> UniqueType.Strength ->
unique.params[0].toFloat() / 4f unique.params[0].toFloat() * 2f//combat strength from beliefs is very strong
UniqueType.ReligionSpreadDistance -> UniqueType.ReligionSpreadDistance ->
(10f + unique.params[0].toFloat()) * goodEarlyModifier (10f + unique.params[0].toFloat()) * goodEarlyModifier
UniqueType.NaturalReligionSpreadStrength -> UniqueType.NaturalReligionSpreadStrength ->
unique.params[0].toFloat() * goodEarlyModifier / 5f unique.params[0].toFloat() * goodEarlyModifier / 5f //We should weigh this according to cityFilter; Religious Texts is way stronger than Religious Unity
UniqueType.SpreadReligionStrength -> UniqueType.SpreadReligionStrength ->
unique.params[0].toFloat() * goodLateModifier / 5f unique.params[0].toFloat() * goodLateModifier / 5f
UniqueType.FaithCostOfGreatProphetChange -> UniqueType.FaithCostOfGreatProphetChange ->
-unique.params[0].toFloat() * goodLateModifier / 2f -unique.params[0].toFloat() * goodLateModifier / 10f //It's only about 1 more prophet, due to the increasing costs.
UniqueType.BuyBuildingsDiscount, UniqueType.BuyUnitsDiscount -> UniqueType.BuyBuildingsDiscount, UniqueType.BuyUnitsDiscount ->
-unique.params[2].toFloat() * goodLateModifier / 5f -unique.params[2].toFloat() * goodLateModifier / 5f
UniqueType.BuyItemsDiscount -> UniqueType.BuyItemsDiscount ->

View File

@ -96,25 +96,30 @@ object CityLocationTileRanker {
if (onCoast) tileValue += 3 if (onCoast) tileValue += 3
// Hills are free production and defence // Hills are free production and defence
if (onHill) tileValue += 7 if (onHill) tileValue += 7
// Observatories are good, but current implementation no mod-friendly // Observatories are good, but current implementation not mod-friendly
if (isNextToMountain) tileValue += 5 if (isNextToMountain) tileValue += 5
if (newCityTile.isAdjacentToRiver()) tileValue += 14 // This bonus for settling on river is a bit outsized for the importance, but otherwise they have a habit of settling 1 tile away
if (newCityTile.terrainHasUnique(UniqueType.FreshWater)) tileValue += 5 if (newCityTile.isAdjacentToRiver()) tileValue += 20
// We want to found the city on an oasis because it can't be improved otherwise // We want to found the city on an oasis because it can't be improved otherwise
if (newCityTile.terrainHasUnique(UniqueType.Unbuildable)) tileValue += 3 if (newCityTile.terrainHasUnique(UniqueType.Unbuildable)) tileValue += 3
// If we build the city on a resource tile, then we can't build any special improvements on it // If we build the city on a resource tile, then we can't build any special improvements on it
if (newCityTile.resource != null) tileValue -= 4 if (newCityTile.resource != null) tileValue -= 4
if (newCityTile.resource != null && newCityTile.tileResource.resourceType == ResourceType.Bonus) tileValue -= 8
// Settling on bonus resources tends to waste a food
// Settling on luxuries generally speeds up our game, and settling on strategics as well, as the AI cheats and can see them.
var tiles = 0 var tiles = 0
for (i in 0..3) { for (i in 0..3) {
//Ideally, we shouldn't really count the center tile, as it's converted into 1 production 2 food anyways with special cases treated above, but doing so can lead to AI moving settler back and forth until forever
for (nearbyTile in newCityTile.getTilesAtDistance(i)) { for (nearbyTile in newCityTile.getTilesAtDistance(i)) {
tiles++ tiles++
tileValue += rankTile(nearbyTile, civ, onCoast, newUniqueLuxuryResources, baseTileMap, uniqueCache) tileValue += rankTile(nearbyTile, civ, onCoast, newUniqueLuxuryResources, baseTileMap, uniqueCache) * (3 / (i + 1))
//Tiles close to the city can be worked more quickly, and thus should gain higher weight.
} }
} }
// Placing cities on the edge of the map is bad, we can't even build improvements on them! // Placing cities on the edge of the map is bad, we can't even build improvements on them!
tileValue -= (HexMath.getNumberOfTilesInHexagon(3) - tiles) * 3 tileValue -= (HexMath.getNumberOfTilesInHexagon(3) - tiles) * 2.4f
return tileValue return tileValue
} }
@ -150,7 +155,7 @@ object CityLocationTileRanker {
// Don't settle near but not on the coast // Don't settle near but not on the coast
if (rankTile.isCoastalTile() && !onCoast) locationSpecificTileValue -= 2 if (rankTile.isCoastalTile() && !onCoast) locationSpecificTileValue -= 2
// Apply the effect of having a lighthouse, since we can probably assume that we will build it // Apply the effect of having a lighthouse, since we can probably assume that we will build it
if (onCoast && rankTile.isOcean) locationSpecificTileValue += 1 if (onCoast && rankTile.isOcean) locationSpecificTileValue += 1.2f //food is ranked at 1.2 points
// Check if there are any new unique luxury resources // Check if there are any new unique luxury resources
if (rankTile.resource != null && rankTile.tileResource.resourceType == ResourceType.Luxury if (rankTile.resource != null && rankTile.tileResource.resourceType == ResourceType.Luxury
&& !(civ.hasResource(rankTile.resource!!) || newUniqueLuxuryResources.contains(rankTile.resource))) { && !(civ.hasResource(rankTile.resource!!) || newUniqueLuxuryResources.contains(rankTile.resource))) {
@ -170,9 +175,13 @@ object CityLocationTileRanker {
rankTileValue += when (rankTile.tileResource.resourceType) { rankTileValue += when (rankTile.tileResource.resourceType) {
ResourceType.Bonus -> 2f ResourceType.Bonus -> 2f
ResourceType.Strategic -> 1.2f * rankTile.resourceAmount ResourceType.Strategic -> 1.2f * rankTile.resourceAmount
ResourceType.Luxury -> 5f * rankTile.resourceAmount ResourceType.Luxury -> 10f * rankTile.resourceAmount //very important for humans who might want to conquer the AI
} }
} }
if (rankTile.terrainHasUnique(UniqueType.FreshWater)) rankTileValue += 0.5f
//Taking into account freshwater farm food, maybe less important in baseruleset mods
if (rankTile.terrainFeatures.isNotEmpty() && rankTile.lastTerrain.hasUnique(UniqueType.ProductionBonusWhenRemoved)) rankTileValue += 0.5f
//Taking into account yields from forest chopping
if (rankTile.isNaturalWonder()) rankTileValue += 10 if (rankTile.isNaturalWonder()) rankTileValue += 10

View File

@ -85,6 +85,7 @@ object CivilianUnitAutomation {
val hurriedPolicy = UnitActions.invokeUnitAction(unit, UnitActionType.HurryPolicy) val hurriedPolicy = UnitActions.invokeUnitAction(unit, UnitActionType.HurryPolicy)
if (hurriedPolicy) return if (hurriedPolicy) return
//TODO: save up great scientists/writers for late game (8 turns after research labs/broadcast towers resp.)
} }
// Great merchant -> Conduct trade mission if late game and if not at war. // Great merchant -> Conduct trade mission if late game and if not at war.
@ -145,7 +146,7 @@ object CivilianUnitAutomation {
private fun isLateGame(civ: Civilization): Boolean { private fun isLateGame(civ: Civilization): Boolean {
val researchCompletePercent = val researchCompletePercent =
(civ.tech.researchedTechnologies.size * 1.0f) / civ.gameInfo.ruleset.technologies.size (civ.tech.researchedTechnologies.size * 1.0f) / civ.gameInfo.ruleset.technologies.size
return researchCompletePercent >= 0.7f return researchCompletePercent >= 0.6f
} }
/** Returns whether the civilian spends its turn hiding and not moving */ /** Returns whether the civilian spends its turn hiding and not moving */