From 2923dc80c9a7716ef9354c5be47a5b06f2cf5a21 Mon Sep 17 00:00:00 2001 From: Yair Morgenstern Date: Mon, 17 Dec 2018 21:33:07 +0200 Subject: [PATCH] Massive performance improvement thanks to: * not mapping out the uniques every tile * saving tech uniques and units uniques specific to movement between tiles --- android/Images/TerrainIcons/Lakes.png | Bin 3536 -> 1685 bytes android/build.gradle | 2 +- core/src/com/unciv/UnCivGame.kt | 2 +- .../unciv/logic/civilization/TechManager.kt | 22 ++++++++++-- core/src/com/unciv/logic/map/MapUnit.kt | 32 +++++++++++++----- core/src/com/unciv/logic/map/TileMap.kt | 15 ++++++-- .../unciv/logic/map/UnitMovementAlgorithms.kt | 6 ++-- 7 files changed, 60 insertions(+), 19 deletions(-) diff --git a/android/Images/TerrainIcons/Lakes.png b/android/Images/TerrainIcons/Lakes.png index afdaacf04693a4d474c0db12f21e77d928b26958..e839d59a6118f426f24eba6b6b9705d3142a3cb0 100644 GIT binary patch literal 1685 zcmai#{Xf%-0>{6?JdD-F%frOxth3`e%R@V19wH)ZWlEWc#Vlr7BdRfMjq(sBtsKdY zB!%X<%Vf^6cAeAWjL_z)<*_1KtE{>(WLrAcw?+06&-1gW>si z;Z|h|)g#rOa53PIJAe;6;BK}8vb6LUZyG#noo7_8beQBQzcx-PnJ^V+*(-Zm_3Qd{ z(G6GuUpqp7Kiuz~BWuV>{}Rf>Ia`@>{wF3fV*qc71%Fo-h%qorgAxoBVh1%mKySb1 zlk*GxoddnJZCUz5Vyi9C3M7j|)JJdeJja-gS+o1*QXDjM1Z{Vy#DpiPx8K^#JT7$V{}*g6Va5)#du}d$a^wEFd02n-X4+{pDa-8`|MxBLZiT-?F?H zc1%JXRYmeDZ|r`)^%$wZaSS_AXA{y2FI27kXnY&EuL=fKmD9FE7ybSCe2-iU&26wClFj^yF2hqWk0rfAn}ziFStS zb?MbK#(T#sbBGKP{xlOtTe`O|^E7tNjPBrWnFH%Jzw7AGlB zM(e$SI4LFK!K?7Ps+o)T<$C=!D(vA4Cw@woO^e4PJpgEQ;&avZrYDO>e4`H8bb=1E zUxsy#$Gv8ZzUvUIHY=q^pB{KaIBPV7qr_FTaJwAiTZqhgo5!cxWdWeNMwi>)YXS(V zW%l3Qv46M-5k>7~&=A6ZJn?;KPB7^XX`@@FC7~4qem{5ZEKFG>hqV+6ZRu5}-x7Y$ z)3>B>q7k2=3s4IktQpez9Y>n{VvVViume=xmF!;a+upmjoDQ(uV3m;mhz_$hgVbgP z*8RYt!>r68?AAcvRe7Y~%>Ad}eRK`pKSaf51!OS)K*$5REgDoT==FAfgA`AFb6pe1 zp+dQ3yan#_bFYG~>bJBV-|p#*FTEM)bj3e#!ms_=Lm*)Nm=z*M*l&igK8Sikx%}Im z`Qf-hZ%?iSIrG$Hh)vHZJKE?Qn^U7h62$Qr zLX9a$J0b+T5Q@>Q@*mE)ROWNzZ!WZd0`t?Q2Zt|3(laW9KqG%L%7-klV6R;loD++3 zFg=psA7MqJKY8?DRDzsIOnxO5?o<8^{?cWx19PZ&eeiTrnuq!-=LRtd4fUySY@?a~ zW*{<{G3B2R)jfx;o2mDdu?Ot>QRw3K*0-2sjCs2mgG~y4nSGf;-U$hw?y+7<%x}vZ zjiN>LH=`77C2DwuA*H97xAlG^Jv&VmxOD{&ZX2mMe;aocZnW~ds`b0;(04XoMZv9!|g(LomcnD-`aTaQJVUq#LGr_W96 zi~p9~rD8hXpHd6=oh9Ly7gm$438vVR1o3ixd1rtk-u~n0?lC{Aaa9L?g(Iq${G4Up z#Q^#pxsWOWNXe?`3(m)9ODQ_huOmlYqjlLJ?dB;kVzQK#yG4{F)lxOolmBmxJvExI Z;2;_MS>rD^HXZ>0_J(7_Izv-f{{=IV4Tt~$ literal 3536 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 + @Transient private var researchedTechUniques=ArrayList() + + // MapUnit.canPassThrough is the most called function in the game, and having these extremey specific booleans is or way of improving the time cost + @Transient var unitsCanEmbark=false + @Transient var embarkedUnitsCanEnterOcean=false + var freeTechs = 0 var techsResearched = HashSet() @@ -56,7 +62,7 @@ class TechManager { return GameBasics.Technologies[TechName]!!.prerequisites.all { isResearched(it) } } - fun getUniques() = researchedTechnologies.flatMap { it.uniques } + fun getUniques() = researchedTechUniques //endregion @@ -103,7 +109,11 @@ class TechManager { techsResearched.add(techName) // this is to avoid concurrent modification problems - researchedTechnologies = researchedTechnologies.withItem(GameBasics.Technologies[techName]!!) + val newTech = GameBasics.Technologies[techName]!! + researchedTechnologies = researchedTechnologies.withItem(newTech) + for(unique in newTech.uniques) + researchedTechUniques = researchedTechUniques.withItem(unique) + updateTransientBooleans() civInfo.addNotification("Research of [$techName] has completed!", null, Color.BLUE) @@ -144,7 +154,13 @@ class TechManager { techsToResearch.remove("Mass Media") researchedTechnologies.addAll(techsResearched.map { GameBasics.Technologies[it]!! }) + researchedTechUniques.addAll(researchedTechnologies.flatMap { it.uniques }) + updateTransientBooleans() } -} + fun updateTransientBooleans(){ + if(researchedTechUniques.contains("Enables embarkation for land units")) unitsCanEmbark=true + if(researchedTechUniques.contains("Enables embarked units to enter ocean tiles")) embarkedUnitsCanEnterOcean=true + } +} \ No newline at end of file diff --git a/core/src/com/unciv/logic/map/MapUnit.kt b/core/src/com/unciv/logic/map/MapUnit.kt index 8a14656c..c9314475 100644 --- a/core/src/com/unciv/logic/map/MapUnit.kt +++ b/core/src/com/unciv/logic/map/MapUnit.kt @@ -19,6 +19,13 @@ class MapUnit { @Transient lateinit var baseUnit: BaseUnit @Transient internal lateinit var currentTile :TileInfo + // These are for performance improvements to getMovementCostBetweenAdjacentTiles, + // a major component of getDistanceToTilesWithinTurn, + // which in turn is a component of getShortestPath and canReach + @Transient var ignoresTerrainCost = false + @Transient var roughTerrainPenalty = false + @Transient var doubleMovementInCoast = false + lateinit var owner: String lateinit var name: String var currentMovement: Float = 0f @@ -81,6 +88,10 @@ class MapUnit { uniques.addAll(baseUnit.uniques) uniques.addAll(promotions.promotions.map { GameBasics.UnitPromotions[it]!!.effect }) tempUniques = uniques + + if("Ignores terrain cost" in uniques) ignoresTerrainCost=true + if("Rough terrain penalty" in uniques) roughTerrainPenalty=true + if("Double movement in coast" in uniques) doubleMovementInCoast=true } fun hasUnique(unique:String): Boolean { @@ -116,23 +127,26 @@ class MapUnit { return "$name - $owner" } + // This is the most called function in the entire game, + // so multiple callees of this function have been optimized, + // because optimization on this function results in massive benefits! fun canPassThrough(tile: TileInfo):Boolean{ val tileOwner = tile.getOwner() + if(tile.getBaseTerrain().impassable) return false - val isOcean = tile.baseTerrain == "Ocean" // profiling showed that 3.5% of all nextTurn time is taken up by string equals in this function =| - if(tile.isWater() && type.isLandUnit()){ - val techUniques = civInfo.tech.getUniques() - if(!techUniques.contains("Enables embarkation for land units")) - return false - if(isOcean && !techUniques.contains("Enables embarked units to enter ocean tiles")) - return false - } if(tile.isLand() && type.isWaterUnit()) return false + + val isOcean = tile.baseTerrain == "Ocean" + if(tile.isWater() && type.isLandUnit()){ + if(!civInfo.tech.unitsCanEmbark) return false + if(isOcean && !civInfo.tech.embarkedUnitsCanEnterOcean) + return false + } + if(isOcean && baseUnit.uniques.contains("Cannot enter ocean tiles")) return false if(isOcean && baseUnit.uniques.contains("Cannot enter ocean tiles until Astronomy") && !civInfo.tech.isResearched("Astronomy")) return false - if(isOcean && baseUnit.uniques.contains("Cannot enter ocean tiles")) return false if(tileOwner!=null && tileOwner.civName!=owner && (tile.isCityCenter() || !civInfo.canEnterTiles(tileOwner))) return false return true diff --git a/core/src/com/unciv/logic/map/TileMap.kt b/core/src/com/unciv/logic/map/TileMap.kt index 85eeea58..8eb3bbd9 100644 --- a/core/src/com/unciv/logic/map/TileMap.kt +++ b/core/src/com/unciv/logic/map/TileMap.kt @@ -92,8 +92,19 @@ class TileMap { fun getViewableTiles(position: Vector2, sightDistance: Int): MutableList { val viewableTiles = getTilesInDistance(position, 1).toMutableList() for (i in 1..sightDistance) { // in each layer, - getTilesAtDistance(position, i).filterTo(viewableTiles) // take only tiles which have a visible neighbor, which is lower than the tile - { tile -> tile.neighbors.any{viewableTiles.contains(it) && (it.getHeight() ==0 || it.getHeight() < tile.getHeight())} } + // This is so we don't use tiles in the same distance to "see over", + // that is to say, the "viewableTiles.contains(it) check will return false for neighbors from the same distance + val tilesToAddInDistanceI = ArrayList() + + for (tile in getTilesAtDistance(position, i)) { // for each tile in that layer, + val tileHeight = tile.getHeight() + val containsViewableNeighborThatCanSeeOver = tile.neighbors.any { + val neighborHeight = it.getHeight() + viewableTiles.contains(it) && (neighborHeight == 0 || neighborHeight < tileHeight) + } + if (containsViewableNeighborThatCanSeeOver) tilesToAddInDistanceI.add(tile) + } + viewableTiles.addAll(tilesToAddInDistanceI) } return viewableTiles diff --git a/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt b/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt index 68bb6224..97949120 100644 --- a/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt +++ b/core/src/com/unciv/logic/map/UnitMovementAlgorithms.kt @@ -19,13 +19,13 @@ class UnitMovementAlgorithms(val unit:MapUnit) { if (unit.civInfo.tech.getUniques().contains("Improves movement speed on roads")) return 1 / 3f else return 1 / 2f } - if (unit.hasUnique("Ignores terrain cost")) return 1f + if (unit.ignoresTerrainCost) return 1f - if (unit.hasUnique("Rough terrain penalty") + if (unit.roughTerrainPenalty && (to.baseTerrain == "Hill" || to.terrainFeature == "Forest" || to.terrainFeature == "Jungle")) return 4f - if(unit.hasUnique("Double movement in coast") && to.baseTerrain=="Coast") + if(unit.doubleMovementInCoast && to.baseTerrain=="Coast") return 1/2f return to.getLastTerrain().movementCost.toFloat() // no road