From d01b8d815be95c41cabbe5ab4a031f4943a091ad Mon Sep 17 00:00:00 2001 From: The Saminator Date: Tue, 27 Sep 2022 11:22:50 -0400 Subject: [PATCH] Fix initiative (again) and AI ship deploying --- .../starshipfights/game/ai/ai_behaviors.kt | 19 +++++++++-- .../starshipfights/game/ai/ai_optimization.kt | 6 ++-- .../starshipfights/game/ai/ai_util_deploy.kt | 33 ------------------- .../starshipfights/game/game_initiative.kt | 24 ++------------ .../net/starshipfights/game/ai/AITesting.kt | 2 +- 5 files changed, 22 insertions(+), 62 deletions(-) diff --git a/src/commonMain/kotlin/net/starshipfights/game/ai/ai_behaviors.kt b/src/commonMain/kotlin/net/starshipfights/game/ai/ai_behaviors.kt index 057c757..1effc07 100644 --- a/src/commonMain/kotlin/net/starshipfights/game/ai/ai_behaviors.kt +++ b/src/commonMain/kotlin/net/starshipfights/game/ai/ai_behaviors.kt @@ -43,9 +43,11 @@ suspend fun AIPlayer.behave(instincts: Instincts, mySide: GlobalShipController) brain[shipAttackPriority forShip identifiedShip.id] += (identifiedShip.ship.shipType.weightClass.tier.ordinal + 1.5).pow(instincts[combatTargetShipWeight]) } } + is ChatEntry.ShipEscaped -> { // handle escaping ship } + is ChatEntry.ShipAttacked -> { state.ships[msg.ship]?.let { targetedShip -> if (targetedShip.owner != mySide) @@ -54,12 +56,14 @@ suspend fun AIPlayer.behave(instincts: Instincts, mySide: GlobalShipController) brain[shipAttackPriority forShip msg.attacker.id] += Random.nextDouble(msg.damageInflicted.toDouble(), msg.damageInflicted + 1.0) * instincts[combatAvengeAttacks] } } + is ChatEntry.ShipAttackFailed -> { state.ships[msg.ship]?.let { targetedShip -> if (targetedShip.owner != mySide) brain[shipAttackPriority forShip targetedShip.id] += instincts[combatFrustratedByFailedAttacks] } } + is ChatEntry.ShipBoarded -> { state.ships[msg.ship]?.let { targetedShip -> if (targetedShip.owner != mySide) @@ -68,12 +72,14 @@ suspend fun AIPlayer.behave(instincts: Instincts, mySide: GlobalShipController) brain[shipAttackPriority forShip msg.boarder] += Random.nextDouble(msg.damageAmount.toDouble(), msg.damageAmount + 1.0) * instincts[combatAvengeAttacks] } } + is ChatEntry.ShipDestroyed -> { state.destroyedShips[msg.ship]?.let { targetedShip -> if (targetedShip.owner == mySide && msg.destroyedBy is ShipAttacker.EnemyShip) brain[shipAttackPriority forShip msg.destroyedBy.id] += instincts[combatAvengeShipwrecks] * (targetedShip.ship.shipType.weightClass.tier.ordinal + 1.5).pow(instincts[combatAvengeShipWeight]) } } + else -> { // ignore } @@ -90,7 +96,7 @@ suspend fun AIPlayer.behave(instincts: Instincts, mySide: GlobalShipController) when (phase) { GamePhase.Deploy -> { - for ((shipId, position) in deploy(state, mySide, instincts)) { + for ((shipId, position) in deploy(state, mySide)) { val abilityType = PlayerAbilityType.DeployShip(shipId.reinterpret()) val abilityData = PlayerAbilityData.DeployShip(position) @@ -103,6 +109,7 @@ suspend fun AIPlayer.behave(instincts: Instincts, mySide: GlobalShipController) doActions.send(PlayerAction.UseAbility(PlayerAbilityType.DonePhase(phase), PlayerAbilityData.DonePhase)) } + is GamePhase.Power -> { val powerableShips = state.ships.values.filter { ship -> ship.owner == mySide && !ship.isDoneCurrentPhase @@ -118,6 +125,7 @@ suspend fun AIPlayer.behave(instincts: Instincts, mySide: GlobalShipController) doActions.send(PlayerAction.UseAbility(PlayerAbilityType.ConfigurePower(ship.id, newPowerMode), PlayerAbilityData.ConfigurePower)) } + is StandardShipReactor -> { val enginesToShields = when { ship.powerMode.engines == 0 -> -1 @@ -140,6 +148,7 @@ suspend fun AIPlayer.behave(instincts: Instincts, mySide: GlobalShipController) doActions.send(PlayerAction.UseAbility(PlayerAbilityType.DonePhase(phase), PlayerAbilityData.DonePhase)) } + is GamePhase.Move -> { val movableShips = state.ships.values.filter { ship -> ship.owner == mySide && !ship.isDoneCurrentPhase @@ -169,6 +178,7 @@ suspend fun AIPlayer.behave(instincts: Instincts, mySide: GlobalShipController) ) } } + is GamePhase.Attack -> { val potentialAttacks = state.ships.values.flatMap { ship -> if (ship.owner == mySide) @@ -233,6 +243,7 @@ suspend fun AIPlayer.behave(instincts: Instincts, mySide: GlobalShipController) PickResponse.Location(chosenLocation) } + is ShipWeapon.Lance -> { doActions.send(PlayerAction.UseAbility(PlayerAbilityType.ChargeLance(ship.id, weaponId), PlayerAbilityData.ChargeLance)) withTimeoutOrNull(50L) { getErrors.receive() }?.let { error -> @@ -241,6 +252,7 @@ suspend fun AIPlayer.behave(instincts: Instincts, mySide: GlobalShipController) PickResponse.Ship(target.id) } + else -> PickResponse.Ship(target.id) } @@ -263,6 +275,7 @@ suspend fun AIPlayer.behave(instincts: Instincts, mySide: GlobalShipController) } } } + is GamePhase.Repair -> { val repairAbility = state.getPossibleAbilities(mySide).filter { it !is PlayerAbilityType.DonePhase @@ -294,7 +307,7 @@ suspend fun AIPlayer.behave(instincts: Instincts, mySide: GlobalShipController) } } -fun deploy(gameState: GameState, mySide: GlobalShipController, instincts: Instincts): Map, Position> { +fun deploy(gameState: GameState, mySide: GlobalShipController): Map, Position> { val size = gameState.battleInfo.size val totalPoints = gameState.getUsablePoints(mySide) val maxTier = size.maxTier @@ -308,7 +321,7 @@ fun deploy(gameState: GameState, mySide: GlobalShipController, instincts: Instin val deployShip = deployable.filter { ship -> deployed.sumOf { it.pointCost } + ship.pointCost <= totalPoints }.associateWith { ship -> - instincts[ship.shipType.weightClass.focus] + ship.pointCost.toDouble() }.weightedRandomOrNull() ?: break deployable -= deployShip diff --git a/src/commonMain/kotlin/net/starshipfights/game/ai/ai_optimization.kt b/src/commonMain/kotlin/net/starshipfights/game/ai/ai_optimization.kt index 7b1229c..4bba0b2 100644 --- a/src/commonMain/kotlin/net/starshipfights/game/ai/ai_optimization.kt +++ b/src/commonMain/kotlin/net/starshipfights/game/ai/ai_optimization.kt @@ -23,10 +23,6 @@ val allInstincts = listOf( combatPreyOnTheWeak, combatFrustratedByFailedAttacks, - deployEscortFocus, - deployCruiserFocus, - deployBattleshipFocus, - navAggression, navPassivity, navLustForBlood, @@ -73,9 +69,11 @@ class TestSession(gameState: GameState) { stateMutable.value = result.newState result.newState.checkVictory()?.let { gameEndMutable.complete(it) } } + is GameEvent.InvalidAction -> { errorMessageChannel(player.side).send(result.message) } + is GameEvent.GameEnd -> { gameEndMutable.complete(result) } diff --git a/src/commonMain/kotlin/net/starshipfights/game/ai/ai_util_deploy.kt b/src/commonMain/kotlin/net/starshipfights/game/ai/ai_util_deploy.kt index 097127a..da36aea 100644 --- a/src/commonMain/kotlin/net/starshipfights/game/ai/ai_util_deploy.kt +++ b/src/commonMain/kotlin/net/starshipfights/game/ai/ai_util_deploy.kt @@ -4,39 +4,6 @@ import net.starshipfights.data.Id import net.starshipfights.game.* import kotlin.math.sign -val deployEscortFocus by instinct(1.0..5.0) -val deployCruiserFocus by instinct(1.0..5.0) -val deployBattleshipFocus by instinct(1.0..5.0) - -val ShipWeightClass.focus: Instinct - get() = when (this) { - ShipWeightClass.ESCORT -> deployEscortFocus - ShipWeightClass.DESTROYER -> deployCruiserFocus - ShipWeightClass.CRUISER -> deployCruiserFocus - ShipWeightClass.BATTLECRUISER -> deployCruiserFocus - ShipWeightClass.BATTLESHIP -> deployBattleshipFocus - - ShipWeightClass.BATTLE_BARGE -> deployBattleshipFocus - - ShipWeightClass.GRAND_CRUISER -> deployBattleshipFocus - ShipWeightClass.COLOSSUS -> deployBattleshipFocus - - ShipWeightClass.FF_ESCORT -> deployEscortFocus - ShipWeightClass.FF_DESTROYER -> deployCruiserFocus - ShipWeightClass.FF_CRUISER -> deployCruiserFocus - ShipWeightClass.FF_BATTLECRUISER -> deployCruiserFocus - ShipWeightClass.FF_BATTLESHIP -> deployBattleshipFocus - - ShipWeightClass.AUXILIARY_SHIP -> deployEscortFocus - ShipWeightClass.LIGHT_CRUISER -> deployEscortFocus - ShipWeightClass.MEDIUM_CRUISER -> deployCruiserFocus - ShipWeightClass.HEAVY_CRUISER -> deployBattleshipFocus - - ShipWeightClass.FRIGATE -> deployEscortFocus - ShipWeightClass.LINE_SHIP -> deployCruiserFocus - ShipWeightClass.DREADNOUGHT -> deployBattleshipFocus - } - private val ShipWeightClass.rowIndex: Int get() = when (this) { ShipWeightClass.ESCORT -> 3 diff --git a/src/commonMain/kotlin/net/starshipfights/game/game_initiative.kt b/src/commonMain/kotlin/net/starshipfights/game/game_initiative.kt index 2f66ebd..7bed172 100644 --- a/src/commonMain/kotlin/net/starshipfights/game/game_initiative.kt +++ b/src/commonMain/kotlin/net/starshipfights/game/game_initiative.kt @@ -9,9 +9,7 @@ fun GameState.calculateMovePhaseInitiative(): InitiativeMap = .values .groupBy { it.owner } .mapValues { (_, shipList) -> - shipList - .filter { !it.isDoneCurrentPhase } - .sumOf { it.ship.pointCost * it.movementCoefficient } + 100.0 / (1 + shipList.sumOf { it.ship.pointCost }) } fun GameState.getValidAttackersWith(target: ShipInstance): Map, Set>> { @@ -30,6 +28,7 @@ fun GameState.isValidTarget(ship: ShipInstance, weapon: ShipWeaponInstance, pick return when (val weaponSpec = weapon.weapon) { is AreaWeapon -> target.owner.side != ship.owner.side && (targetPos - pickRequest.boundary.closestPointTo(targetPos)).length < weaponSpec.areaRadius + else -> target.owner.side in (pickRequest.type as PickType.Ship).allowSides && isValidPick(pickRequest, PickResponse.Ship(target.id)) } @@ -53,24 +52,7 @@ fun GameState.calculateAttackPhaseInitiative(): InitiativeMap = .values .groupBy { it.owner } .mapValues { (_, shipList) -> - shipList - .filter { !it.isDoneCurrentPhase } - .sumOf { ship -> - val allWeapons = ship.armaments - .filterValues { weapon -> hasValidTargets(ship, weapon) } - val usableWeapons = allWeapons - ship.usedArmaments - - val boardingPartyShot = (if (ship.canSendBoardingParty || ship.hasSentBoardingParty) 1 else 0) - val usableBoardingPartyShot = (if (ship.canSendBoardingParty) 1 else 0) - - val disruptionPulseShot = (if (ship.canUseDisruptionPulse || ship.hasUsedDisruptionPulse) 1 else 0) - val usableDisruptionPulseShot = (if (ship.canUseDisruptionPulse) 1 else 0) - - val allWeaponShots = allWeapons.values.sumOf { it.weapon.numShots * it.weapon.firingArcs.size } + boardingPartyShot + disruptionPulseShot - val usableWeaponShots = usableWeapons.values.sumOf { it.weapon.numShots * it.weapon.firingArcs.size } + usableBoardingPartyShot + usableDisruptionPulseShot - - ship.ship.pointCost * (usableWeaponShots.toDouble() / allWeaponShots) - } + 100.0 / (1 + shipList.sumOf { it.ship.pointCost }) } fun GameState.withRecalculatedInitiative(initiativeMapAccessor: GameState.() -> InitiativeMap): GameState { diff --git a/src/jvmTest/kotlin/net/starshipfights/game/ai/AITesting.kt b/src/jvmTest/kotlin/net/starshipfights/game/ai/AITesting.kt index 9525d9d..2c81d74 100644 --- a/src/jvmTest/kotlin/net/starshipfights/game/ai/AITesting.kt +++ b/src/jvmTest/kotlin/net/starshipfights/game/ai/AITesting.kt @@ -17,7 +17,7 @@ object AITesting { fun main(args: Array) { UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel") - val instinctVectorCounts = listOf(5, 11, 17) + val instinctVectorCounts = listOf(4, 9, 14) val instinctVectorOptions = instinctVectorCounts.map { it.toString() }.toTypedArray() val instinctVectorIndex = JOptionPane.showOptionDialog( -- 2.25.1