From 8b745c068a9a6a2f6ddb34cf2e1e933479291528 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 5 Apr 2020 00:30:12 +0200 Subject: [PATCH] Fix pathfinder, increase performance --- .../leveleditor/api/editor/RawMap.scala | 6 +-- .../ynerant/leveleditor/game/GameFrame.scala | 39 +++++++++++++----- .../ynerant/leveleditor/game/PathFinder.scala | 40 +++++++++++++++++++ .../ynerant/leveleditor/game/mobs/Mob.scala | 27 +------------ 4 files changed, 75 insertions(+), 37 deletions(-) create mode 100644 src/main/scala/fr/ynerant/leveleditor/game/PathFinder.scala diff --git a/src/main/scala/fr/ynerant/leveleditor/api/editor/RawMap.scala b/src/main/scala/fr/ynerant/leveleditor/api/editor/RawMap.scala index 6264c28..810cdb4 100644 --- a/src/main/scala/fr/ynerant/leveleditor/api/editor/RawMap.scala +++ b/src/main/scala/fr/ynerant/leveleditor/api/editor/RawMap.scala @@ -24,11 +24,11 @@ case class RawMap(var cases: List[RawCase], var width: Int, var height: Int) { def getNeighbours(c: RawCase): Iterable[RawCase] = { var list = Nil: List[RawCase] - list ::= getCase(c.getPosX, c.getPosY + 1) - list ::= getCase(c.getPosX + 1, c.getPosY) list ::= getCase(c.getPosX, c.getPosY - 1) + list ::= getCase(c.getPosX + 1, c.getPosY) + list ::= getCase(c.getPosX, c.getPosY + 1) list ::= getCase(c.getPosX - 1, c.getPosY) - list.filter((_c: RawCase) => _c != null && _c.getCollision.equals(Collision.ANY)) + list.filter((_c: RawCase) => _c != null) } def getCase(x: Int, y: Int): RawCase = { diff --git a/src/main/scala/fr/ynerant/leveleditor/game/GameFrame.scala b/src/main/scala/fr/ynerant/leveleditor/game/GameFrame.scala index 635b2aa..d03789b 100644 --- a/src/main/scala/fr/ynerant/leveleditor/game/GameFrame.scala +++ b/src/main/scala/fr/ynerant/leveleditor/game/GameFrame.scala @@ -1,7 +1,7 @@ package fr.ynerant.leveleditor.game -import java.awt.event.{MouseEvent, MouseListener} import java.awt._ +import java.awt.event.{MouseEvent, MouseListener} import java.util.Random import fr.ynerant.leveleditor.api.editor.sprites.SpriteRegister @@ -21,6 +21,7 @@ class GameFrame(val map: RawMap) extends JFrame("Jeu") { private var reward = 20 private var mobs = ListBuffer[Mob]() private var towers = ListBuffer[Tower]() + private val pathFinder = PathFinder(this) final private var basicTower = null: JRadioButton final private var nullTower = null: JRadioButton final private var autoTower = null: JRadioButton @@ -70,10 +71,16 @@ class GameFrame(val map: RawMap) extends JFrame("Jeu") { pane.add(winLabel) setVisible(true) new Thread(() => { - while ( { - hp > 0 && (round < 4 || mobs.nonEmpty) - }) { - tick() + pathFinder.calculatePath() + + while (hp > 0 && (round < 4 || mobs.nonEmpty)) { + try + tick() + catch { + case e: Throwable => + e.printStackTrace() + } + try Thread.sleep(50L) catch { case e: InterruptedException => @@ -85,10 +92,12 @@ class GameFrame(val map: RawMap) extends JFrame("Jeu") { def getMap: RawMap = map + def getPathFinder: PathFinder = pathFinder + def tick(): Unit = { if (mobs.isEmpty && round < 4) { round += 1 - val nb_mobs = round * (RANDOM.nextInt(16) + 1) + val nb_mobs = round * (RANDOM.nextInt(8) + 1) for (_ <- 1 to nb_mobs) { val mob = Mob.getRandomMob do mob.move(RANDOM.nextInt(getMap.getWidth / 16), RANDOM.nextInt(getMap.getHeight / 16)) while ( { @@ -144,11 +153,23 @@ class GameFrame(val map: RawMap) extends JFrame("Jeu") { else null if (tower == null || tower.getPrice > reward) return val c = getMap.getCase(x, y) - println(x + ", " + y + ", " + tower + ", " + c) if (c == null || !c.getCollision.equals(Collision.ANY)) return c.setCollision(Collision.FULL) - reward -= tower.getPrice - towers += tower + + pathFinder.invalidate() + + val accessible = getMap.getCases.filter(c => !Collision.FULL.equals(c.getCollision)) + if (accessible.exists(c => c.getPosX > 0 && pathFinder.nextPos(c.getPosX, c.getPosY) == null) || !accessible.exists(c => c.getPosX == 0 && !c.getCollision.equals(Collision.FULL))) { + println(accessible.exists(c => c.getPosX > 0 && pathFinder.nextPos(c.getPosX, c.getPosY) == null)) + println(!accessible.exists(c => c.getPosX == 0 && pathFinder.nextPos(c.getPosX, c.getPosY) != null)) + // We ensure that the end of the game is accessible from everywhere, the tower should not block the game + c.setCollision(Collision.ANY) + pathFinder.invalidate() + } + else { + reward -= tower.getPrice + towers += tower + } } override def mouseClicked(event: MouseEvent): Unit = { diff --git a/src/main/scala/fr/ynerant/leveleditor/game/PathFinder.scala b/src/main/scala/fr/ynerant/leveleditor/game/PathFinder.scala new file mode 100644 index 0000000..64d9e60 --- /dev/null +++ b/src/main/scala/fr/ynerant/leveleditor/game/PathFinder.scala @@ -0,0 +1,40 @@ +package fr.ynerant.leveleditor.game + +import java.util + +import fr.ynerant.leveleditor.api.editor.{Collision, RawCase} + +case class PathFinder(game: GameFrame) { + var pred: Map[Int, RawCase] = Map(): Map[Int, RawCase] + + def invalidate(): Unit = calculatePath() + + def calculatePath(): Unit = { + pred = Map() + val queue = new util.ArrayDeque[RawCase] + + for (i <- 0 until game.getMap.getHeight / 16) { + val start = game.getMap.getCase(0, i) + if (!start.getCollision.equals(Collision.FULL)) { + pred += (coords(start) -> null) + queue.add(start) + } + } + + while (!queue.isEmpty) { + val visiting = queue.poll + game.getMap.getNeighbours(visiting).foreach(neighbour => { + if (neighbour != null && !neighbour.collision.equals(Collision.FULL) && !pred.contains(coords(neighbour))) { + pred += (coords(neighbour) -> visiting) + queue.add(neighbour) + } + }) + } + } + + def coords(rawCase: RawCase): Int = rawCase.getPosY * game.getMap.getWidth / 16 + rawCase.getPosX + + def nextPos(x: Int, y: Int): RawCase = { + pred.getOrElse(y * game.getMap.getWidth / 16 + x, null) + } +} diff --git a/src/main/scala/fr/ynerant/leveleditor/game/mobs/Mob.scala b/src/main/scala/fr/ynerant/leveleditor/game/mobs/Mob.scala index cda4f5e..c785274 100644 --- a/src/main/scala/fr/ynerant/leveleditor/game/mobs/Mob.scala +++ b/src/main/scala/fr/ynerant/leveleditor/game/mobs/Mob.scala @@ -1,6 +1,5 @@ package fr.ynerant.leveleditor.game.mobs -import java.util import java.util.Random import fr.ynerant.leveleditor.api.editor.RawCase @@ -76,30 +75,8 @@ abstract class Mob() { return } - var visited = Nil: List[RawCase] - val queue = new util.ArrayDeque[RawCase] - var pred = Map(): Map[RawCase, RawCase] - var last = null: RawCase - queue.add(current) - while (!queue.isEmpty) { - val visiting = queue.poll - visited ::= visiting - game.getMap.getNeighbours(visiting).foreach(neighbour => { - if (neighbour != null && !visited.contains(neighbour)) { - pred += (neighbour -> visiting) - queue.add(neighbour) - if (neighbour.getPosX == 0) { - last = neighbour - queue.clear() - return - } - } - }) - if (last != null) { - while (pred(last) != current) last = pred(last) - move(last.getPosX, last.getPosY) - } - } + val newCase: RawCase = game.getPathFinder.nextPos(getX, getY) + move(newCase.getPosX, newCase.getPosY) } }