From 438e8c703d110de3216c05ecdb5d245c6b76a8b5 Mon Sep 17 00:00:00 2001 From: Agata Bogacz Date: Sat, 19 Oct 2019 16:31:46 +0200 Subject: [PATCH 1/2] Add curved smell propagation --- .../scala/pl/edu/agh/xinuk/Simulation.scala | 6 +- ...SimulationWithCurvedSmellPropagation.scala | 87 +++++++++++++++++++ .../pl/edu/agh/xinuk/config/XinukConfig.scala | 5 ++ .../xinuk/model/CurvedSmellPropagation.scala | 63 ++++++++++++++ 4 files changed, 158 insertions(+), 3 deletions(-) create mode 100644 xinuk-core/src/main/scala/pl/edu/agh/xinuk/SimulationWithCurvedSmellPropagation.scala create mode 100644 xinuk-core/src/main/scala/pl/edu/agh/xinuk/model/CurvedSmellPropagation.scala diff --git a/xinuk-core/src/main/scala/pl/edu/agh/xinuk/Simulation.scala b/xinuk-core/src/main/scala/pl/edu/agh/xinuk/Simulation.scala index d271f75c..2a5641fd 100644 --- a/xinuk-core/src/main/scala/pl/edu/agh/xinuk/Simulation.scala +++ b/xinuk-core/src/main/scala/pl/edu/agh/xinuk/Simulation.scala @@ -40,12 +40,12 @@ class Simulation[ConfigType <: XinukConfig : ValueReader]( private def logHeader: String = s"worker:${metricHeaders.mkString(";")}" implicit val config: ConfigType = { - val forminConfig = rawConfig.getConfig(configPrefix) - logger.info(WorkerActor.MetricsMarker, forminConfig.root().render(ConfigRenderOptions.concise())) + val parsedConfig = rawConfig.getConfig(configPrefix) + logger.info(WorkerActor.MetricsMarker, parsedConfig.root().render(ConfigRenderOptions.concise())) logger.info(WorkerActor.MetricsMarker, logHeader) import net.ceedubs.ficus.Ficus._ - Try(forminConfig.as[ConfigType]("config")) match { + Try(parsedConfig.as[ConfigType]("config")) match { case Success(parsedConfig) => parsedConfig case Failure(parsingError) => diff --git a/xinuk-core/src/main/scala/pl/edu/agh/xinuk/SimulationWithCurvedSmellPropagation.scala b/xinuk-core/src/main/scala/pl/edu/agh/xinuk/SimulationWithCurvedSmellPropagation.scala new file mode 100644 index 00000000..2ff49e39 --- /dev/null +++ b/xinuk-core/src/main/scala/pl/edu/agh/xinuk/SimulationWithCurvedSmellPropagation.scala @@ -0,0 +1,87 @@ +package pl.edu.agh.xinuk + +import java.awt.Color +import java.io.File + +import akka.actor.{ActorRef, ActorSystem} +import akka.cluster.sharding.{ClusterSharding, ClusterShardingSettings} +import com.typesafe.config.{Config, ConfigFactory, ConfigRenderOptions} +import com.typesafe.scalalogging.LazyLogging +import net.ceedubs.ficus.readers.ValueReader +import pl.edu.agh.xinuk.algorithm.MovesController +import pl.edu.agh.xinuk.config.{GuiType, XinukConfig, XinukConfigWithBendFactors} +import pl.edu.agh.xinuk.gui.GuiActor +import pl.edu.agh.xinuk.model.Grid.CellArray +import pl.edu.agh.xinuk.model.parallel.{ConflictResolver, Neighbour, NeighbourPosition} +import pl.edu.agh.xinuk.model._ +import pl.edu.agh.xinuk.simulation.WorkerActor + +import scala.collection.immutable.TreeSet +import scala.util.{Failure, Success, Try} + +class SimulationWithCurvedSmellPropagation[ConfigType <: XinukConfig : ValueReader]( + configPrefix: String, + metricHeaders: Vector[String], + conflictResolver: ConflictResolver[ConfigType], + smellPropagationFunction: XinukConfigWithBendFactors => (CellArray, Int, Int) => Vector[Option[Signal]], + emptyCellFactory: => SmellingCell = EmptyCell.Instance)( + movesControllerFactory: (TreeSet[(Int, Int)], ConfigType) => MovesController, + cellToColor: PartialFunction[GridPart, Color] = PartialFunction.empty + ) extends LazyLogging { + + private val rawConfig: Config = + Try(ConfigFactory.parseFile(new File("xinuk.conf"))) + .filter(_.hasPath(configPrefix)) + .getOrElse { + logger.info("Falling back to reference.conf") + ConfigFactory.empty() + }.withFallback(ConfigFactory.load("cluster.conf")) + + private def logHeader: String = s"worker:${metricHeaders.mkString(";")}" + + implicit val config: ConfigType = { + val parsedConfig = rawConfig.getConfig(configPrefix) + logger.info(WorkerActor.MetricsMarker, parsedConfig.root().render(ConfigRenderOptions.concise())) + logger.info(WorkerActor.MetricsMarker, logHeader) + + import net.ceedubs.ficus.Ficus._ + Try(parsedConfig.as[ConfigType]("config")) match { + case Success(parsedConfig) => + parsedConfig + case Failure(parsingError) => + logger.error("Config parsing error.", parsingError) + System.exit(2) + throw new IllegalArgumentException + } + } + + private val system = ActorSystem(rawConfig.getString("application.name"), rawConfig) + private val workerRegionRef: ActorRef = + ClusterSharding(system).start( + typeName = WorkerActor.Name, + entityProps = WorkerActor.props[ConfigType](workerRegionRef, movesControllerFactory, conflictResolver, smellPropagationFunction(config.asInstanceOf[XinukConfigWithBendFactors]), emptyCellFactory), + settings = ClusterShardingSettings(system), + extractShardId = WorkerActor.extractShardId, + extractEntityId = WorkerActor.extractEntityId + ) + + def start(): Unit = { + if (config.isSupervisor) { + + val workers: Vector[WorkerId] = + (1 to math.pow(config.workersRoot, 2).toInt) + .map(WorkerId)(collection.breakOut) + + workers.foreach { id => + if (config.guiType != GuiType.None) { + system.actorOf(GuiActor.props(workerRegionRef, id, cellToColor)) + } + val neighbours: Vector[Neighbour] = NeighbourPosition.values.flatMap { pos => + pos.neighbourId(id).map(_ => Neighbour(pos)) + }(collection.breakOut) + workerRegionRef ! WorkerActor.NeighboursInitialized(id, neighbours) + } + } + } + +} \ No newline at end of file diff --git a/xinuk-core/src/main/scala/pl/edu/agh/xinuk/config/XinukConfig.scala b/xinuk-core/src/main/scala/pl/edu/agh/xinuk/config/XinukConfig.scala index 303e3344..1d227be2 100644 --- a/xinuk-core/src/main/scala/pl/edu/agh/xinuk/config/XinukConfig.scala +++ b/xinuk-core/src/main/scala/pl/edu/agh/xinuk/config/XinukConfig.scala @@ -16,6 +16,11 @@ trait XinukConfig { def iterationsNumber: Long } +trait XinukConfigWithBendFactors extends XinukConfig { + def crossBendFactor: Double + def straightBendFactor: Double +} + sealed trait GuiType extends NamedEnum object GuiType extends AbstractNamedEnumCompanion[GuiType] { diff --git a/xinuk-core/src/main/scala/pl/edu/agh/xinuk/model/CurvedSmellPropagation.scala b/xinuk-core/src/main/scala/pl/edu/agh/xinuk/model/CurvedSmellPropagation.scala new file mode 100644 index 00000000..9163b88f --- /dev/null +++ b/xinuk-core/src/main/scala/pl/edu/agh/xinuk/model/CurvedSmellPropagation.scala @@ -0,0 +1,63 @@ +package pl.edu.agh.xinuk.model + +import pl.edu.agh.xinuk.config.XinukConfigWithBendFactors +import pl.edu.agh.xinuk.model.Cell.SmellArray +import pl.edu.agh.xinuk.model.Grid.{CellArray, SubcellCoordinates} + +object CurvedSmellPropagation { + def calculateSmellAddends(config: XinukConfigWithBendFactors): (CellArray, Int, Int) => Vector[Option[Signal]] = { + def calculateSmellAddends(cells: CellArray, x: Int, y: Int): Vector[Option[Signal]] = { + @inline def destinationCellSignal(i: Int, j: Int): Option[SmellArray] = { + cells.lift(x + i - 1).flatMap(_.lift(y + j - 1).map(_.smell)) + } + + val crossWeight: Double = config.crossBendFactor + val straightWeight: Double = config.straightBendFactor + + SubcellCoordinates.map { + case (i, j) if i == 1 || j == 1 => + destinationCellSignal(i, j).map(signal => + signal(i)(j) + signal(i + j - 1)(i + j - 1) + signal(i - j + 1)(j - i + 1) + ) + case (i, j) if i == 0 && j == 0 => + destinationCellSignal(i, j).map(signal => { + var signalSum = signal(0)(0) + if (cells(x - 1)(y) == Obstacle) + signalSum += (signal(0)(1) * straightWeight + signal(0)(2) * crossWeight) + if (cells(x)(y - 1) == Obstacle) + signalSum += (signal(1)(0) * straightWeight + signal(2)(0) * crossWeight) + signalSum + }) + case (i, j) if i == 2 && j == 0 => + destinationCellSignal(i, j).map(signal => { + var signalSum = signal(2)(0) + if (cells(x + 1)(y) == Obstacle) + signalSum += (signal(2)(1) * straightWeight + signal(2)(2) * crossWeight) + if (cells(x)(y - 1) == Obstacle) + signalSum += (signal(0)(0) * straightWeight + signal(1)(0) * crossWeight) + signalSum + }) + case (i, j) if i == 0 && j == 2 => + destinationCellSignal(i, j).map(signal => { + var signalSum = signal(0)(2) + if (cells(x - 1)(y) == Obstacle) + signalSum += (signal(0)(0) * straightWeight + signal(0)(1) * crossWeight) + if (cells(x)(y + 1) == Obstacle) + signalSum += (signal(1)(2) * straightWeight + signal(2)(2) * crossWeight) + signalSum + }) + case (i, j) if i == 2 && j == 2 => + destinationCellSignal(i, j).map(signal => { + var signalSum = signal(2)(2) + if (cells(x + 1)(y) == Obstacle) + signalSum += (signal(2)(0) * straightWeight + signal(2)(1) * crossWeight) + if (cells(x)(y + 1) == Obstacle) + signalSum += (signal(0)(2) * straightWeight + signal(1)(2) * crossWeight) + signalSum + }) + + } + } + calculateSmellAddends + } +} From 1f739765544ff524f2654d348640f7c47b06799f Mon Sep 17 00:00:00 2001 From: Agata Bogacz Date: Sun, 24 Nov 2019 18:38:06 +0100 Subject: [PATCH 2/2] Remove unnecessary code of curved smell. Add wind simulation to demonstrate functionallity. --- build.sbt | 3 +- formin/src/main/resources/reference.conf | 2 + .../edu/agh/formin/config/ForminConfig.scala | 4 +- .../scala/pl.edu.agh.formin/GridTest.scala | 18 +-- .../MovesControllerTest.scala | 4 +- .../pl.edu.agh.formin/ParallelTest.scala | 26 ++-- .../pl.edu.agh.formin/WorkerActorTest.scala | 10 +- fortwist/src/main/resources/reference.conf | 2 + .../agh/fortwist/config/FortwistConfig.scala | 4 +- mock/src/main/resources/reference.conf | 3 + .../pl.edu.agh.mock/config/MockConfig.scala | 5 +- torch/src/main/resources/reference.conf | 2 + .../pl.edu.agh.torch/config/TorchConfig.scala | 5 +- wind/src/main/resources/reference.conf | 41 ++++++ .../main/scala/pl/edu/agh/wind/WindMain.scala | 58 +++++++++ .../wind/algorithm/WindMovesController.scala | 49 ++++++++ .../pl/edu/agh/wind/config/WindConfig.scala | 23 ++++ .../edu/agh/wind/model/WindSourceCell.scala | 14 +++ .../model/parallel/WindConflictResolver.scala | 34 +++++ .../edu/agh/wind/simulation/WindMetrics.scala | 21 ++++ .../scala/pl/edu/agh/xinuk/Simulation.scala | 16 ++- ...SimulationWithCurvedSmellPropagation.scala | 87 ------------- .../pl/edu/agh/xinuk/config/XinukConfig.scala | 2 - .../xinuk/model/CurvedSmellPropagation.scala | 63 ---------- .../xinuk/model/DefaultSmellPropagation.scala | 119 ++++++++++++++---- 25 files changed, 400 insertions(+), 215 deletions(-) create mode 100644 wind/src/main/resources/reference.conf create mode 100644 wind/src/main/scala/pl/edu/agh/wind/WindMain.scala create mode 100644 wind/src/main/scala/pl/edu/agh/wind/algorithm/WindMovesController.scala create mode 100644 wind/src/main/scala/pl/edu/agh/wind/config/WindConfig.scala create mode 100644 wind/src/main/scala/pl/edu/agh/wind/model/WindSourceCell.scala create mode 100644 wind/src/main/scala/pl/edu/agh/wind/model/parallel/WindConflictResolver.scala create mode 100644 wind/src/main/scala/pl/edu/agh/wind/simulation/WindMetrics.scala delete mode 100644 xinuk-core/src/main/scala/pl/edu/agh/xinuk/SimulationWithCurvedSmellPropagation.scala delete mode 100644 xinuk-core/src/main/scala/pl/edu/agh/xinuk/model/CurvedSmellPropagation.scala diff --git a/build.sbt b/build.sbt index f435ab3f..973d0b43 100644 --- a/build.sbt +++ b/build.sbt @@ -83,4 +83,5 @@ def modelProject(projectName: String)(mainClassName: String): Project = { lazy val formin = modelProject("formin")("pl.edu.agh.formin.ForminMain") lazy val fortwist = modelProject("fortwist")("pl.edu.agh.fortwist.FortwistMain") lazy val torch = modelProject("torch")("pl.edu.agh.torch.TorchMain") -lazy val mock = modelProject("mock")("pl.edu.agh.mock.MockMain") \ No newline at end of file +lazy val mock = modelProject("mock")("pl.edu.agh.mock.MockMain") +lazy val wind = modelProject("wind")("pl.edu.agh.wind.WindMain") \ No newline at end of file diff --git a/formin/src/main/resources/reference.conf b/formin/src/main/resources/reference.conf index e98c6fce..ddd6cc8a 100644 --- a/formin/src/main/resources/reference.conf +++ b/formin/src/main/resources/reference.conf @@ -42,5 +42,7 @@ formin { iterationsNumber = 10000 isSupervisor = true shardingMod = 144 + crossBendFactor = 0 + straightBendFactor = 0 } } \ No newline at end of file diff --git a/formin/src/main/scala/pl/edu/agh/formin/config/ForminConfig.scala b/formin/src/main/scala/pl/edu/agh/formin/config/ForminConfig.scala index 41c3fa66..dc5961a7 100644 --- a/formin/src/main/scala/pl/edu/agh/formin/config/ForminConfig.scala +++ b/formin/src/main/scala/pl/edu/agh/formin/config/ForminConfig.scala @@ -39,5 +39,7 @@ final case class ForminConfig( workersRoot: Int, iterationsNumber: Long, isSupervisor: Boolean, - shardingMod: Int + shardingMod: Int, + crossBendFactor: Double, + straightBendFactor: Double ) extends XinukConfig diff --git a/formin/src/test/scala/pl.edu.agh.formin/GridTest.scala b/formin/src/test/scala/pl.edu.agh.formin/GridTest.scala index 9db0e9da..ef882fc2 100644 --- a/formin/src/test/scala/pl.edu.agh.formin/GridTest.scala +++ b/formin/src/test/scala/pl.edu.agh.formin/GridTest.scala @@ -28,7 +28,9 @@ class GridTest extends FlatSpecLike with Matchers with BeforeAndAfter { workersRoot = 1, iterationsNumber = 1000, isSupervisor = true, - 1 + 1, + crossBendFactor = 0, + straightBendFactor = 0 ) private var grid: Grid = _ @@ -59,8 +61,8 @@ class GridTest extends FlatSpecLike with Matchers with BeforeAndAfter { it should "propagate signal correctly for one foraminifera cell" in { grid.cells(2)(2) = ForaminiferaAccessible.unapply(EmptyCell.Instance).withForaminifera(config.foraminiferaStartEnergy, 0) - grid.cells(3)(2) = grid.propagatedSignal(DefaultSmellPropagation.calculateSmellAddendsStandard, 3, 2) - grid.cells(3)(1) = grid.propagatedSignal(DefaultSmellPropagation.calculateSmellAddendsStandard, 3, 1) + grid.cells(3)(2) = grid.propagatedSignal(DefaultSmellPropagation.calculateSmellAddendsStandard(config), 3, 2) + grid.cells(3)(1) = grid.propagatedSignal(DefaultSmellPropagation.calculateSmellAddendsStandard(config), 3, 1) grid.cells(2)(2).smell(0)(0).value shouldBe -1 grid.cells(2)(2).smell(0)(1).value shouldBe -1 @@ -73,8 +75,8 @@ class GridTest extends FlatSpecLike with Matchers with BeforeAndAfter { it should "propagate signal correctly for one algae cell" in { grid.cells(2)(2) = AlgaeAccessible.unapply(EmptyCell.Instance).withAlgae(0) - grid.cells(3)(2) = grid.propagatedSignal(DefaultSmellPropagation.calculateSmellAddendsStandard, 3, 2) - grid.cells(3)(1) = grid.propagatedSignal(DefaultSmellPropagation.calculateSmellAddendsStandard, 3, 1) + grid.cells(3)(2) = grid.propagatedSignal(DefaultSmellPropagation.calculateSmellAddendsStandard(config), 3, 2) + grid.cells(3)(1) = grid.propagatedSignal(DefaultSmellPropagation.calculateSmellAddendsStandard(config), 3, 1) grid.cells(2)(2).smell(0)(0).value shouldBe 1 grid.cells(2)(2).smell(0)(1).value shouldBe 1 @@ -88,8 +90,8 @@ class GridTest extends FlatSpecLike with Matchers with BeforeAndAfter { it should "propagate signal correctly between algae and foraminifera cells" in { grid.cells(2)(2) = AlgaeAccessible.unapply(EmptyCell.Instance).withAlgae(0) grid.cells(3)(2) = ForaminiferaAccessible.unapply(EmptyCell.Instance).withForaminifera(config.foraminiferaStartEnergy, 0) - val gridCellWithAlgaeAfterSignalPropagation = grid.propagatedSignal(DefaultSmellPropagation.calculateSmellAddendsStandard, 2, 2) - val gridCellWithForaminiferaAfterSignalPropagation = grid.propagatedSignal(DefaultSmellPropagation.calculateSmellAddendsStandard, 3, 2) + val gridCellWithAlgaeAfterSignalPropagation = grid.propagatedSignal(DefaultSmellPropagation.calculateSmellAddendsStandard(config), 2, 2) + val gridCellWithForaminiferaAfterSignalPropagation = grid.propagatedSignal(DefaultSmellPropagation.calculateSmellAddendsStandard(config), 3, 2) grid.cells(2)(2) = gridCellWithAlgaeAfterSignalPropagation grid.cells(3)(2) = gridCellWithForaminiferaAfterSignalPropagation @@ -110,7 +112,7 @@ class GridTest extends FlatSpecLike with Matchers with BeforeAndAfter { it should "not propagate signal on obstacle cell" in { grid.cells(3)(2) = ForaminiferaAccessible.unapply(EmptyCell.Instance).withForaminifera(config.foraminiferaStartEnergy, 0) - grid.cells(4)(2) = grid.propagatedSignal(DefaultSmellPropagation.calculateSmellAddendsStandard, 4, 2) + grid.cells(4)(2) = grid.propagatedSignal(DefaultSmellPropagation.calculateSmellAddendsStandard(config), 4, 2) grid.cells(3)(2).smell(1)(1).value shouldBe -1 grid.cells(4)(2).smell(0)(1).value shouldBe 0 } diff --git a/formin/src/test/scala/pl.edu.agh.formin/MovesControllerTest.scala b/formin/src/test/scala/pl.edu.agh.formin/MovesControllerTest.scala index 7af2efcc..90d3186c 100644 --- a/formin/src/test/scala/pl.edu.agh.formin/MovesControllerTest.scala +++ b/formin/src/test/scala/pl.edu.agh.formin/MovesControllerTest.scala @@ -31,7 +31,9 @@ class MovesControllerTest extends FlatSpecLike with Matchers with BeforeAndAfter workersRoot = 1, iterationsNumber = 1000, isSupervisor = true, - shardingMod = 1 + shardingMod = 1, + crossBendFactor = 0, + straightBendFactor = 0 ) private var grid: Grid = _ diff --git a/formin/src/test/scala/pl.edu.agh.formin/ParallelTest.scala b/formin/src/test/scala/pl.edu.agh.formin/ParallelTest.scala index 93cf9386..4482a723 100644 --- a/formin/src/test/scala/pl.edu.agh.formin/ParallelTest.scala +++ b/formin/src/test/scala/pl.edu.agh.formin/ParallelTest.scala @@ -36,7 +36,9 @@ class ParallelTest extends FlatSpec with Matchers with Eventually with ScalaFutu workersRoot = 3, iterationsNumber = 3, isSupervisor = true, - shardingMod = 1 + shardingMod = 1, + crossBendFactor = 0, + straightBendFactor = 0 ) trait Fixture { @@ -47,7 +49,7 @@ class ParallelTest extends FlatSpec with Matchers with Eventually with ScalaFutu val workerRegion = TestProbe("workerRegion") val worker = TestActorRef(WorkerActor.props[ForminConfig](workerRegion.ref, (bufferZone, config) => new ForminMovesController(bufferZone)(config), ForminConflictResolver, - DefaultSmellPropagation.calculateSmellAddendsStandard + DefaultSmellPropagation.calculateSmellAddendsStandard(config) )) worker ! WorkerActor.NeighboursInitialized(WorkerId(5), Vector(Neighbour(NeighbourPosition.Bottom), @@ -73,7 +75,7 @@ class ParallelTest extends FlatSpec with Matchers with Eventually with ScalaFutu val workerRegion = TestProbe("workerRegion") val worker = TestActorRef(WorkerActor.props[ForminConfig](workerRegion.ref, (bufferZone, config) => new ForminMovesController(bufferZone)(config), ForminConflictResolver, - DefaultSmellPropagation.calculateSmellAddendsStandard + DefaultSmellPropagation.calculateSmellAddendsStandard(config) )) worker ! WorkerActor.NeighboursInitialized(WorkerId(5), Vector(Neighbour(NeighbourPosition.Bottom), @@ -95,7 +97,7 @@ class ParallelTest extends FlatSpec with Matchers with Eventually with ScalaFutu val workerRegion = TestProbe("workerRegion") val worker = TestActorRef(WorkerActor.props[ForminConfig](workerRegion.ref, (bufferZone, config) => new ForminMovesController(bufferZone)(config), ForminConflictResolver, - DefaultSmellPropagation.calculateSmellAddendsStandard + DefaultSmellPropagation.calculateSmellAddendsStandard(config) )) worker ! WorkerActor.NeighboursInitialized(WorkerId(5), Vector(Neighbour(NeighbourPosition.Top), @@ -117,7 +119,7 @@ class ParallelTest extends FlatSpec with Matchers with Eventually with ScalaFutu val workerRegion = TestProbe("workerRegion") val worker = TestActorRef(WorkerActor.props[ForminConfig](workerRegion.ref, (bufferZone, config) => new ForminMovesController(bufferZone)(config), ForminConflictResolver, - DefaultSmellPropagation.calculateSmellAddendsStandard + DefaultSmellPropagation.calculateSmellAddendsStandard(config) )) worker ! WorkerActor.NeighboursInitialized(WorkerId(5), Vector(Neighbour(NeighbourPosition.Top), @@ -139,7 +141,7 @@ class ParallelTest extends FlatSpec with Matchers with Eventually with ScalaFutu val workerRegion = TestProbe("workerRegion") val worker = TestActorRef(WorkerActor.props[ForminConfig](workerRegion.ref, (bufferZone, config) => new ForminMovesController(bufferZone)(config), ForminConflictResolver, - DefaultSmellPropagation.calculateSmellAddendsStandard + DefaultSmellPropagation.calculateSmellAddendsStandard(config) )) worker ! WorkerActor.NeighboursInitialized(WorkerId(5), Vector(Neighbour(NeighbourPosition.Bottom), @@ -161,7 +163,7 @@ class ParallelTest extends FlatSpec with Matchers with Eventually with ScalaFutu val workerRegion = TestProbe("workerRegion") val worker = TestActorRef(WorkerActor.props[ForminConfig](workerRegion.ref, (bufferZone, config) => new ForminMovesController(bufferZone)(config), ForminConflictResolver, - DefaultSmellPropagation.calculateSmellAddendsStandard + DefaultSmellPropagation.calculateSmellAddendsStandard(config) )) worker ! WorkerActor.NeighboursInitialized(WorkerId(5), Vector(Neighbour(NeighbourPosition.Bottom), @@ -193,7 +195,7 @@ class ParallelTest extends FlatSpec with Matchers with Eventually with ScalaFutu val workerRegion = TestProbe("workerRegion") val worker = TestActorRef(WorkerActor.props[ForminConfig](workerRegion.ref, (bufferZone, config) => new ForminMovesController(bufferZone)(config), ForminConflictResolver, - DefaultSmellPropagation.calculateSmellAddendsStandard + DefaultSmellPropagation.calculateSmellAddendsStandard(config) )) worker ! WorkerActor.NeighboursInitialized(WorkerId(5), Vector(Neighbour(NeighbourPosition.Bottom), @@ -225,7 +227,7 @@ class ParallelTest extends FlatSpec with Matchers with Eventually with ScalaFutu val workerRegion = TestProbe("workerRegion") val worker = TestActorRef(WorkerActor.props[ForminConfig](workerRegion.ref, (bufferZone, config) => new ForminMovesController(bufferZone)(config), ForminConflictResolver, - DefaultSmellPropagation.calculateSmellAddendsStandard + DefaultSmellPropagation.calculateSmellAddendsStandard(config) )) worker ! WorkerActor.NeighboursInitialized(WorkerId(5), Vector(Neighbour(NeighbourPosition.Bottom), @@ -257,7 +259,7 @@ class ParallelTest extends FlatSpec with Matchers with Eventually with ScalaFutu val workerRegion = TestProbe("workerRegion") val worker = TestActorRef(WorkerActor.props[ForminConfig](workerRegion.ref, (bufferZone, config) => new ForminMovesController(bufferZone)(config), ForminConflictResolver, - DefaultSmellPropagation.calculateSmellAddendsStandard + DefaultSmellPropagation.calculateSmellAddendsStandard(config) )) worker ! WorkerActor.NeighboursInitialized(WorkerId(5), Vector(Neighbour(NeighbourPosition.Left), @@ -289,11 +291,11 @@ class ParallelTest extends FlatSpec with Matchers with Eventually with ScalaFutu val workerRegion = TestProbe("workerRegion") val worker1 = TestActorRef(WorkerActor.props[ForminConfig](workerRegion.ref, (bufferZone, config) => new ForminMovesController(bufferZone)(config), ForminConflictResolver, - DefaultSmellPropagation.calculateSmellAddendsStandard + DefaultSmellPropagation.calculateSmellAddendsStandard(config) )) val worker2 = TestActorRef(WorkerActor.props[ForminConfig](workerRegion.ref, (bufferZone, config) => new ForminMovesController(bufferZone)(config), ForminConflictResolver, - DefaultSmellPropagation.calculateSmellAddendsStandard + DefaultSmellPropagation.calculateSmellAddendsStandard(config) )) worker1 ! WorkerActor.NeighboursInitialized(WorkerId(5), diff --git a/formin/src/test/scala/pl.edu.agh.formin/WorkerActorTest.scala b/formin/src/test/scala/pl.edu.agh.formin/WorkerActorTest.scala index 3bb00405..5e91f26e 100644 --- a/formin/src/test/scala/pl.edu.agh.formin/WorkerActorTest.scala +++ b/formin/src/test/scala/pl.edu.agh.formin/WorkerActorTest.scala @@ -33,7 +33,9 @@ class WorkerActorTest extends FlatSpecLike with Matchers with Eventually with Sc workersRoot = 2, iterationsNumber = 3, isSupervisor = true, - shardingMod = 1 + shardingMod = 1, + crossBendFactor = 0, + straightBendFactor = 0 ) trait Fixture { @@ -44,7 +46,7 @@ class WorkerActorTest extends FlatSpecLike with Matchers with Eventually with Sc val workerRegion = TestProbe("workerRegion") val worker = system.actorOf(WorkerActor.props[ForminConfig](workerRegion.ref, (bufferZone, config) => new ForminMovesController(bufferZone)(config), ForminConflictResolver, - DefaultSmellPropagation.calculateSmellAddendsStandard + DefaultSmellPropagation.calculateSmellAddendsStandard(config) )) worker ! WorkerActor.NeighboursInitialized(WorkerId(2), Vector(Neighbour(NeighbourPosition.Bottom))) @@ -60,7 +62,7 @@ class WorkerActorTest extends FlatSpecLike with Matchers with Eventually with Sc val workerRegion = TestProbe("workerRegion") val worker = system.actorOf(WorkerActor.props[ForminConfig](workerRegion.ref, (bufferZone, config) => new ForminMovesController(bufferZone)(config), ForminConflictResolver, - DefaultSmellPropagation.calculateSmellAddendsStandard + DefaultSmellPropagation.calculateSmellAddendsStandard(config) )) worker ! WorkerActor.NeighboursInitialized(WorkerId(2), Vector(Neighbour(NeighbourPosition.Bottom))) @@ -87,7 +89,7 @@ class WorkerActorTest extends FlatSpecLike with Matchers with Eventually with Sc val workerRegion = TestProbe("workerRegion") val worker = system.actorOf(WorkerActor.props[ForminConfig](workerRegion.ref, (bufferZone, config) => new ForminMovesController(bufferZone)(config), ForminConflictResolver, - DefaultSmellPropagation.calculateSmellAddendsStandard + DefaultSmellPropagation.calculateSmellAddendsStandard(config) )) worker ! WorkerActor.NeighboursInitialized(WorkerId(2), Vector()) diff --git a/fortwist/src/main/resources/reference.conf b/fortwist/src/main/resources/reference.conf index 13a81ad9..ed7f4512 100644 --- a/fortwist/src/main/resources/reference.conf +++ b/fortwist/src/main/resources/reference.conf @@ -41,5 +41,7 @@ fortwist { iterationsNumber = 10000 isSupervisor = true shardingMod = 144 + crossBendFactor = 0 + straightBendFactor = 0 } } \ No newline at end of file diff --git a/fortwist/src/main/scala/pl/edu/agh/fortwist/config/FortwistConfig.scala b/fortwist/src/main/scala/pl/edu/agh/fortwist/config/FortwistConfig.scala index e549c43b..4be68a26 100644 --- a/fortwist/src/main/scala/pl/edu/agh/fortwist/config/FortwistConfig.scala +++ b/fortwist/src/main/scala/pl/edu/agh/fortwist/config/FortwistConfig.scala @@ -39,5 +39,7 @@ final case class FortwistConfig( workersRoot: Int, iterationsNumber: Long, isSupervisor: Boolean, - shardingMod: Int + shardingMod: Int, + crossBendFactor: Double, + straightBendFactor: Double ) extends XinukConfig \ No newline at end of file diff --git a/mock/src/main/resources/reference.conf b/mock/src/main/resources/reference.conf index c18e222a..062cef2b 100644 --- a/mock/src/main/resources/reference.conf +++ b/mock/src/main/resources/reference.conf @@ -34,5 +34,8 @@ mock { iterationsNumber = 10000 mockInitialSignal = 1 + + crossBendFactor = 0 + straightBendFactor = 0 } } \ No newline at end of file diff --git a/mock/src/main/scala/pl.edu.agh.mock/config/MockConfig.scala b/mock/src/main/scala/pl.edu.agh.mock/config/MockConfig.scala index 8ca12a93..c44de4b0 100644 --- a/mock/src/main/scala/pl.edu.agh.mock/config/MockConfig.scala +++ b/mock/src/main/scala/pl.edu.agh.mock/config/MockConfig.scala @@ -16,5 +16,8 @@ final case class MockConfig( signalSpeedRatio: Int, iterationsNumber: Long, - mockInitialSignal: Signal + mockInitialSignal: Signal, + + crossBendFactor: Double, + straightBendFactor: Double ) extends XinukConfig \ No newline at end of file diff --git a/torch/src/main/resources/reference.conf b/torch/src/main/resources/reference.conf index 5c6441e7..db0a30a7 100644 --- a/torch/src/main/resources/reference.conf +++ b/torch/src/main/resources/reference.conf @@ -42,5 +42,7 @@ torch { iterationsNumber = 1000 isSupervisor = true shardingMod = 144 + crossBendFactor = 0 + straightBendFactor = 0 } } \ No newline at end of file diff --git a/torch/src/main/scala/pl.edu.agh.torch/config/TorchConfig.scala b/torch/src/main/scala/pl.edu.agh.torch/config/TorchConfig.scala index 1650184d..d1401bb8 100644 --- a/torch/src/main/scala/pl.edu.agh.torch/config/TorchConfig.scala +++ b/torch/src/main/scala/pl.edu.agh.torch/config/TorchConfig.scala @@ -4,7 +4,6 @@ import pl.edu.agh.xinuk.config.{GuiType, XinukConfig} import pl.edu.agh.xinuk.model.Signal final case class TorchConfig( - humanMaxSpeed: Int, fireSpeadingFrequency: Int, signalSpeedRatio: Int, @@ -23,5 +22,7 @@ final case class TorchConfig( workersRoot: Int, iterationsNumber: Long, isSupervisor: Boolean, - shardingMod: Int + shardingMod: Int, + crossBendFactor: Double, + straightBendFactor: Double ) extends XinukConfig \ No newline at end of file diff --git a/wind/src/main/resources/reference.conf b/wind/src/main/resources/reference.conf new file mode 100644 index 00000000..2516322e --- /dev/null +++ b/wind/src/main/resources/reference.conf @@ -0,0 +1,41 @@ +application { + name = wind +} + +clustering { + ip = "0.0.0.0" + port = 2551 + supervisor { + ip = "0.0.0.0" + port = 2551 + } + min-nr-of-members = 1 +} + +xinuk { + classes = [ + "pl.edu.agh.wind.model.WindSourceCell", + "pl.edu.agh.wind.simulation.WindMetrics", + ] +} + +wind { + config { + gridSize = 62 + guiCellSize = 4 + signalSuppressionFactor = 0.5 + signalAttenuationFactor = 1 + workersRoot = 1 + shardingMod = 144 + + guiType = basic + isSupervisor = true + signalSpeedRatio = 1 + iterationsNumber = 10000 + + crossBendFactor = 0.5 + straightBendFactor = 0.5 + + windSourceInitialSignal = 1 + } +} \ No newline at end of file diff --git a/wind/src/main/scala/pl/edu/agh/wind/WindMain.scala b/wind/src/main/scala/pl/edu/agh/wind/WindMain.scala new file mode 100644 index 00000000..c2916eb1 --- /dev/null +++ b/wind/src/main/scala/pl/edu/agh/wind/WindMain.scala @@ -0,0 +1,58 @@ +package pl.edu.agh.wind + +import java.awt.Color + +import com.typesafe.scalalogging.LazyLogging +import pl.edu.agh.wind.algorithm.WindMovesController +import pl.edu.agh.wind.model.WindSourceCell +import pl.edu.agh.wind.model.parallel.WindConflictResolver +import pl.edu.agh.xinuk.Simulation +import pl.edu.agh.xinuk.model.{DefaultSmellPropagation, Obstacle, SmellingCell} + +object WindMain extends LazyLogging { + private val configPrefix = "wind" + private val metricHeaders = Vector() + + def main(args: Array[String]): Unit = { + import pl.edu.agh.xinuk.config.ValueReaders._ + new Simulation( + configPrefix, + metricHeaders, + WindConflictResolver, + DefaultSmellPropagation.calculateSmellAddendsCurved + )( + new WindMovesController(_)(_), + { + case WindSourceCell(_) => Color.WHITE + case Obstacle => Color.GRAY + case cell: SmellingCell => colorSmell(cell: SmellingCell) + case _ => Color.BLACK + } + ).start() + } + + def colorSmell(cell: SmellingCell): Color = { + val smellValue = cell.smell.map(_.map(_.value).max).max.toFloat + + val brightness = Math.pow(smellValue, 0.1).toFloat + if (smellValue < 0.00001) { + val hue = 1f + val saturation = 1f + Color.getHSBColor(hue, saturation, brightness) + } else if (smellValue < 0.001) { + val hue = 0.65f + val saturation = 1f + Color.getHSBColor(hue, saturation, brightness) + } else if (smellValue < 0.1) { + val hue = 0.28f + val saturation = 1f + Color.getHSBColor(hue, saturation, brightness) + } else { + val hue = 0.11f + val saturation = 0.69f + Color.getHSBColor(hue, saturation, brightness) + } + } + +} + diff --git a/wind/src/main/scala/pl/edu/agh/wind/algorithm/WindMovesController.scala b/wind/src/main/scala/pl/edu/agh/wind/algorithm/WindMovesController.scala new file mode 100644 index 00000000..0b1a2e26 --- /dev/null +++ b/wind/src/main/scala/pl/edu/agh/wind/algorithm/WindMovesController.scala @@ -0,0 +1,49 @@ +package pl.edu.agh.wind.algorithm + +import pl.edu.agh.wind.config.WindConfig +import pl.edu.agh.wind.model.WindSourceCell +import pl.edu.agh.wind.simulation.WindMetrics +import pl.edu.agh.xinuk.algorithm.MovesController +import pl.edu.agh.xinuk.model.{Grid, Obstacle} + +import scala.collection.immutable.TreeSet + +final class WindMovesController(bufferZone: TreeSet[(Int, Int)])(implicit config: WindConfig) extends MovesController { + override def initialGrid: (Grid, WindMetrics) = { + val grid = Grid.empty(bufferZone) + val metrics = WindMetrics.empty() + + if (config.gridSize > 4) { + for (i <- 1 to config.gridSize - 2) { + grid.cells(1)(i) = Obstacle + grid.cells(i)(config.gridSize - 2) = Obstacle + + if (i < config.gridSize - 4) { + grid.cells(3)(i) = Obstacle + } + + if (i > 2) { + grid.cells(i)(config.gridSize - 4) = Obstacle + } + } + grid.cells(2)(1) = Obstacle + grid.cells(config.gridSize - 2)(config.gridSize - 3) = Obstacle + + grid.cells(2)(2) = WindSourceCell.create(config.windSourceInitialSignal) + } + + (grid, metrics) + } + + override def makeMoves(iteration: Long, grid: Grid): (Grid, WindMetrics) = { + val newGrid = Grid.empty(bufferZone) + + for { + x <- 0 until config.gridSize + y <- 0 until config.gridSize + } + newGrid.cells(x)(y) = grid.cells(x)(y) + + (newGrid, WindMetrics.empty()) + } +} diff --git a/wind/src/main/scala/pl/edu/agh/wind/config/WindConfig.scala b/wind/src/main/scala/pl/edu/agh/wind/config/WindConfig.scala new file mode 100644 index 00000000..9db859ce --- /dev/null +++ b/wind/src/main/scala/pl/edu/agh/wind/config/WindConfig.scala @@ -0,0 +1,23 @@ +package pl.edu.agh.wind.config + +import pl.edu.agh.xinuk.config.{GuiType, XinukConfig} +import pl.edu.agh.xinuk.model.Signal + +final case class WindConfig( + gridSize: Int, + guiCellSize: Int, + signalSuppressionFactor: Double, + signalAttenuationFactor: Double, + workersRoot: Int, + shardingMod: Int, + + guiType: GuiType, + isSupervisor: Boolean, + signalSpeedRatio: Int, + iterationsNumber: Long, + + crossBendFactor: Double, + straightBendFactor: Double, + + windSourceInitialSignal: Signal + ) extends XinukConfig \ No newline at end of file diff --git a/wind/src/main/scala/pl/edu/agh/wind/model/WindSourceCell.scala b/wind/src/main/scala/pl/edu/agh/wind/model/WindSourceCell.scala new file mode 100644 index 00000000..6d337f33 --- /dev/null +++ b/wind/src/main/scala/pl/edu/agh/wind/model/WindSourceCell.scala @@ -0,0 +1,14 @@ +package pl.edu.agh.wind.model + +import pl.edu.agh.xinuk.model.Cell.SmellArray +import pl.edu.agh.xinuk.model.{Cell, Signal, SmellingCell} + +final case class WindSourceCell(smell: SmellArray) extends SmellingCell { + override type Self = WindSourceCell + + override def withSmell(smell: SmellArray): WindSourceCell = copy(smell = smell) +} + +object WindSourceCell { + def create(initialSignal: Signal): WindSourceCell = WindSourceCell(Array.fill(Cell.Size, Cell.Size)(initialSignal)) +} \ No newline at end of file diff --git a/wind/src/main/scala/pl/edu/agh/wind/model/parallel/WindConflictResolver.scala b/wind/src/main/scala/pl/edu/agh/wind/model/parallel/WindConflictResolver.scala new file mode 100644 index 00000000..9ee680e9 --- /dev/null +++ b/wind/src/main/scala/pl/edu/agh/wind/model/parallel/WindConflictResolver.scala @@ -0,0 +1,34 @@ +package pl.edu.agh.wind.model.parallel + +import pl.edu.agh.wind.config.WindConfig +import pl.edu.agh.wind.model.WindSourceCell +import pl.edu.agh.wind.simulation.WindMetrics +import pl.edu.agh.xinuk.model.parallel.ConflictResolver +import pl.edu.agh.xinuk.model._ + +object WindConflictResolver extends ConflictResolver[WindConfig] { + + import Cell._ + + override def resolveConflict(current: GridPart, incoming: SmellingCell)(implicit config: WindConfig): (GridPart, WindMetrics) = { + (current, incoming) match { + + case (Obstacle, _) => + (Obstacle, WindMetrics.empty()) + + case (EmptyCell(currentSmell), EmptyCell(incomingSmell)) => + (EmptyCell(currentSmell + incomingSmell), WindMetrics.empty()) + + case (WindSourceCell(currentSmell), EmptyCell(incomingSmell)) => + (WindSourceCell(currentSmell + incomingSmell), WindMetrics.empty()) + + case (EmptyCell(currentSmell), WindSourceCell(incomingSmell)) => + (WindSourceCell(currentSmell + incomingSmell), WindMetrics.empty()) + + case (WindSourceCell(currentSmell), WindSourceCell(incomingSmell)) => + (WindSourceCell(currentSmell + incomingSmell), WindMetrics.empty()) + + case (x, y) => throw new UnsupportedOperationException(s"Unresolved conflict: $x with $y") + } + } +} diff --git a/wind/src/main/scala/pl/edu/agh/wind/simulation/WindMetrics.scala b/wind/src/main/scala/pl/edu/agh/wind/simulation/WindMetrics.scala new file mode 100644 index 00000000..af08ea21 --- /dev/null +++ b/wind/src/main/scala/pl/edu/agh/wind/simulation/WindMetrics.scala @@ -0,0 +1,21 @@ +package pl.edu.agh.wind.simulation + +import pl.edu.agh.xinuk.simulation.Metrics + +final case class WindMetrics() extends Metrics { + override def log: String = { + s"" + } + + override def series: Vector[(String, Double)] = Vector() + + override def +(other: Metrics): WindMetrics = { + this + } +} + +object WindMetrics { + private val EMPTY = WindMetrics() + + def empty(): WindMetrics = EMPTY +} diff --git a/xinuk-core/src/main/scala/pl/edu/agh/xinuk/Simulation.scala b/xinuk-core/src/main/scala/pl/edu/agh/xinuk/Simulation.scala index 2a5641fd..c5b0ac52 100644 --- a/xinuk-core/src/main/scala/pl/edu/agh/xinuk/Simulation.scala +++ b/xinuk-core/src/main/scala/pl/edu/agh/xinuk/Simulation.scala @@ -23,7 +23,7 @@ class Simulation[ConfigType <: XinukConfig : ValueReader]( configPrefix: String, metricHeaders: Vector[String], conflictResolver: ConflictResolver[ConfigType], - smellPropagationFunction: (CellArray, Int, Int) => Vector[Option[Signal]], + smellPropagationFunction: XinukConfig => (CellArray, Int, Int) => Vector[Option[Signal]], emptyCellFactory: => SmellingCell = EmptyCell.Instance)( movesControllerFactory: (TreeSet[(Int, Int)], ConfigType) => MovesController, cellToColor: PartialFunction[GridPart, Color] = PartialFunction.empty @@ -40,12 +40,12 @@ class Simulation[ConfigType <: XinukConfig : ValueReader]( private def logHeader: String = s"worker:${metricHeaders.mkString(";")}" implicit val config: ConfigType = { - val parsedConfig = rawConfig.getConfig(configPrefix) - logger.info(WorkerActor.MetricsMarker, parsedConfig.root().render(ConfigRenderOptions.concise())) + val conf = rawConfig.getConfig(configPrefix) + logger.info(WorkerActor.MetricsMarker, conf.root().render(ConfigRenderOptions.concise())) logger.info(WorkerActor.MetricsMarker, logHeader) import net.ceedubs.ficus.Ficus._ - Try(parsedConfig.as[ConfigType]("config")) match { + Try(conf.as[ConfigType]("config")) match { case Success(parsedConfig) => parsedConfig case Failure(parsingError) => @@ -59,7 +59,13 @@ class Simulation[ConfigType <: XinukConfig : ValueReader]( private val workerRegionRef: ActorRef = ClusterSharding(system).start( typeName = WorkerActor.Name, - entityProps = WorkerActor.props[ConfigType](workerRegionRef, movesControllerFactory, conflictResolver, smellPropagationFunction, emptyCellFactory), + entityProps = WorkerActor.props[ConfigType]( + workerRegionRef, + movesControllerFactory, + conflictResolver, + smellPropagationFunction(config.asInstanceOf[XinukConfig]), + emptyCellFactory + ), settings = ClusterShardingSettings(system), extractShardId = WorkerActor.extractShardId, extractEntityId = WorkerActor.extractEntityId diff --git a/xinuk-core/src/main/scala/pl/edu/agh/xinuk/SimulationWithCurvedSmellPropagation.scala b/xinuk-core/src/main/scala/pl/edu/agh/xinuk/SimulationWithCurvedSmellPropagation.scala deleted file mode 100644 index 2ff49e39..00000000 --- a/xinuk-core/src/main/scala/pl/edu/agh/xinuk/SimulationWithCurvedSmellPropagation.scala +++ /dev/null @@ -1,87 +0,0 @@ -package pl.edu.agh.xinuk - -import java.awt.Color -import java.io.File - -import akka.actor.{ActorRef, ActorSystem} -import akka.cluster.sharding.{ClusterSharding, ClusterShardingSettings} -import com.typesafe.config.{Config, ConfigFactory, ConfigRenderOptions} -import com.typesafe.scalalogging.LazyLogging -import net.ceedubs.ficus.readers.ValueReader -import pl.edu.agh.xinuk.algorithm.MovesController -import pl.edu.agh.xinuk.config.{GuiType, XinukConfig, XinukConfigWithBendFactors} -import pl.edu.agh.xinuk.gui.GuiActor -import pl.edu.agh.xinuk.model.Grid.CellArray -import pl.edu.agh.xinuk.model.parallel.{ConflictResolver, Neighbour, NeighbourPosition} -import pl.edu.agh.xinuk.model._ -import pl.edu.agh.xinuk.simulation.WorkerActor - -import scala.collection.immutable.TreeSet -import scala.util.{Failure, Success, Try} - -class SimulationWithCurvedSmellPropagation[ConfigType <: XinukConfig : ValueReader]( - configPrefix: String, - metricHeaders: Vector[String], - conflictResolver: ConflictResolver[ConfigType], - smellPropagationFunction: XinukConfigWithBendFactors => (CellArray, Int, Int) => Vector[Option[Signal]], - emptyCellFactory: => SmellingCell = EmptyCell.Instance)( - movesControllerFactory: (TreeSet[(Int, Int)], ConfigType) => MovesController, - cellToColor: PartialFunction[GridPart, Color] = PartialFunction.empty - ) extends LazyLogging { - - private val rawConfig: Config = - Try(ConfigFactory.parseFile(new File("xinuk.conf"))) - .filter(_.hasPath(configPrefix)) - .getOrElse { - logger.info("Falling back to reference.conf") - ConfigFactory.empty() - }.withFallback(ConfigFactory.load("cluster.conf")) - - private def logHeader: String = s"worker:${metricHeaders.mkString(";")}" - - implicit val config: ConfigType = { - val parsedConfig = rawConfig.getConfig(configPrefix) - logger.info(WorkerActor.MetricsMarker, parsedConfig.root().render(ConfigRenderOptions.concise())) - logger.info(WorkerActor.MetricsMarker, logHeader) - - import net.ceedubs.ficus.Ficus._ - Try(parsedConfig.as[ConfigType]("config")) match { - case Success(parsedConfig) => - parsedConfig - case Failure(parsingError) => - logger.error("Config parsing error.", parsingError) - System.exit(2) - throw new IllegalArgumentException - } - } - - private val system = ActorSystem(rawConfig.getString("application.name"), rawConfig) - private val workerRegionRef: ActorRef = - ClusterSharding(system).start( - typeName = WorkerActor.Name, - entityProps = WorkerActor.props[ConfigType](workerRegionRef, movesControllerFactory, conflictResolver, smellPropagationFunction(config.asInstanceOf[XinukConfigWithBendFactors]), emptyCellFactory), - settings = ClusterShardingSettings(system), - extractShardId = WorkerActor.extractShardId, - extractEntityId = WorkerActor.extractEntityId - ) - - def start(): Unit = { - if (config.isSupervisor) { - - val workers: Vector[WorkerId] = - (1 to math.pow(config.workersRoot, 2).toInt) - .map(WorkerId)(collection.breakOut) - - workers.foreach { id => - if (config.guiType != GuiType.None) { - system.actorOf(GuiActor.props(workerRegionRef, id, cellToColor)) - } - val neighbours: Vector[Neighbour] = NeighbourPosition.values.flatMap { pos => - pos.neighbourId(id).map(_ => Neighbour(pos)) - }(collection.breakOut) - workerRegionRef ! WorkerActor.NeighboursInitialized(id, neighbours) - } - } - } - -} \ No newline at end of file diff --git a/xinuk-core/src/main/scala/pl/edu/agh/xinuk/config/XinukConfig.scala b/xinuk-core/src/main/scala/pl/edu/agh/xinuk/config/XinukConfig.scala index 1d227be2..05628862 100644 --- a/xinuk-core/src/main/scala/pl/edu/agh/xinuk/config/XinukConfig.scala +++ b/xinuk-core/src/main/scala/pl/edu/agh/xinuk/config/XinukConfig.scala @@ -14,9 +14,7 @@ trait XinukConfig { def isSupervisor: Boolean def signalSpeedRatio: Int def iterationsNumber: Long -} -trait XinukConfigWithBendFactors extends XinukConfig { def crossBendFactor: Double def straightBendFactor: Double } diff --git a/xinuk-core/src/main/scala/pl/edu/agh/xinuk/model/CurvedSmellPropagation.scala b/xinuk-core/src/main/scala/pl/edu/agh/xinuk/model/CurvedSmellPropagation.scala deleted file mode 100644 index 9163b88f..00000000 --- a/xinuk-core/src/main/scala/pl/edu/agh/xinuk/model/CurvedSmellPropagation.scala +++ /dev/null @@ -1,63 +0,0 @@ -package pl.edu.agh.xinuk.model - -import pl.edu.agh.xinuk.config.XinukConfigWithBendFactors -import pl.edu.agh.xinuk.model.Cell.SmellArray -import pl.edu.agh.xinuk.model.Grid.{CellArray, SubcellCoordinates} - -object CurvedSmellPropagation { - def calculateSmellAddends(config: XinukConfigWithBendFactors): (CellArray, Int, Int) => Vector[Option[Signal]] = { - def calculateSmellAddends(cells: CellArray, x: Int, y: Int): Vector[Option[Signal]] = { - @inline def destinationCellSignal(i: Int, j: Int): Option[SmellArray] = { - cells.lift(x + i - 1).flatMap(_.lift(y + j - 1).map(_.smell)) - } - - val crossWeight: Double = config.crossBendFactor - val straightWeight: Double = config.straightBendFactor - - SubcellCoordinates.map { - case (i, j) if i == 1 || j == 1 => - destinationCellSignal(i, j).map(signal => - signal(i)(j) + signal(i + j - 1)(i + j - 1) + signal(i - j + 1)(j - i + 1) - ) - case (i, j) if i == 0 && j == 0 => - destinationCellSignal(i, j).map(signal => { - var signalSum = signal(0)(0) - if (cells(x - 1)(y) == Obstacle) - signalSum += (signal(0)(1) * straightWeight + signal(0)(2) * crossWeight) - if (cells(x)(y - 1) == Obstacle) - signalSum += (signal(1)(0) * straightWeight + signal(2)(0) * crossWeight) - signalSum - }) - case (i, j) if i == 2 && j == 0 => - destinationCellSignal(i, j).map(signal => { - var signalSum = signal(2)(0) - if (cells(x + 1)(y) == Obstacle) - signalSum += (signal(2)(1) * straightWeight + signal(2)(2) * crossWeight) - if (cells(x)(y - 1) == Obstacle) - signalSum += (signal(0)(0) * straightWeight + signal(1)(0) * crossWeight) - signalSum - }) - case (i, j) if i == 0 && j == 2 => - destinationCellSignal(i, j).map(signal => { - var signalSum = signal(0)(2) - if (cells(x - 1)(y) == Obstacle) - signalSum += (signal(0)(0) * straightWeight + signal(0)(1) * crossWeight) - if (cells(x)(y + 1) == Obstacle) - signalSum += (signal(1)(2) * straightWeight + signal(2)(2) * crossWeight) - signalSum - }) - case (i, j) if i == 2 && j == 2 => - destinationCellSignal(i, j).map(signal => { - var signalSum = signal(2)(2) - if (cells(x + 1)(y) == Obstacle) - signalSum += (signal(2)(0) * straightWeight + signal(2)(1) * crossWeight) - if (cells(x)(y + 1) == Obstacle) - signalSum += (signal(0)(2) * straightWeight + signal(1)(2) * crossWeight) - signalSum - }) - - } - } - calculateSmellAddends - } -} diff --git a/xinuk-core/src/main/scala/pl/edu/agh/xinuk/model/DefaultSmellPropagation.scala b/xinuk-core/src/main/scala/pl/edu/agh/xinuk/model/DefaultSmellPropagation.scala index 4d42a1a5..1e700287 100644 --- a/xinuk-core/src/main/scala/pl/edu/agh/xinuk/model/DefaultSmellPropagation.scala +++ b/xinuk-core/src/main/scala/pl/edu/agh/xinuk/model/DefaultSmellPropagation.scala @@ -1,44 +1,109 @@ package pl.edu.agh.xinuk.model +import pl.edu.agh.xinuk.config.XinukConfig import pl.edu.agh.xinuk.model.Cell.SmellArray import pl.edu.agh.xinuk.model.Grid.{CellArray, SubcellCoordinates} object DefaultSmellPropagation { - def calculateSmellAddendsStandard(cells: CellArray, x: Int, y: Int): Vector[Option[Signal]] = { - @inline def destinationCellSignal(i: Int, j: Int): Option[SmellArray] = { - cells.lift(x + i - 1).flatMap(_.lift(y + j - 1).map(_.smell)) - } + def calculateSmellAddendsStandard(config: XinukConfig): (CellArray, Int, Int) => Vector[Option[Signal]] = { + def calculateSmellAddendsStandard(cells: CellArray, x: Int, y: Int): Vector[Option[Signal]] = { + @inline def destinationCellSignal(i: Int, j: Int): Option[SmellArray] = { + cells.lift(x + i - 1).flatMap(_.lift(y + j - 1).map(_.smell)) + } - SubcellCoordinates.map { - case (i, j) if i == 1 || j == 1 => - destinationCellSignal(i, j).map(signal => - signal(i)(j) + signal(i + j - 1)(i + j - 1) + signal(i - j + 1)(j - i + 1) - ) - case (i, j) => - destinationCellSignal(i, j).map(_.apply(i)(j)) + SubcellCoordinates.map { + case (i, j) if i == 1 || j == 1 => + destinationCellSignal(i, j).map(signal => + signal(i)(j) + signal(i + j - 1)(i + j - 1) + signal(i - j + 1)(j - i + 1) + ) + case (i, j) => + destinationCellSignal(i, j).map(_.apply(i)(j)) + } } + calculateSmellAddendsStandard } - def calculateSmellAddendsCircular(cells: CellArray, x: Int, y: Int): Vector[Option[Signal]] = { - def sideToSide = 1.0 / 3 - def sideToCorner = 1.0 / Math.sqrt(10) - def cornerToSide = 1.0 / Math.sqrt(13) - def cornerToCorner = 1.0 / (3 * Math.sqrt(2)) + def calculateSmellAddendsCircular(config: XinukConfig): (CellArray, Int, Int) => Vector[Option[Signal]] = { + def calculateSmellAddendsCircular(cells: CellArray, x: Int, y: Int): Vector[Option[Signal]] = { + def sideToSide = 1.0 / 3 + + def sideToCorner = 1.0 / Math.sqrt(10) + + def cornerToSide = 1.0 / Math.sqrt(13) + + def cornerToCorner = 1.0 / (3 * Math.sqrt(2)) - @inline def destinationCellSignal(i: Int, j: Int): Option[SmellArray] = { - cells.lift(x + i - 1).flatMap(_.lift(y + j - 1).map(_.smell)) + @inline def destinationCellSignal(i: Int, j: Int): Option[SmellArray] = { + cells.lift(x + i - 1).flatMap(_.lift(y + j - 1).map(_.smell)) + } + + SubcellCoordinates.map { + case (i, j) if i == 1 || j == 1 => + destinationCellSignal(i, j).map(signal => + signal(i)(j) * sideToSide + (signal(i + j - 1)(i + j - 1) + signal(i - j + 1)(j - i + 1)) * cornerToSide + ) + case (i, j) => + destinationCellSignal(i, j).map(signal => + signal(i)(j) * cornerToCorner + (signal(i)(1) + signal(1)(j)) * sideToCorner + ) + } } + calculateSmellAddendsCircular + } + + def calculateSmellAddendsCurved(config: XinukConfig): (CellArray, Int, Int) => Vector[Option[Signal]] = { + def calculateSmellAddendsCurved(cells: CellArray, x: Int, y: Int): Vector[Option[Signal]] = { + @inline def destinationCellSignal(i: Int, j: Int): Option[SmellArray] = { + cells.lift(x + i - 1).flatMap(_.lift(y + j - 1).map(_.smell)) + } + + val crossWeight: Double = config.crossBendFactor + val straightWeight: Double = config.straightBendFactor - SubcellCoordinates.map { - case (i, j) if i == 1 || j == 1 => - destinationCellSignal(i, j).map(signal => - signal(i)(j) * sideToSide + (signal(i + j - 1)(i + j - 1) + signal(i - j + 1)(j - i + 1)) * cornerToSide - ) - case (i, j) => - destinationCellSignal(i, j).map(signal => - signal(i)(j) * cornerToCorner + (signal(i)(1) + signal(1)(j)) * sideToCorner - ) + SubcellCoordinates.map { + case (i, j) if i == 1 || j == 1 => + destinationCellSignal(i, j).map(signal => + signal(i)(j) + signal(i + j - 1)(i + j - 1) + signal(i - j + 1)(j - i + 1) + ) + case (i, j) if i == 0 && j == 0 => + destinationCellSignal(i, j).map(signal => { + var signalSum = signal(0)(0) + if (cells(x - 1)(y) == Obstacle) + signalSum += (signal(0)(1) * straightWeight + signal(0)(2) * crossWeight) + if (cells(x)(y - 1) == Obstacle) + signalSum += (signal(1)(0) * straightWeight + signal(2)(0) * crossWeight) + signalSum + }) + case (i, j) if i == 2 && j == 0 => + destinationCellSignal(i, j).map(signal => { + var signalSum = signal(2)(0) + if (cells(x + 1)(y) == Obstacle) + signalSum += (signal(2)(1) * straightWeight + signal(2)(2) * crossWeight) + if (cells(x)(y - 1) == Obstacle) + signalSum += (signal(0)(0) * straightWeight + signal(1)(0) * crossWeight) + signalSum + }) + case (i, j) if i == 0 && j == 2 => + destinationCellSignal(i, j).map(signal => { + var signalSum = signal(0)(2) + if (cells(x - 1)(y) == Obstacle) + signalSum += (signal(0)(0) * straightWeight + signal(0)(1) * crossWeight) + if (cells(x)(y + 1) == Obstacle) + signalSum += (signal(1)(2) * straightWeight + signal(2)(2) * crossWeight) + signalSum + }) + case (i, j) if i == 2 && j == 2 => + destinationCellSignal(i, j).map(signal => { + var signalSum = signal(2)(2) + if (cells(x + 1)(y) == Obstacle) + signalSum += (signal(2)(0) * straightWeight + signal(2)(1) * crossWeight) + if (cells(x)(y + 1) == Obstacle) + signalSum += (signal(0)(2) * straightWeight + signal(1)(2) * crossWeight) + signalSum + }) + } } + calculateSmellAddendsCurved } }