From 3b6f976a934e884ba6742ba7ca842839cd20a05d Mon Sep 17 00:00:00 2001 From: Divine Threepwood Date: Wed, 19 Aug 2026 00:55:08 +0200 Subject: [PATCH 01/11] first draft implemented --- .../game/strategy/DivineStrategy.kt | 18 +++++----- .../planetsudo/level/levelobjects/Agent.kt | 36 +++++++++++++++++++ .../levelobjects/AgentSpecialInterface.kt | 17 +++++++++ 3 files changed, 62 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/org/openbase/planetsudo/game/strategy/DivineStrategy.kt b/src/main/kotlin/org/openbase/planetsudo/game/strategy/DivineStrategy.kt index 327c2a92..4479abe2 100644 --- a/src/main/kotlin/org/openbase/planetsudo/game/strategy/DivineStrategy.kt +++ b/src/main/kotlin/org/openbase/planetsudo/game/strategy/DivineStrategy.kt @@ -64,7 +64,7 @@ class DivineStrategy(agent: AgentInterface) : StrategyLevelLegacy(agent) { // --------------------------------------------> "See Resources" swat SwatTeam.NOT_COMMANDER inCase { agent.seeResource && - (agent.isTonicAtLimit && agent.seeResource(ResourceType.Tonic)).not() + (agent.isTonicAtLimit && agent.seeResource(ResourceType.Tonic)).not() } then { agent.goToResource() } @@ -110,10 +110,10 @@ class DivineStrategy(agent: AgentInterface) : StrategyLevelLegacy(agent) { object : Rule("PickUp") { override fun constraint(): Boolean { return !agent.isCommander && ( - agent.isTouchingResource(ResourceType.DoublePoints) || agent.isTouchingResource( - ResourceType.ExtraMothershipFuel, - ) - ) + agent.isTouchingResource(ResourceType.DoublePoints) || agent.isTouchingResource( + ResourceType.ExtraMothershipFuel, + ) + ) } override fun action() { @@ -141,10 +141,10 @@ class DivineStrategy(agent: AgentInterface) : StrategyLevelLegacy(agent) { object : Rule("PickUp and Place") { override fun constraint(): Boolean { return agent.isCommander && ( - agent.isTouchingResource(ResourceType.DoublePoints) || agent.isTouchingResource( - ResourceType.ExtraMothershipFuel, - ) - ) && !mothership.isMarkerDeployed && !agent.seeMarker() + agent.isTouchingResource(ResourceType.DoublePoints) || agent.isTouchingResource( + ResourceType.ExtraMothershipFuel, + ) + ) && !mothership.isMarkerDeployed && !agent.seeMarker() } override fun action() { diff --git a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Agent.kt b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Agent.kt index ad0b6341..e8d5cc81 100644 --- a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Agent.kt +++ b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Agent.kt @@ -162,6 +162,40 @@ class Agent( override fun isCollisionDetectedAtRight(beta: Int): Boolean = level.collisionDetected(computeFutureBoundsRight(beta)) + override fun seeWallAtLeft(beta: Int, distance: Int): Boolean { + // compute front point of the agent + val dir = Direction2D(direction.angle).also { it.angle -= beta } + val front = position.clone() + front.translate(dir, (width / 2).toInt()) + + // step along the direction and check small area for walls + for (d in 1..distance) { + val p = front.clone() + p.translate(dir, d) + val testRect = Rectangle2D.Double(p.x - 1.0, p.y - 1.0, 2.0, 2.0) + if (level.containsWall(testRect)) { + return true + } + } + return false + } + + override fun seeWallAtRight(beta: Int, distance: Int): Boolean { + val dir = Direction2D(direction.angle).also { it.angle += beta } + val front = position.clone() + front.translate(dir, (width / 2).toInt()) + + for (d in 1..distance) { + val p = front.clone() + p.translate(dir, d) + val testRect = Rectangle2D.Double(p.x - 1.0, p.y - 1.0, 2.0, 2.0) + if (level.containsWall(testRect)) { + return true + } + } + return false + } + override val isShifting get(): Boolean = shiftTonic > 0.0 @@ -931,6 +965,8 @@ class Agent( const val MAX_TONIC: Int = 3 const val AGENT_SIZE: Int = 50 const val AGENT_VIEW_DISTANCE: Int = AGENT_SIZE + /** Default see distance (in pixels) for agent line-of-sight checks */ + const val SEE_DISTANCE: Int = AGENT_VIEW_DISTANCE const val DEFAULT_AGENT_SPEED: Int = 6 const val SHIFT_EXTRA_SPEED: Int = 3 const val SHIFT_TONIC_CONSUMPTION: Double = 0.01 diff --git a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/AgentSpecialInterface.kt b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/AgentSpecialInterface.kt index 43e86f4d..9522e8f8 100644 --- a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/AgentSpecialInterface.kt +++ b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/AgentSpecialInterface.kt @@ -4,6 +4,7 @@ interface AgentSpecialInterface : Agent override val de get() = AgentSpecialInterfaceGermanWrapper(this) + /** * Gibt an, ob ein Zusammenstoß mit einer Wand zur linken Seite bevorsteht. * @@ -11,6 +12,14 @@ interface AgentSpecialInterface : Agent */ fun isCollisionDetectedAtLeft(beta: Int): Boolean + /** + * Abfrage, ob sich eine Wand in der linken Sichtweite befindet. + * + * @param beta Winkel in Grad (0 = vorne - 90 = links - 180 = hinten) + * @param distance Sicht-Distanz in Pixeln + */ + fun seeWallAtLeft(beta: Int, distance: Int): Boolean + /** * Gibt an, ob ein Zusammenstoß mit einer Wand zur rechten Seite bevorsteht. * @@ -18,6 +27,14 @@ interface AgentSpecialInterface : Agent */ fun isCollisionDetectedAtRight(beta: Int): Boolean + /** + * Abfrage, ob sich eine Wand in der rechten Sichtweite befindet. + * + * @param beta Winkel in Grad (0 = vorne - 90 = rechts - 180 = hinten) + * @param distance Sicht-Distanz in Pixeln + */ + fun seeWallAtRight(beta: Int, distance: Int): Boolean + /** * Abfrage, ob der Agent aktuell unsichtbar ist. */ From 40617fb9331e7e8157bd9275929220de6d3026c5 Mon Sep 17 00:00:00 2001 From: Divine Threepwood Date: Wed, 19 Aug 2026 01:07:36 +0200 Subject: [PATCH 02/11] Add hitsWall ray-stepper and use in Agent.seeWall - Implement AbstractLevel.hitsWall(start, direction, maxDistance) with minimal allocations - Replace Agent.seeWallAtLeft/Right to call hitsWall Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../game/strategy/DivineStrategy.kt | 18 ++++++------- .../planetsudo/level/AbstractLevel.kt | 27 +++++++++++++++++++ .../planetsudo/level/levelobjects/Agent.kt | 25 +++-------------- .../level/levelobjects/AgentMock.kt | 8 ++++++ .../levelobjects/AgentSpecialInterface.kt | 1 - 5 files changed, 47 insertions(+), 32 deletions(-) diff --git a/src/main/kotlin/org/openbase/planetsudo/game/strategy/DivineStrategy.kt b/src/main/kotlin/org/openbase/planetsudo/game/strategy/DivineStrategy.kt index 4479abe2..327c2a92 100644 --- a/src/main/kotlin/org/openbase/planetsudo/game/strategy/DivineStrategy.kt +++ b/src/main/kotlin/org/openbase/planetsudo/game/strategy/DivineStrategy.kt @@ -64,7 +64,7 @@ class DivineStrategy(agent: AgentInterface) : StrategyLevelLegacy(agent) { // --------------------------------------------> "See Resources" swat SwatTeam.NOT_COMMANDER inCase { agent.seeResource && - (agent.isTonicAtLimit && agent.seeResource(ResourceType.Tonic)).not() + (agent.isTonicAtLimit && agent.seeResource(ResourceType.Tonic)).not() } then { agent.goToResource() } @@ -110,10 +110,10 @@ class DivineStrategy(agent: AgentInterface) : StrategyLevelLegacy(agent) { object : Rule("PickUp") { override fun constraint(): Boolean { return !agent.isCommander && ( - agent.isTouchingResource(ResourceType.DoublePoints) || agent.isTouchingResource( - ResourceType.ExtraMothershipFuel, - ) - ) + agent.isTouchingResource(ResourceType.DoublePoints) || agent.isTouchingResource( + ResourceType.ExtraMothershipFuel, + ) + ) } override fun action() { @@ -141,10 +141,10 @@ class DivineStrategy(agent: AgentInterface) : StrategyLevelLegacy(agent) { object : Rule("PickUp and Place") { override fun constraint(): Boolean { return agent.isCommander && ( - agent.isTouchingResource(ResourceType.DoublePoints) || agent.isTouchingResource( - ResourceType.ExtraMothershipFuel, - ) - ) && !mothership.isMarkerDeployed && !agent.seeMarker() + agent.isTouchingResource(ResourceType.DoublePoints) || agent.isTouchingResource( + ResourceType.ExtraMothershipFuel, + ) + ) && !mothership.isMarkerDeployed && !agent.seeMarker() } override fun action() { diff --git a/src/main/kotlin/org/openbase/planetsudo/level/AbstractLevel.kt b/src/main/kotlin/org/openbase/planetsudo/level/AbstractLevel.kt index dc710792..1d580a0a 100644 --- a/src/main/kotlin/org/openbase/planetsudo/level/AbstractLevel.kt +++ b/src/main/kotlin/org/openbase/planetsudo/level/AbstractLevel.kt @@ -7,6 +7,7 @@ import org.openbase.planetsudo.game.GameManager import org.openbase.planetsudo.game.Team import org.openbase.planetsudo.game.strategy.AbstractStrategy import org.openbase.planetsudo.geometry.Base2D +import org.openbase.planetsudo.geometry.Direction2D import org.openbase.planetsudo.geometry.Point2D import org.openbase.planetsudo.level.levelobjects.Agent import org.openbase.planetsudo.level.levelobjects.Mothership @@ -179,6 +180,32 @@ abstract class AbstractLevel : AbstractGameObject, Runnable { return !levelBorderPolygon.contains(bounds) } + /** + * Steps along a direction from start point and returns true when the first wall/border is hit. + * Uses a single reusable rectangle and incremental translation to reduce allocations. + */ + fun hitsWall(start: org.openbase.planetsudo.geometry.Point2D, direction: org.openbase.planetsudo.geometry.Direction2D, maxDistance: Int): Boolean { + val p = start.clone() + val testRect = Rectangle2D.Double(0.0, 0.0, 2.0, 2.0) + val walls = levelWallPolygons + for (i in 1..maxDistance) { + p.translate(direction, 1) + testRect.x = p.xy[0] - 1.0 + testRect.y = p.xy[1] - 1.0 + if (walls != null) { + for (wall in walls) { + if (wall.intersects(testRect) || wall.contains(testRect)) { + return true + } + } + } + if (!levelBorderPolygon.contains(testRect)) { + return true + } + } + return false + } + fun collisionDetected(bounds: Rectangle2D?): Boolean { return containsWall(bounds) } diff --git a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Agent.kt b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Agent.kt index e8d5cc81..63c2d814 100644 --- a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Agent.kt +++ b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Agent.kt @@ -163,37 +163,17 @@ class Agent( level.collisionDetected(computeFutureBoundsRight(beta)) override fun seeWallAtLeft(beta: Int, distance: Int): Boolean { - // compute front point of the agent val dir = Direction2D(direction.angle).also { it.angle -= beta } val front = position.clone() front.translate(dir, (width / 2).toInt()) - - // step along the direction and check small area for walls - for (d in 1..distance) { - val p = front.clone() - p.translate(dir, d) - val testRect = Rectangle2D.Double(p.x - 1.0, p.y - 1.0, 2.0, 2.0) - if (level.containsWall(testRect)) { - return true - } - } - return false + return level.hitsWall(front, dir, distance) } override fun seeWallAtRight(beta: Int, distance: Int): Boolean { val dir = Direction2D(direction.angle).also { it.angle += beta } val front = position.clone() front.translate(dir, (width / 2).toInt()) - - for (d in 1..distance) { - val p = front.clone() - p.translate(dir, d) - val testRect = Rectangle2D.Double(p.x - 1.0, p.y - 1.0, 2.0, 2.0) - if (level.containsWall(testRect)) { - return true - } - } - return false + return level.hitsWall(front, dir, distance) } override val isShifting @@ -965,6 +945,7 @@ class Agent( const val MAX_TONIC: Int = 3 const val AGENT_SIZE: Int = 50 const val AGENT_VIEW_DISTANCE: Int = AGENT_SIZE + /** Default see distance (in pixels) for agent line-of-sight checks */ const val SEE_DISTANCE: Int = AGENT_VIEW_DISTANCE const val DEFAULT_AGENT_SPEED: Int = 6 diff --git a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/AgentMock.kt b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/AgentMock.kt index 1df86c21..06c62675 100644 --- a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/AgentMock.kt +++ b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/AgentMock.kt @@ -250,6 +250,14 @@ class AgentMock : AgentInterface { error("Mock does not offer any functionality.") } + override fun seeWallAtLeft(beta: Int, distance: Int): Boolean { + error("Mock does not offer any functionality.") + } + + override fun seeWallAtRight(beta: Int, distance: Int): Boolean { + error("Mock does not offer any functionality.") + } + override fun constructTower(type: Tower.TowerType) { error("Mock does not offer any functionality.") } diff --git a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/AgentSpecialInterface.kt b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/AgentSpecialInterface.kt index 9522e8f8..8dec00df 100644 --- a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/AgentSpecialInterface.kt +++ b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/AgentSpecialInterface.kt @@ -4,7 +4,6 @@ interface AgentSpecialInterface : Agent override val de get() = AgentSpecialInterfaceGermanWrapper(this) - /** * Gibt an, ob ein Zusammenstoß mit einer Wand zur linken Seite bevorsteht. * From 9da7c1f28dbd57b5ae02d8186bd63ab3f7d3ae66 Mon Sep 17 00:00:00 2001 From: Divine Threepwood Date: Fri, 21 Aug 2026 16:59:25 +0200 Subject: [PATCH 03/11] finish first draft --- build.gradle.kts | 2 ++ .../game/strategy/DivineStrategy.kt | 36 +++++++++++++------ .../planetsudo/level/levelobjects/Agent.kt | 18 +++++----- .../levelobjects/AgentSpecialInterface.kt | 6 ++-- .../level/levelobjects/Mothership.kt | 6 ++-- 5 files changed, 43 insertions(+), 25 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 555b55a7..3e1b3bea 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -13,6 +13,7 @@ plugins { `maven-publish` signing id("io.github.gradle-nexus.publish-plugin") version "2.0.0" + kotlin("plugin.lombok") version "1.9.22" } group = "org.openbase" @@ -55,6 +56,7 @@ dependencies { testImplementation(libs.org.jetbrains.kotlin.test.junit5) testImplementation(libs.io.mockk) testImplementation(libs.org.amshove.kluent) + testImplementation(kotlin("test")) } nexusPublishing { diff --git a/src/main/kotlin/org/openbase/planetsudo/game/strategy/DivineStrategy.kt b/src/main/kotlin/org/openbase/planetsudo/game/strategy/DivineStrategy.kt index 327c2a92..763c0ad8 100644 --- a/src/main/kotlin/org/openbase/planetsudo/game/strategy/DivineStrategy.kt +++ b/src/main/kotlin/org/openbase/planetsudo/game/strategy/DivineStrategy.kt @@ -40,6 +40,20 @@ class DivineStrategy(agent: AgentInterface) : StrategyLevelLegacy(agent) { ) "Discover" commander inCase { true } then { agent.goRight(4) } +// "Discover" commander inCase { true } then { agent.goRight(4) } +// "Follow Left Wall" commander inCase { agent.seeWallAtLeft(45) } then { agent.go() } + "Follow Wall" commander inCase { + agent.seeWallAtRight(135) && agent.seeWallAtRight(45) && !agent.seeWallAtRight( + 0, + ) + } then { agent.go() } + "Follow Corner" commander inCase { agent.seeWallAtRight(45) && agent.seeWallAtRight(0) } then { agent.goLeft(20) } + "Follow Edge" commander inCase { + agent.seeWallAtRight(135) && !agent.seeWallAtRight(100) && !agent.seeWallAtRight( + 0, + ) + } then { agent.turnRight(10) } + "Scan" commander inCase { tower.type == Tower.TowerType.ObservationTower && tower.levelSize == LevelSize.UNKNOWN } then { tower.scanLevelSize() @@ -64,7 +78,7 @@ class DivineStrategy(agent: AgentInterface) : StrategyLevelLegacy(agent) { // --------------------------------------------> "See Resources" swat SwatTeam.NOT_COMMANDER inCase { agent.seeResource && - (agent.isTonicAtLimit && agent.seeResource(ResourceType.Tonic)).not() + (agent.isTonicAtLimit && agent.seeResource(ResourceType.Tonic)).not() } then { agent.goToResource() } @@ -110,10 +124,10 @@ class DivineStrategy(agent: AgentInterface) : StrategyLevelLegacy(agent) { object : Rule("PickUp") { override fun constraint(): Boolean { return !agent.isCommander && ( - agent.isTouchingResource(ResourceType.DoublePoints) || agent.isTouchingResource( - ResourceType.ExtraMothershipFuel, - ) - ) + agent.isTouchingResource(ResourceType.DoublePoints) || agent.isTouchingResource( + ResourceType.ExtraMothershipFuel, + ) + ) } override fun action() { @@ -141,10 +155,10 @@ class DivineStrategy(agent: AgentInterface) : StrategyLevelLegacy(agent) { object : Rule("PickUp and Place") { override fun constraint(): Boolean { return agent.isCommander && ( - agent.isTouchingResource(ResourceType.DoublePoints) || agent.isTouchingResource( - ResourceType.ExtraMothershipFuel, - ) - ) && !mothership.isMarkerDeployed && !agent.seeMarker() + agent.isTouchingResource(ResourceType.DoublePoints) || agent.isTouchingResource( + ResourceType.ExtraMothershipFuel, + ) + ) && !mothership.isMarkerDeployed && !agent.seeMarker() } override fun action() { @@ -352,7 +366,7 @@ class DivineStrategy(agent: AgentInterface) : StrategyLevelLegacy(agent) { ) // --------------------------------------------> createRule( - object : Rule("RepaireMothership") { + object : Rule("RepairMothership") { override fun constraint(): Boolean { return mothership.isDamaged && agent.isAtMothership } @@ -488,7 +502,7 @@ class DivineStrategy(agent: AgentInterface) : StrategyLevelLegacy(agent) { ) // --------------------------------------------> createRule( - object : Rule("Follow Wall", SwatTeam.COMMANDER) { + object : Rule("Avoid Wall Collision", SwatTeam.COMMANDER) { override fun constraint(): Boolean { return agent.isCollisionDetected } diff --git a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Agent.kt b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Agent.kt index 63c2d814..10bef379 100644 --- a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Agent.kt +++ b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Agent.kt @@ -681,7 +681,7 @@ class Agent( direction.turnTo(position, teamAgent.position) ap.getActionPoint(value * 2) teamAgent.spendFuel(useFuel(value)).also { - // charge leftover back to origin + // charge leftover back to origin leftover -> spendFuel(leftover) } @@ -752,14 +752,15 @@ class Agent( override fun goToSupportAgent() { try { - val agentToSupport = mothership.getAgentToSupport(this) - if (agentToSupport !== this) { - goTo { - agentToSupport.levelView?.getRelativeDirection(this@Agent) - ?.also { angle = it } + mothership.getAgentToSupport(this)?.let { agentToSupport -> + if (agentToSupport !== this) { + goTo { + agentToSupport.levelView?.getRelativeDirection(this@Agent) + ?.also { angle = it } + } + } else { + throw CouldNotPerformException("Could not support itself!") } - } else { - throw CouldNotPerformException("Could not support itself!") } } catch (ex: CouldNotPerformException) { ExceptionPrinter.printHistory(CouldNotPerformException("Could not goToSupportAgent!", ex), LOGGER) @@ -947,7 +948,6 @@ class Agent( const val AGENT_VIEW_DISTANCE: Int = AGENT_SIZE /** Default see distance (in pixels) for agent line-of-sight checks */ - const val SEE_DISTANCE: Int = AGENT_VIEW_DISTANCE const val DEFAULT_AGENT_SPEED: Int = 6 const val SHIFT_EXTRA_SPEED: Int = 3 const val SHIFT_TONIC_CONSUMPTION: Double = 0.01 diff --git a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/AgentSpecialInterface.kt b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/AgentSpecialInterface.kt index 8dec00df..d56352bb 100644 --- a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/AgentSpecialInterface.kt +++ b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/AgentSpecialInterface.kt @@ -1,5 +1,7 @@ package org.openbase.planetsudo.level.levelobjects +import org.openbase.planetsudo.level.levelobjects.Agent.Companion.AGENT_VIEW_DISTANCE + interface AgentSpecialInterface : AgentBattleInterface { override val de get() = AgentSpecialInterfaceGermanWrapper(this) @@ -17,7 +19,7 @@ interface AgentSpecialInterface : Agent * @param beta Winkel in Grad (0 = vorne - 90 = links - 180 = hinten) * @param distance Sicht-Distanz in Pixeln */ - fun seeWallAtLeft(beta: Int, distance: Int): Boolean + fun seeWallAtLeft(beta: Int, distance: Int = AGENT_VIEW_DISTANCE): Boolean /** * Gibt an, ob ein Zusammenstoß mit einer Wand zur rechten Seite bevorsteht. @@ -32,7 +34,7 @@ interface AgentSpecialInterface : Agent * @param beta Winkel in Grad (0 = vorne - 90 = rechts - 180 = hinten) * @param distance Sicht-Distanz in Pixeln */ - fun seeWallAtRight(beta: Int, distance: Int): Boolean + fun seeWallAtRight(beta: Int, distance: Int = AGENT_VIEW_DISTANCE): Boolean /** * Abfrage, ob der Agent aktuell unsichtbar ist. diff --git a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Mothership.kt b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Mothership.kt index a0340f11..32a62476 100644 --- a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Mothership.kt +++ b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Mothership.kt @@ -320,16 +320,16 @@ class Mothership(id: Int, team: Team, level: AbstractLevel) : } @Throws(CouldNotPerformException::class) - fun getAgentToSupport(helper: Agent): Agent { + fun getAgentToSupport(helper: Agent): Agent? { supportChannelLock.read { if (supportChannel.isEmpty()) { - throw CouldNotPerformException("No support necessary.") + return null } val supportCaller: Agent = supportChannel .filterNot { it === helper } // do not help yourself .minByOrNull { helper.levelView?.getDistance(it) ?: Int.MAX_VALUE } - ?: throw CouldNotPerformException("No support possible.") + ?: return null // remove caller from support channel if support is possible if (supportCaller.bounds.intersects(helper.viewBounds)) { From fc20911a8077c885650935ba6f1e9b58cdec21b2 Mon Sep 17 00:00:00 2001 From: Divine Threepwood Date: Thu, 10 Sep 2026 20:34:33 +0200 Subject: [PATCH 04/11] improve wall detection api --- .../game/strategy/DivineStrategy.kt | 47 ++++++++++++++----- .../planetsudo/level/levelobjects/Agent.kt | 8 ++-- .../levelobjects/AgentLegacyInterface.kt | 3 -- .../level/levelobjects/AgentMock.kt | 5 +- .../levelobjects/AgentSpecialInterface.kt | 5 +- .../level/levelobjects/WallDistance.kt | 14 ++++++ .../view/level/levelobjects/TowerTopPanel.kt | 12 +++-- 7 files changed, 66 insertions(+), 28 deletions(-) create mode 100644 src/main/kotlin/org/openbase/planetsudo/level/levelobjects/WallDistance.kt diff --git a/src/main/kotlin/org/openbase/planetsudo/game/strategy/DivineStrategy.kt b/src/main/kotlin/org/openbase/planetsudo/game/strategy/DivineStrategy.kt index 763c0ad8..d7c35271 100644 --- a/src/main/kotlin/org/openbase/planetsudo/game/strategy/DivineStrategy.kt +++ b/src/main/kotlin/org/openbase/planetsudo/game/strategy/DivineStrategy.kt @@ -5,6 +5,7 @@ import org.openbase.planetsudo.level.LevelSize import org.openbase.planetsudo.level.levelobjects.AgentInterface import org.openbase.planetsudo.level.levelobjects.Resource.ResourceType import org.openbase.planetsudo.level.levelobjects.Tower +import org.openbase.planetsudo.level.levelobjects.WallDistance /** * @@ -40,20 +41,44 @@ class DivineStrategy(agent: AgentInterface) : StrategyLevelLegacy(agent) { ) "Discover" commander inCase { true } then { agent.goRight(4) } -// "Discover" commander inCase { true } then { agent.goRight(4) } -// "Follow Left Wall" commander inCase { agent.seeWallAtLeft(45) } then { agent.go() } + + // Keep a bit of clearance from the wall while still following it. The distance parameter ensures the + // agent does not hug the wall tightly when it is supposed to stay parallel to it. + "Re-find Wall" commander inCase { + !agent.seeWallAtRight(45, WallDistance.VERY_FAR_AWAY) && !agent.seeWallAtRight( + 90, + WallDistance.VERY_FAR_AWAY, + ) && !agent.seeWallAtRight(135, WallDistance.VERY_FAR_AWAY) + } then { agent.turnRight(2) } + "Follow Wall With Clearance" commander inCase { + ( + agent.seeWallAtRight(45, WallDistance.CLOSE) + && agent.seeWallAtRight(135, WallDistance.CLOSE) + ) && !agent.seeWallAtRight(0, WallDistance.VERY_FAR_AWAY) + } then { agent.goLeft(5) } + "Follow Wall Closer" commander inCase { + ( + agent.seeWallAtRight(45, WallDistance.VERY_FAR_AWAY) + && agent.seeWallAtRight(135, WallDistance.VERY_FAR_AWAY) + ) && !agent.seeWallAtRight(0, WallDistance.VERY_FAR_AWAY) + } then { agent.goRight(5) } "Follow Wall" commander inCase { - agent.seeWallAtRight(135) && agent.seeWallAtRight(45) && !agent.seeWallAtRight( - 0, - ) + ( + agent.seeWallAtRight(45, WallDistance.FAR_AWAY) + && agent.seeWallAtRight(135, WallDistance.FAR_AWAY) + ) && !agent.seeWallAtRight(0, WallDistance.VERY_FAR_AWAY) } then { agent.go() } - "Follow Corner" commander inCase { agent.seeWallAtRight(45) && agent.seeWallAtRight(0) } then { agent.goLeft(20) } + "Follow Corner" commander inCase { + agent.seeWallAtRight(0, WallDistance.FAR_AWAY) && (agent.seeWallAtRight( + 45, + WallDistance.VERY_FAR_AWAY, + ) || agent.seeWallAtRight(90, WallDistance.VERY_FAR_AWAY)) + } then { agent.goLeft(20) } "Follow Edge" commander inCase { - agent.seeWallAtRight(135) && !agent.seeWallAtRight(100) && !agent.seeWallAtRight( - 0, - ) - } then { agent.turnRight(10) } - + agent.seeWallAtRight(135, WallDistance.VERY_FAR_AWAY) + && !agent.seeWallAtRight(110, WallDistance.VERY_FAR_AWAY) + && !agent.seeWallAtRight(0, WallDistance.FAR_AWAY) + } then { agent.turnRight(20) } "Scan" commander inCase { tower.type == Tower.TowerType.ObservationTower && tower.levelSize == LevelSize.UNKNOWN } then { tower.scanLevelSize() diff --git a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Agent.kt b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Agent.kt index 10bef379..9febb79e 100644 --- a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Agent.kt +++ b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Agent.kt @@ -162,18 +162,18 @@ class Agent( override fun isCollisionDetectedAtRight(beta: Int): Boolean = level.collisionDetected(computeFutureBoundsRight(beta)) - override fun seeWallAtLeft(beta: Int, distance: Int): Boolean { + override fun seeWallAtLeft(beta: Int, distance: WallDistance): Boolean { val dir = Direction2D(direction.angle).also { it.angle -= beta } val front = position.clone() front.translate(dir, (width / 2).toInt()) - return level.hitsWall(front, dir, distance) + return level.hitsWall(front, dir, distance.pixel) } - override fun seeWallAtRight(beta: Int, distance: Int): Boolean { + override fun seeWallAtRight(beta: Int, distance: WallDistance): Boolean { val dir = Direction2D(direction.angle).also { it.angle += beta } val front = position.clone() front.translate(dir, (width / 2).toInt()) - return level.hitsWall(front, dir, distance) + return level.hitsWall(front, dir, distance.pixel) } override val isShifting diff --git a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/AgentLegacyInterface.kt b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/AgentLegacyInterface.kt index 52d00547..525c1368 100644 --- a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/AgentLegacyInterface.kt +++ b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/AgentLegacyInterface.kt @@ -17,7 +17,6 @@ interface AgentLegacyInterface : AgentSpecialInterface : AgentBattleInterface { @@ -19,7 +18,7 @@ interface AgentSpecialInterface : Agent * @param beta Winkel in Grad (0 = vorne - 90 = links - 180 = hinten) * @param distance Sicht-Distanz in Pixeln */ - fun seeWallAtLeft(beta: Int, distance: Int = AGENT_VIEW_DISTANCE): Boolean + fun seeWallAtLeft(beta: Int, distance: WallDistance = WallDistance.FAR): Boolean /** * Gibt an, ob ein Zusammenstoß mit einer Wand zur rechten Seite bevorsteht. @@ -34,7 +33,7 @@ interface AgentSpecialInterface : Agent * @param beta Winkel in Grad (0 = vorne - 90 = rechts - 180 = hinten) * @param distance Sicht-Distanz in Pixeln */ - fun seeWallAtRight(beta: Int, distance: Int = AGENT_VIEW_DISTANCE): Boolean + fun seeWallAtRight(beta: Int, distance: WallDistance = WallDistance.FAR): Boolean /** * Abfrage, ob der Agent aktuell unsichtbar ist. diff --git a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/WallDistance.kt b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/WallDistance.kt new file mode 100644 index 00000000..b202f18a --- /dev/null +++ b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/WallDistance.kt @@ -0,0 +1,14 @@ +package org.openbase.planetsudo.level.levelobjects + +import org.openbase.planetsudo.level.levelobjects.Agent.Companion.AGENT_SIZE + +enum class WallDistance(val pixel: Int) { + VERY_CLOSE(pixel = OFFSET + agentFactor(0.1)), + CLOSE(pixel = OFFSET + agentFactor(0.5)), + FAR(pixel = OFFSET + agentFactor(1.0)), + FAR_AWAY(pixel = OFFSET + agentFactor(1.5)), + VERY_FAR_AWAY(pixel = OFFSET + agentFactor(2.0)), +} + +private val OFFSET = (AGENT_SIZE / 2) +private fun agentFactor(factor: Double) = (AGENT_SIZE * factor).toInt() diff --git a/src/main/kotlin/org/openbase/planetsudo/view/level/levelobjects/TowerTopPanel.kt b/src/main/kotlin/org/openbase/planetsudo/view/level/levelobjects/TowerTopPanel.kt index 4a548443..929afce8 100644 --- a/src/main/kotlin/org/openbase/planetsudo/view/level/levelobjects/TowerTopPanel.kt +++ b/src/main/kotlin/org/openbase/planetsudo/view/level/levelobjects/TowerTopPanel.kt @@ -14,6 +14,7 @@ import java.awt.geom.Arc2D import java.beans.PropertyChangeEvent import java.beans.PropertyChangeListener import javax.swing.Timer +import kotlin.math.hypot /** * @@ -28,7 +29,7 @@ class TowerTopPanel(tower: Tower, parentPanel: TowerPanel) : DrawLayer.BACKGROUND, ), PropertyChangeListener { - private val tower: Tower + private val tower: Tower? private var gg2: Graphics2D? = null private var direction: Direction2D? = null @@ -70,7 +71,8 @@ class TowerTopPanel(tower: Tower, parentPanel: TowerPanel) : } override fun paintComponent(g2: Graphics2D, gl: Graphics2D) { - // only paint if constructed + // Guard against stale or already-removed tower references during asynchronous Swing repaints. + val currentTower = tower ?: return if (!resource.isConstructed) { return } @@ -79,7 +81,7 @@ class TowerTopPanel(tower: Tower, parentPanel: TowerPanel) : direction = resource.direction gg2 = g2.create() as Graphics2D - paintImageRotated(tower.direction, gg2!!) + paintImageRotated(currentTower.direction, gg2!!) // draw scanning waves if active if (scanning) { @@ -116,7 +118,7 @@ class TowerTopPanel(tower: Tower, parentPanel: TowerPanel) : // parentResourcePanel.removeChild(this); // } // } - if (evt.propertyName == Tower.TOWER_SCAN_LEVEL) { + if (evt.propertyName == Tower.TOWER_SCAN_LEVEL && tower != null) { // start wave animation: compute max radius to level borders val levelBounds = tower.level.levelBorderPolygon.bounds2D val cx = tower.position.x @@ -132,7 +134,7 @@ class TowerTopPanel(tower: Tower, parentPanel: TowerPanel) : for (c in corners) { val dx = c[0] - cx val dy = c[1] - cy - val d = Math.hypot(dx, dy) + val d = hypot(dx, dy) if (d > maxd) maxd = d } scanMaxRadius = maxd From b69aa0d1b826761985148f6741a0fc07281f662d Mon Sep 17 00:00:00 2001 From: Divine Threepwood Date: Thu, 10 Sep 2026 20:34:54 +0200 Subject: [PATCH 05/11] apply formatting --- .../game/strategy/DivineStrategy.kt | 52 ++++++++++--------- .../planetsudo/level/levelobjects/Agent.kt | 2 +- .../levelobjects/AgentSpecialInterface.kt | 1 - 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/main/kotlin/org/openbase/planetsudo/game/strategy/DivineStrategy.kt b/src/main/kotlin/org/openbase/planetsudo/game/strategy/DivineStrategy.kt index d7c35271..6f2f6a14 100644 --- a/src/main/kotlin/org/openbase/planetsudo/game/strategy/DivineStrategy.kt +++ b/src/main/kotlin/org/openbase/planetsudo/game/strategy/DivineStrategy.kt @@ -52,32 +52,34 @@ class DivineStrategy(agent: AgentInterface) : StrategyLevelLegacy(agent) { } then { agent.turnRight(2) } "Follow Wall With Clearance" commander inCase { ( - agent.seeWallAtRight(45, WallDistance.CLOSE) - && agent.seeWallAtRight(135, WallDistance.CLOSE) - ) && !agent.seeWallAtRight(0, WallDistance.VERY_FAR_AWAY) + agent.seeWallAtRight(45, WallDistance.CLOSE) && + agent.seeWallAtRight(135, WallDistance.CLOSE) + ) && !agent.seeWallAtRight(0, WallDistance.VERY_FAR_AWAY) } then { agent.goLeft(5) } "Follow Wall Closer" commander inCase { ( - agent.seeWallAtRight(45, WallDistance.VERY_FAR_AWAY) - && agent.seeWallAtRight(135, WallDistance.VERY_FAR_AWAY) - ) && !agent.seeWallAtRight(0, WallDistance.VERY_FAR_AWAY) + agent.seeWallAtRight(45, WallDistance.VERY_FAR_AWAY) && + agent.seeWallAtRight(135, WallDistance.VERY_FAR_AWAY) + ) && !agent.seeWallAtRight(0, WallDistance.VERY_FAR_AWAY) } then { agent.goRight(5) } "Follow Wall" commander inCase { ( - agent.seeWallAtRight(45, WallDistance.FAR_AWAY) - && agent.seeWallAtRight(135, WallDistance.FAR_AWAY) - ) && !agent.seeWallAtRight(0, WallDistance.VERY_FAR_AWAY) + agent.seeWallAtRight(45, WallDistance.FAR_AWAY) && + agent.seeWallAtRight(135, WallDistance.FAR_AWAY) + ) && !agent.seeWallAtRight(0, WallDistance.VERY_FAR_AWAY) } then { agent.go() } "Follow Corner" commander inCase { - agent.seeWallAtRight(0, WallDistance.FAR_AWAY) && (agent.seeWallAtRight( - 45, - WallDistance.VERY_FAR_AWAY, - ) || agent.seeWallAtRight(90, WallDistance.VERY_FAR_AWAY)) + agent.seeWallAtRight(0, WallDistance.FAR_AWAY) && ( + agent.seeWallAtRight( + 45, + WallDistance.VERY_FAR_AWAY, + ) || agent.seeWallAtRight(90, WallDistance.VERY_FAR_AWAY) + ) } then { agent.goLeft(20) } "Follow Edge" commander inCase { - agent.seeWallAtRight(135, WallDistance.VERY_FAR_AWAY) - && !agent.seeWallAtRight(110, WallDistance.VERY_FAR_AWAY) - && !agent.seeWallAtRight(0, WallDistance.FAR_AWAY) + agent.seeWallAtRight(135, WallDistance.VERY_FAR_AWAY) && + !agent.seeWallAtRight(110, WallDistance.VERY_FAR_AWAY) && + !agent.seeWallAtRight(0, WallDistance.FAR_AWAY) } then { agent.turnRight(20) } "Scan" commander inCase { tower.type == Tower.TowerType.ObservationTower && tower.levelSize == LevelSize.UNKNOWN } then { @@ -103,7 +105,7 @@ class DivineStrategy(agent: AgentInterface) : StrategyLevelLegacy(agent) { // --------------------------------------------> "See Resources" swat SwatTeam.NOT_COMMANDER inCase { agent.seeResource && - (agent.isTonicAtLimit && agent.seeResource(ResourceType.Tonic)).not() + (agent.isTonicAtLimit && agent.seeResource(ResourceType.Tonic)).not() } then { agent.goToResource() } @@ -149,10 +151,10 @@ class DivineStrategy(agent: AgentInterface) : StrategyLevelLegacy(agent) { object : Rule("PickUp") { override fun constraint(): Boolean { return !agent.isCommander && ( - agent.isTouchingResource(ResourceType.DoublePoints) || agent.isTouchingResource( - ResourceType.ExtraMothershipFuel, - ) - ) + agent.isTouchingResource(ResourceType.DoublePoints) || agent.isTouchingResource( + ResourceType.ExtraMothershipFuel, + ) + ) } override fun action() { @@ -180,10 +182,10 @@ class DivineStrategy(agent: AgentInterface) : StrategyLevelLegacy(agent) { object : Rule("PickUp and Place") { override fun constraint(): Boolean { return agent.isCommander && ( - agent.isTouchingResource(ResourceType.DoublePoints) || agent.isTouchingResource( - ResourceType.ExtraMothershipFuel, - ) - ) && !mothership.isMarkerDeployed && !agent.seeMarker() + agent.isTouchingResource(ResourceType.DoublePoints) || agent.isTouchingResource( + ResourceType.ExtraMothershipFuel, + ) + ) && !mothership.isMarkerDeployed && !agent.seeMarker() } override fun action() { diff --git a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Agent.kt b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Agent.kt index 9febb79e..357ec105 100644 --- a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Agent.kt +++ b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Agent.kt @@ -681,7 +681,7 @@ class Agent( direction.turnTo(position, teamAgent.position) ap.getActionPoint(value * 2) teamAgent.spendFuel(useFuel(value)).also { - // charge leftover back to origin + // charge leftover back to origin leftover -> spendFuel(leftover) } diff --git a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/AgentSpecialInterface.kt b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/AgentSpecialInterface.kt index 6f31d4c3..e3ba34a2 100644 --- a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/AgentSpecialInterface.kt +++ b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/AgentSpecialInterface.kt @@ -1,6 +1,5 @@ package org.openbase.planetsudo.level.levelobjects - interface AgentSpecialInterface : AgentBattleInterface { override val de get() = AgentSpecialInterfaceGermanWrapper(this) From 9ed29605d2b0d3a75135c29d8189ee89360a287b Mon Sep 17 00:00:00 2001 From: Divine Threepwood Date: Fri, 2 Oct 2026 01:25:36 +0200 Subject: [PATCH 06/11] move scan origin to agent center, fix goToAdversary agent wall collision bug, simplify distance naming. --- .../game/strategy/DivineStrategy.kt | 70 +++++++++---------- .../planetsudo/level/levelobjects/Agent.kt | 15 ++-- .../level/levelobjects/WallDistance.kt | 14 ++-- 3 files changed, 50 insertions(+), 49 deletions(-) diff --git a/src/main/kotlin/org/openbase/planetsudo/game/strategy/DivineStrategy.kt b/src/main/kotlin/org/openbase/planetsudo/game/strategy/DivineStrategy.kt index 6f2f6a14..e79be10f 100644 --- a/src/main/kotlin/org/openbase/planetsudo/game/strategy/DivineStrategy.kt +++ b/src/main/kotlin/org/openbase/planetsudo/game/strategy/DivineStrategy.kt @@ -42,44 +42,44 @@ class DivineStrategy(agent: AgentInterface) : StrategyLevelLegacy(agent) { "Discover" commander inCase { true } then { agent.goRight(4) } - // Keep a bit of clearance from the wall while still following it. The distance parameter ensures the - // agent does not hug the wall tightly when it is supposed to stay parallel to it. - "Re-find Wall" commander inCase { - !agent.seeWallAtRight(45, WallDistance.VERY_FAR_AWAY) && !agent.seeWallAtRight( - 90, - WallDistance.VERY_FAR_AWAY, - ) && !agent.seeWallAtRight(135, WallDistance.VERY_FAR_AWAY) - } then { agent.turnRight(2) } +// // Keep a bit of clearance from the wall while still following it. The distance parameter ensures the +// // agent does not hug the wall tightly when it is supposed to stay parallel to it. +// "Re-find Wall" commander inCase { +// !agent.seeWallAtRight(45, WallDistance.VERY_FAR) && !agent.seeWallAtRight( +// 90, +// WallDistance.VERY_FAR, +// ) && !agent.seeWallAtRight(135, WallDistance.VERY_FAR) +// } then { agent.turnRight(2) } "Follow Wall With Clearance" commander inCase { ( - agent.seeWallAtRight(45, WallDistance.CLOSE) && - agent.seeWallAtRight(135, WallDistance.CLOSE) - ) && !agent.seeWallAtRight(0, WallDistance.VERY_FAR_AWAY) + agent.seeWallAtRight(45, WallDistance.CLOSE) && + agent.seeWallAtRight(135, WallDistance.CLOSE) + ) && !agent.seeWallAtRight(0, WallDistance.VERY_FAR) } then { agent.goLeft(5) } "Follow Wall Closer" commander inCase { ( - agent.seeWallAtRight(45, WallDistance.VERY_FAR_AWAY) && - agent.seeWallAtRight(135, WallDistance.VERY_FAR_AWAY) - ) && !agent.seeWallAtRight(0, WallDistance.VERY_FAR_AWAY) + agent.seeWallAtRight(45, WallDistance.VERY_FAR) && + agent.seeWallAtRight(135, WallDistance.VERY_FAR) + ) && !agent.seeWallAtRight(0, WallDistance.VERY_FAR) } then { agent.goRight(5) } "Follow Wall" commander inCase { ( - agent.seeWallAtRight(45, WallDistance.FAR_AWAY) && - agent.seeWallAtRight(135, WallDistance.FAR_AWAY) - ) && !agent.seeWallAtRight(0, WallDistance.VERY_FAR_AWAY) + agent.seeWallAtRight(45, WallDistance.FAR) && + agent.seeWallAtRight(135, WallDistance.FAR) + ) && !agent.seeWallAtRight(0, WallDistance.VERY_FAR) } then { agent.go() } "Follow Corner" commander inCase { - agent.seeWallAtRight(0, WallDistance.FAR_AWAY) && ( - agent.seeWallAtRight( - 45, - WallDistance.VERY_FAR_AWAY, - ) || agent.seeWallAtRight(90, WallDistance.VERY_FAR_AWAY) - ) + agent.seeWallAtRight(0, WallDistance.FAR) && ( + agent.seeWallAtRight( + 45, + WallDistance.VERY_FAR, + ) || agent.seeWallAtRight(90, WallDistance.VERY_FAR) + ) } then { agent.goLeft(20) } "Follow Edge" commander inCase { - agent.seeWallAtRight(135, WallDistance.VERY_FAR_AWAY) && - !agent.seeWallAtRight(110, WallDistance.VERY_FAR_AWAY) && - !agent.seeWallAtRight(0, WallDistance.FAR_AWAY) + agent.seeWallAtRight(135, WallDistance.VERY_FAR) && + !agent.seeWallAtRight(110, WallDistance.VERY_FAR) && + !agent.seeWallAtRight(0, WallDistance.FAR) } then { agent.turnRight(20) } "Scan" commander inCase { tower.type == Tower.TowerType.ObservationTower && tower.levelSize == LevelSize.UNKNOWN } then { @@ -105,7 +105,7 @@ class DivineStrategy(agent: AgentInterface) : StrategyLevelLegacy(agent) { // --------------------------------------------> "See Resources" swat SwatTeam.NOT_COMMANDER inCase { agent.seeResource && - (agent.isTonicAtLimit && agent.seeResource(ResourceType.Tonic)).not() + (agent.isTonicAtLimit && agent.seeResource(ResourceType.Tonic)).not() } then { agent.goToResource() } @@ -151,10 +151,10 @@ class DivineStrategy(agent: AgentInterface) : StrategyLevelLegacy(agent) { object : Rule("PickUp") { override fun constraint(): Boolean { return !agent.isCommander && ( - agent.isTouchingResource(ResourceType.DoublePoints) || agent.isTouchingResource( - ResourceType.ExtraMothershipFuel, - ) - ) + agent.isTouchingResource(ResourceType.DoublePoints) || agent.isTouchingResource( + ResourceType.ExtraMothershipFuel, + ) + ) } override fun action() { @@ -182,10 +182,10 @@ class DivineStrategy(agent: AgentInterface) : StrategyLevelLegacy(agent) { object : Rule("PickUp and Place") { override fun constraint(): Boolean { return agent.isCommander && ( - agent.isTouchingResource(ResourceType.DoublePoints) || agent.isTouchingResource( - ResourceType.ExtraMothershipFuel, - ) - ) && !mothership.isMarkerDeployed && !agent.seeMarker() + agent.isTouchingResource(ResourceType.DoublePoints) || agent.isTouchingResource( + ResourceType.ExtraMothershipFuel, + ) + ) && !mothership.isMarkerDeployed && !agent.seeMarker() } override fun action() { diff --git a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Agent.kt b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Agent.kt index 673a7970..045965cc 100644 --- a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Agent.kt +++ b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Agent.kt @@ -164,16 +164,12 @@ class Agent( override fun seeWallAtLeft(beta: Int, distance: WallDistance): Boolean { val dir = Direction2D(direction.angle).also { it.angle -= beta } - val front = position.clone() - front.translate(dir, (width / 2).toInt()) - return level.hitsWall(front, dir, distance.pixel) + return level.hitsWall(position.clone(), dir, distance.pixel) } override fun seeWallAtRight(beta: Int, distance: WallDistance): Boolean { val dir = Direction2D(direction.angle).also { it.angle += beta } - val front = position.clone() - front.translate(dir, (width / 2).toInt()) - return level.hitsWall(front, dir, distance.pixel) + return level.hitsWall(position.clone(), dir, distance.pixel) } override val isShifting @@ -681,7 +677,7 @@ class Agent( direction.turnTo(position, teamAgent.position) ap.getActionPoint(value * 2) teamAgent.spendFuel(useFuel(value)).also { - // charge leftover back to origin + // charge leftover back to origin leftover -> spendFuel(leftover) } @@ -788,7 +784,10 @@ class Agent( level.getAdversaryAgent(this)?.let { enemyAgent -> // do not come too close to the adversary agents if (enemyAgent.levelView!!.getDistance(this) >= (AGENT_SIZE / 2)) { - goTo { turnTo(position, enemyAgent.position) } + goTo { + enemyAgent.levelView?.getRelativeDirection(this@Agent) + ?.also { angle = it } + } } else { performAction { direction.turnTo(position, enemyAgent.position) diff --git a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/WallDistance.kt b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/WallDistance.kt index b202f18a..4006e083 100644 --- a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/WallDistance.kt +++ b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/WallDistance.kt @@ -2,13 +2,15 @@ package org.openbase.planetsudo.level.levelobjects import org.openbase.planetsudo.level.levelobjects.Agent.Companion.AGENT_SIZE +/** + * Maximum wall-search distance in pixels, measured from the agent's center. + * The values represent 1, 1.5, 2, and 2.5 times [AGENT_SIZE], respectively. + */ enum class WallDistance(val pixel: Int) { - VERY_CLOSE(pixel = OFFSET + agentFactor(0.1)), - CLOSE(pixel = OFFSET + agentFactor(0.5)), - FAR(pixel = OFFSET + agentFactor(1.0)), - FAR_AWAY(pixel = OFFSET + agentFactor(1.5)), - VERY_FAR_AWAY(pixel = OFFSET + agentFactor(2.0)), + VERY_CLOSE(pixel = agentFactor(1.0)), + CLOSE(pixel = agentFactor(1.5)), + FAR(pixel = agentFactor(2.0)), + VERY_FAR(pixel = agentFactor(2.5)), } -private val OFFSET = (AGENT_SIZE / 2) private fun agentFactor(factor: Double) = (AGENT_SIZE * factor).toInt() From 7bd2fb1bdcb6a76d75684ce1ebe8f746439ca36b Mon Sep 17 00:00:00 2001 From: Divine Threepwood Date: Fri, 2 Oct 2026 01:25:53 +0200 Subject: [PATCH 07/11] fix formatting --- .../game/strategy/DivineStrategy.kt | 50 +++++++++---------- .../planetsudo/level/levelobjects/Agent.kt | 2 +- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/main/kotlin/org/openbase/planetsudo/game/strategy/DivineStrategy.kt b/src/main/kotlin/org/openbase/planetsudo/game/strategy/DivineStrategy.kt index e79be10f..1a27b864 100644 --- a/src/main/kotlin/org/openbase/planetsudo/game/strategy/DivineStrategy.kt +++ b/src/main/kotlin/org/openbase/planetsudo/game/strategy/DivineStrategy.kt @@ -52,34 +52,34 @@ class DivineStrategy(agent: AgentInterface) : StrategyLevelLegacy(agent) { // } then { agent.turnRight(2) } "Follow Wall With Clearance" commander inCase { ( - agent.seeWallAtRight(45, WallDistance.CLOSE) && - agent.seeWallAtRight(135, WallDistance.CLOSE) - ) && !agent.seeWallAtRight(0, WallDistance.VERY_FAR) + agent.seeWallAtRight(45, WallDistance.CLOSE) && + agent.seeWallAtRight(135, WallDistance.CLOSE) + ) && !agent.seeWallAtRight(0, WallDistance.VERY_FAR) } then { agent.goLeft(5) } "Follow Wall Closer" commander inCase { ( - agent.seeWallAtRight(45, WallDistance.VERY_FAR) && - agent.seeWallAtRight(135, WallDistance.VERY_FAR) - ) && !agent.seeWallAtRight(0, WallDistance.VERY_FAR) + agent.seeWallAtRight(45, WallDistance.VERY_FAR) && + agent.seeWallAtRight(135, WallDistance.VERY_FAR) + ) && !agent.seeWallAtRight(0, WallDistance.VERY_FAR) } then { agent.goRight(5) } "Follow Wall" commander inCase { ( - agent.seeWallAtRight(45, WallDistance.FAR) && - agent.seeWallAtRight(135, WallDistance.FAR) - ) && !agent.seeWallAtRight(0, WallDistance.VERY_FAR) + agent.seeWallAtRight(45, WallDistance.FAR) && + agent.seeWallAtRight(135, WallDistance.FAR) + ) && !agent.seeWallAtRight(0, WallDistance.VERY_FAR) } then { agent.go() } "Follow Corner" commander inCase { agent.seeWallAtRight(0, WallDistance.FAR) && ( - agent.seeWallAtRight( - 45, - WallDistance.VERY_FAR, - ) || agent.seeWallAtRight(90, WallDistance.VERY_FAR) - ) + agent.seeWallAtRight( + 45, + WallDistance.VERY_FAR, + ) || agent.seeWallAtRight(90, WallDistance.VERY_FAR) + ) } then { agent.goLeft(20) } "Follow Edge" commander inCase { agent.seeWallAtRight(135, WallDistance.VERY_FAR) && - !agent.seeWallAtRight(110, WallDistance.VERY_FAR) && - !agent.seeWallAtRight(0, WallDistance.FAR) + !agent.seeWallAtRight(110, WallDistance.VERY_FAR) && + !agent.seeWallAtRight(0, WallDistance.FAR) } then { agent.turnRight(20) } "Scan" commander inCase { tower.type == Tower.TowerType.ObservationTower && tower.levelSize == LevelSize.UNKNOWN } then { @@ -105,7 +105,7 @@ class DivineStrategy(agent: AgentInterface) : StrategyLevelLegacy(agent) { // --------------------------------------------> "See Resources" swat SwatTeam.NOT_COMMANDER inCase { agent.seeResource && - (agent.isTonicAtLimit && agent.seeResource(ResourceType.Tonic)).not() + (agent.isTonicAtLimit && agent.seeResource(ResourceType.Tonic)).not() } then { agent.goToResource() } @@ -151,10 +151,10 @@ class DivineStrategy(agent: AgentInterface) : StrategyLevelLegacy(agent) { object : Rule("PickUp") { override fun constraint(): Boolean { return !agent.isCommander && ( - agent.isTouchingResource(ResourceType.DoublePoints) || agent.isTouchingResource( - ResourceType.ExtraMothershipFuel, - ) - ) + agent.isTouchingResource(ResourceType.DoublePoints) || agent.isTouchingResource( + ResourceType.ExtraMothershipFuel, + ) + ) } override fun action() { @@ -182,10 +182,10 @@ class DivineStrategy(agent: AgentInterface) : StrategyLevelLegacy(agent) { object : Rule("PickUp and Place") { override fun constraint(): Boolean { return agent.isCommander && ( - agent.isTouchingResource(ResourceType.DoublePoints) || agent.isTouchingResource( - ResourceType.ExtraMothershipFuel, - ) - ) && !mothership.isMarkerDeployed && !agent.seeMarker() + agent.isTouchingResource(ResourceType.DoublePoints) || agent.isTouchingResource( + ResourceType.ExtraMothershipFuel, + ) + ) && !mothership.isMarkerDeployed && !agent.seeMarker() } override fun action() { diff --git a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Agent.kt b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Agent.kt index 045965cc..f5c54d29 100644 --- a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Agent.kt +++ b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Agent.kt @@ -677,7 +677,7 @@ class Agent( direction.turnTo(position, teamAgent.position) ap.getActionPoint(value * 2) teamAgent.spendFuel(useFuel(value)).also { - // charge leftover back to origin + // charge leftover back to origin leftover -> spendFuel(leftover) } From 292f241e946e8b968110fab494f01add08820e12 Mon Sep 17 00:00:00 2001 From: Divine Threepwood Date: Fri, 2 Oct 2026 01:38:53 +0200 Subject: [PATCH 08/11] add docs to WallDistance --- .../level/levelobjects/WallDistance.kt | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/WallDistance.kt b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/WallDistance.kt index 4006e083..79e7f8aa 100644 --- a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/WallDistance.kt +++ b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/WallDistance.kt @@ -3,13 +3,26 @@ package org.openbase.planetsudo.level.levelobjects import org.openbase.planetsudo.level.levelobjects.Agent.Companion.AGENT_SIZE /** - * Maximum wall-search distance in pixels, measured from the agent's center. - * The values represent 1, 1.5, 2, and 2.5 times [AGENT_SIZE], respectively. + * Maximum distance used by [AgentSpecialInterface.seeWallAtLeft] and + * [AgentSpecialInterface.seeWallAtRight] when checking for a wall. + * + * The distance is measured in pixels from the agent's center in the requested direction. + * It is a search limit, not the measured distance to a wall. Each step represents a multiple + * of [AGENT_SIZE]. + * + * @property pixel Maximum search distance in pixels. */ enum class WallDistance(val pixel: Int) { + /** Search up to one agent size away. */ VERY_CLOSE(pixel = agentFactor(1.0)), + + /** Search up to one and a half agent sizes away. */ CLOSE(pixel = agentFactor(1.5)), + + /** Search up to two agent sizes away. */ FAR(pixel = agentFactor(2.0)), + + /** Search up to two and a half agent sizes away. */ VERY_FAR(pixel = agentFactor(2.5)), } From 20204fd2f24d697830beb802b71a3a9ecf60070e Mon Sep 17 00:00:00 2001 From: Divine Threepwood Date: Fri, 2 Oct 2026 01:40:53 +0200 Subject: [PATCH 09/11] Implement german version of the wall follow api --- .../levelobjects/AgentSpecialInterface.kt | 4 ++-- .../AgentSpecialInterfaceGermanWrapper.kt | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/AgentSpecialInterface.kt b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/AgentSpecialInterface.kt index e3ba34a2..bdca6955 100644 --- a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/AgentSpecialInterface.kt +++ b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/AgentSpecialInterface.kt @@ -15,7 +15,7 @@ interface AgentSpecialInterface : Agent * Abfrage, ob sich eine Wand in der linken Sichtweite befindet. * * @param beta Winkel in Grad (0 = vorne - 90 = links - 180 = hinten) - * @param distance Sicht-Distanz in Pixeln + * @param distance Sicht-Distanz */ fun seeWallAtLeft(beta: Int, distance: WallDistance = WallDistance.FAR): Boolean @@ -30,7 +30,7 @@ interface AgentSpecialInterface : Agent * Abfrage, ob sich eine Wand in der rechten Sichtweite befindet. * * @param beta Winkel in Grad (0 = vorne - 90 = rechts - 180 = hinten) - * @param distance Sicht-Distanz in Pixeln + * @param distance Sicht-Distanz */ fun seeWallAtRight(beta: Int, distance: WallDistance = WallDistance.FAR): Boolean diff --git a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/AgentSpecialInterfaceGermanWrapper.kt b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/AgentSpecialInterfaceGermanWrapper.kt index 92224ed4..bb4c5ee2 100644 --- a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/AgentSpecialInterfaceGermanWrapper.kt +++ b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/AgentSpecialInterfaceGermanWrapper.kt @@ -17,6 +17,24 @@ open class AgentSpecialInterfaceGermanWrapper(private val agent: AgentSpecialInt */ fun istKollisionRechtsErkannt(beta: Int) = agent.isCollisionDetectedAtRight(beta) + /** + * Prüft, ob sich links vom Agenten innerhalb der angegebenen Distanz eine Wand befindet. + * + * @param beta Winkel in Grad (0 = vorne, 90 = links, 180 = hinten) + * @param distanz Maximale Suchdistanz + */ + fun seheWandLinks(beta: Int, distanz: WallDistance = WallDistance.FAR) = + agent.seeWallAtLeft(beta, distanz) + + /** + * Prüft, ob sich rechts vom Agenten innerhalb der angegebenen Distanz eine Wand befindet. + * + * @param beta Winkel in Grad (0 = vorne, 90 = rechts, 180 = hinten) + * @param distanz Maximale Suchdistanz + */ + fun seheWandRechts(beta: Int, distanz: WallDistance = WallDistance.FAR) = + agent.seeWallAtRight(beta, distanz) + /** * Abfrage, ob der Agent aktuell unsichtbar ist. */ From 872af99cdf8581428f6fe474fa966bf322227fd1 Mon Sep 17 00:00:00 2001 From: Divine Threepwood Date: Fri, 2 Oct 2026 01:52:13 +0200 Subject: [PATCH 10/11] code improvements --- build.gradle.kts | 1 - .../planetsudo/level/levelobjects/Agent.kt | 22 +++++++++++-------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 3e1b3bea..ddca6b22 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -13,7 +13,6 @@ plugins { `maven-publish` signing id("io.github.gradle-nexus.publish-plugin") version "2.0.0" - kotlin("plugin.lombok") version "1.9.22" } group = "org.openbase" diff --git a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Agent.kt b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Agent.kt index f5c54d29..9f55cfed 100644 --- a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Agent.kt +++ b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Agent.kt @@ -162,15 +162,19 @@ class Agent( override fun isCollisionDetectedAtRight(beta: Int): Boolean = level.collisionDetected(computeFutureBoundsRight(beta)) - override fun seeWallAtLeft(beta: Int, distance: WallDistance): Boolean { - val dir = Direction2D(direction.angle).also { it.angle -= beta } - return level.hitsWall(position.clone(), dir, distance.pixel) - } + override fun seeWallAtLeft(beta: Int, distance: WallDistance): Boolean = + level.hitsWall( + start = position.clone(), + direction = Direction2D(direction.angle).also { it.angle -= beta }, + maxDistance = distance.pixel, + ) - override fun seeWallAtRight(beta: Int, distance: WallDistance): Boolean { - val dir = Direction2D(direction.angle).also { it.angle += beta } - return level.hitsWall(position.clone(), dir, distance.pixel) - } + override fun seeWallAtRight(beta: Int, distance: WallDistance): Boolean = + level.hitsWall( + start = position.clone(), + direction = Direction2D(direction.angle).also { it.angle += beta }, + maxDistance = distance.pixel, + ) override val isShifting get(): Boolean = shiftTonic > 0.0 @@ -677,7 +681,7 @@ class Agent( direction.turnTo(position, teamAgent.position) ap.getActionPoint(value * 2) teamAgent.spendFuel(useFuel(value)).also { - // charge leftover back to origin + // charge leftover back to origin leftover -> spendFuel(leftover) } From 74ac66966e63cd0bfa1b5c09a8ea5b8fc4fc4782 Mon Sep 17 00:00:00 2001 From: Divine Threepwood Date: Fri, 2 Oct 2026 01:52:41 +0200 Subject: [PATCH 11/11] formatting --- .../kotlin/org/openbase/planetsudo/level/levelobjects/Agent.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Agent.kt b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Agent.kt index 9f55cfed..6c66d06f 100644 --- a/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Agent.kt +++ b/src/main/kotlin/org/openbase/planetsudo/level/levelobjects/Agent.kt @@ -681,7 +681,7 @@ class Agent( direction.turnTo(position, teamAgent.position) ap.getActionPoint(value * 2) teamAgent.spendFuel(useFuel(value)).also { - // charge leftover back to origin + // charge leftover back to origin leftover -> spendFuel(leftover) }