From 4170b2f84acdee3479f49b37d8dea985813ad042 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Zieli=C5=84ski?= Date: Fri, 5 Apr 2019 22:55:47 +0200 Subject: [PATCH 01/10] Add second smell and implement it in formin simulation --- formin/src/main/resources/reference.conf | 8 ++-- .../scala/pl/edu/agh/formin/ForminMain.scala | 2 +- .../algorithm/ForminMovesController.scala | 7 ++- .../edu/agh/formin/config/ForminConfig.scala | 8 ++-- .../scala/pl/edu/agh/formin/model/Algae.scala | 5 +- .../edu/agh/formin/model/Foraminifera.scala | 9 ++-- .../parallel/ForminConflictResolver.scala | 12 ++--- .../scala/pl.edu.agh.formin/GridTest.scala | 6 ++- .../MovesControllerTest.scala | 30 ++++++------ .../pl.edu.agh.formin/ParallelTest.scala | 6 ++- .../pl.edu.agh.formin/WorkerActorTest.scala | 6 ++- .../algorithm/TorchMovesController.scala | 3 ++ .../scala/pl.edu.agh.torch/model/Human.scala | 2 +- .../scala/pl/edu/agh/xinuk/Simulation.scala | 2 +- .../xinuk/model/DefaultSmellPropagation.scala | 4 +- .../scala/pl/edu/agh/xinuk/model/Grid.scala | 47 +++++++++++++++---- .../agh/xinuk/simulation/WorkerActor.scala | 4 +- 17 files changed, 104 insertions(+), 57 deletions(-) diff --git a/formin/src/main/resources/reference.conf b/formin/src/main/resources/reference.conf index e98c6fce..3190372b 100644 --- a/formin/src/main/resources/reference.conf +++ b/formin/src/main/resources/reference.conf @@ -31,11 +31,13 @@ formin { signalSpeedRatio = 2 signalSuppressionFactor = 0.5 signalAttenuationFactor = 1 - gridSize = 100 + gridSize = 50 spawnChance = 0.1 foraminiferaSpawnChance = 0.3 - foraminiferaInitialSignal = -1 - algaeInitialSignal = 1 + foraminiferaInitialSignal = [-1, 0] + foraminiferaPursuedSignalIndex = 0 + algaeInitialSignal = [1, 0] + algaePursuedSignalIndex = 0 guiType = basic guiCellSize = 4 workersRoot = 2 diff --git a/formin/src/main/scala/pl/edu/agh/formin/ForminMain.scala b/formin/src/main/scala/pl/edu/agh/formin/ForminMain.scala index 2f444475..e1e5d70a 100644 --- a/formin/src/main/scala/pl/edu/agh/formin/ForminMain.scala +++ b/formin/src/main/scala/pl/edu/agh/formin/ForminMain.scala @@ -26,7 +26,7 @@ object ForminMain extends LazyLogging { private def cellToColor(cell: SmellingCell): Color = { cell match { case AlgaeCell(_, _) => new Color(0, 128, 0) - case ForaminiferaCell(_, _, _) => new Color(139, 69, 19) + case ForaminiferaCell(_, _, _, _) => new Color(139, 69, 19) case _ => Color.WHITE } } diff --git a/formin/src/main/scala/pl/edu/agh/formin/algorithm/ForminMovesController.scala b/formin/src/main/scala/pl/edu/agh/formin/algorithm/ForminMovesController.scala index 006c641d..afef4adf 100644 --- a/formin/src/main/scala/pl/edu/agh/formin/algorithm/ForminMovesController.scala +++ b/formin/src/main/scala/pl/edu/agh/formin/algorithm/ForminMovesController.scala @@ -49,6 +49,9 @@ final class ForminMovesController(bufferZone: TreeSet[(Int, Int)])(implicit conf Grid.SubcellCoordinates .map { case (i, j) => cell.smell(i)(j) } .zipWithIndex + .map { + case (signalVector, index) => (signalVector(cell.pursuedSignalIndex), index) + } .sorted(implicitly[Ordering[(Signal, Int)]].reverse) .iterator .map { case (_, idx) => @@ -172,10 +175,10 @@ final class ForminMovesController(bufferZone: TreeSet[(Int, Int)])(implicit conf y <- 0 until config.gridSize } { this.grid.cells(x)(y) match { - case ForaminiferaCell(energy, _, _) => + case ForaminiferaCell(energy, _, _, _) => foraminiferaTotalEnergy += energy.value foraminiferaCount += 1 - case BufferCell(ForaminiferaCell(energy, _, _)) => + case BufferCell(ForaminiferaCell(energy, _, _, _)) => foraminiferaTotalEnergy += energy.value foraminiferaCount += 1 case AlgaeCell(_, _) | BufferCell(AlgaeCell(_, _)) => 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..5f890b57 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 @@ -1,7 +1,7 @@ package pl.edu.agh.formin.config import pl.edu.agh.xinuk.config.{GuiType, XinukConfig} -import pl.edu.agh.xinuk.model.{Energy, Signal} +import pl.edu.agh.xinuk.model.{Energy, Signal, SignalVector} /* FSE - foraminifera start energy; FSE ∈ [0,1] && FSE ∈ R. @@ -32,8 +32,10 @@ final case class ForminConfig( gridSize: Int, spawnChance: Double, foraminiferaSpawnChance: Double, - foraminiferaInitialSignal: Signal, - algaeInitialSignal: Signal, + foraminiferaInitialSignal: List[Signal], + foraminiferaPursuedSignalIndex: Int, + algaeInitialSignal: List[Signal], + algaePursuedSignalIndex: Int, guiType: GuiType, guiCellSize: Int, workersRoot: Int, diff --git a/formin/src/main/scala/pl/edu/agh/formin/model/Algae.scala b/formin/src/main/scala/pl/edu/agh/formin/model/Algae.scala index eb4a34f7..39bdc7f4 100644 --- a/formin/src/main/scala/pl/edu/agh/formin/model/Algae.scala +++ b/formin/src/main/scala/pl/edu/agh/formin/model/Algae.scala @@ -3,6 +3,7 @@ package pl.edu.agh.formin.model import pl.edu.agh.formin.config.ForminConfig import pl.edu.agh.xinuk.model.Cell.SmellArray import pl.edu.agh.xinuk.model.{BufferCell, EmptyCell, GridPart, SmellingCell} +import pl.edu.agh.xinuk.model.SignalVector.SignalVectorOps final case class AlgaeCell(smell: SmellArray, lifespan: Long) extends SmellingCell { override type Self = AlgaeCell @@ -18,12 +19,12 @@ object AlgaeAccessible { def unapply(arg: EmptyCell)(implicit config: ForminConfig): AlgaeAccessible[AlgaeCell] = new AlgaeAccessible[AlgaeCell] { - override def withAlgae(lifespan: Long): AlgaeCell = AlgaeCell(arg.smellWith(config.algaeInitialSignal), lifespan) + override def withAlgae(lifespan: Long): AlgaeCell = AlgaeCell(arg.smellWith(config.algaeInitialSignal.toSignalVector), lifespan) } def unapply(arg: BufferCell)(implicit config: ForminConfig): AlgaeAccessible[BufferCell] = new AlgaeAccessible[BufferCell] { - override def withAlgae(lifespan: Long): BufferCell = BufferCell(AlgaeCell(arg.smellWith(config.algaeInitialSignal), lifespan)) + override def withAlgae(lifespan: Long): BufferCell = BufferCell(AlgaeCell(arg.smellWith(config.algaeInitialSignal.toSignalVector), lifespan)) } def unapply(arg: GridPart)(implicit config: ForminConfig): Option[AlgaeAccessible[GridPart]] = arg match { diff --git a/formin/src/main/scala/pl/edu/agh/formin/model/Foraminifera.scala b/formin/src/main/scala/pl/edu/agh/formin/model/Foraminifera.scala index e086ad1b..586a7f5f 100644 --- a/formin/src/main/scala/pl/edu/agh/formin/model/Foraminifera.scala +++ b/formin/src/main/scala/pl/edu/agh/formin/model/Foraminifera.scala @@ -3,8 +3,9 @@ package pl.edu.agh.formin.model import pl.edu.agh.formin.config.ForminConfig import pl.edu.agh.xinuk.model.Cell.SmellArray import pl.edu.agh.xinuk.model._ +import pl.edu.agh.xinuk.model.SignalVector.SignalVectorOps -final case class ForaminiferaCell(energy: Energy, smell: SmellArray, lifespan: Long) extends SmellingCell { +final case class ForaminiferaCell(energy: Energy, smell: SmellArray, lifespan: Long, pursuedSignalIndex: Int) extends SmellingCell { override type Self = ForaminiferaCell override def withSmell(smell: SmellArray): ForaminiferaCell = copy(smell = smell) @@ -18,17 +19,17 @@ object ForaminiferaAccessible { def unapply(arg: AlgaeCell)(implicit config: ForminConfig): ForaminiferaAccessible[ForaminiferaCell] = new ForaminiferaAccessible[ForaminiferaCell] { - override def withForaminifera(energy: Energy, lifespan: Long): ForaminiferaCell = ForaminiferaCell(energy + config.algaeEnergeticCapacity, arg.smellWith(config.foraminiferaInitialSignal), lifespan) + override def withForaminifera(energy: Energy, lifespan: Long): ForaminiferaCell = ForaminiferaCell(energy + config.algaeEnergeticCapacity, arg.smellWith(config.foraminiferaInitialSignal.toSignalVector), lifespan, config.foraminiferaPursuedSignalIndex) } def unapply(arg: EmptyCell)(implicit config: ForminConfig): ForaminiferaAccessible[ForaminiferaCell] = new ForaminiferaAccessible[ForaminiferaCell] { - override def withForaminifera(energy: Energy, lifespan: Long): ForaminiferaCell = ForaminiferaCell(energy, arg.smellWith(config.foraminiferaInitialSignal), lifespan) + override def withForaminifera(energy: Energy, lifespan: Long): ForaminiferaCell = ForaminiferaCell(energy, arg.smellWith(config.foraminiferaInitialSignal.toSignalVector), lifespan, config.foraminiferaPursuedSignalIndex) } def unapply(arg: BufferCell)(implicit config: ForminConfig): ForaminiferaAccessible[BufferCell] = new ForaminiferaAccessible[BufferCell] { - override def withForaminifera(energy: Energy, lifespan: Long): BufferCell = BufferCell(ForaminiferaCell(energy, arg.smellWith(config.foraminiferaInitialSignal), lifespan)) + override def withForaminifera(energy: Energy, lifespan: Long): BufferCell = BufferCell(ForaminiferaCell(energy, arg.smellWith(config.foraminiferaInitialSignal.toSignalVector), lifespan, config.foraminiferaPursuedSignalIndex)) } def unapply(arg: GridPart)(implicit config: ForminConfig): Option[ForaminiferaAccessible[GridPart]] = arg match { diff --git a/formin/src/main/scala/pl/edu/agh/formin/model/parallel/ForminConflictResolver.scala b/formin/src/main/scala/pl/edu/agh/formin/model/parallel/ForminConflictResolver.scala index 631cea0a..3de86c1c 100644 --- a/formin/src/main/scala/pl/edu/agh/formin/model/parallel/ForminConflictResolver.scala +++ b/formin/src/main/scala/pl/edu/agh/formin/model/parallel/ForminConflictResolver.scala @@ -16,14 +16,14 @@ object ForminConflictResolver extends ConflictResolver[ForminConfig] { (incomingCell.withSmell(incomingCell.smell + currentSmell), ForminMetrics.empty()) case (currentCell: SmellingCell, EmptyCell(incomingSmell)) => (currentCell.withSmell(currentCell.smell + incomingSmell), ForminMetrics.empty()) - case (AlgaeCell(currentSmell, currentLifespan), ForaminiferaCell(energy, incomingSmell, incomingLifespan)) => - (ForaminiferaCell(energy + config.algaeEnergeticCapacity, incomingSmell + currentSmell, incomingLifespan), ForminMetrics(0, 0, 0, 0, 0, 1, 0, currentLifespan)) - case (ForaminiferaCell(energy, currentSmell, currentLifespan), AlgaeCell(incomingSmell, incomingLifespan)) => - (ForaminiferaCell(energy + config.algaeEnergeticCapacity, incomingSmell + currentSmell, currentLifespan), ForminMetrics(0, 0, 0, 0, 0, 1, 0, incomingLifespan)) + case (AlgaeCell(currentSmell, currentLifespan), ForaminiferaCell(energy, incomingSmell, incomingLifespan, pursuedSignalIndex)) => + (ForaminiferaCell(energy + config.algaeEnergeticCapacity, incomingSmell + currentSmell, incomingLifespan, pursuedSignalIndex), ForminMetrics(0, 0, 0, 0, 0, 1, 0, currentLifespan)) + case (ForaminiferaCell(energy, currentSmell, currentLifespan, pursuedSignalIndex), AlgaeCell(incomingSmell, incomingLifespan)) => + (ForaminiferaCell(energy + config.algaeEnergeticCapacity, incomingSmell + currentSmell, currentLifespan, pursuedSignalIndex), ForminMetrics(0, 0, 0, 0, 0, 1, 0, incomingLifespan)) case (AlgaeCell(currentSmell, lifespan), AlgaeCell(incomingSmell, incomingLifespan)) => (AlgaeCell(currentSmell + incomingSmell, math.max(lifespan, incomingLifespan)), ForminMetrics.empty()) - case (ForaminiferaCell(currentEnergy, currentSmell, lifespan), ForaminiferaCell(incomingEnergy, incomingSmell, incomingLifespan)) => - (ForaminiferaCell(currentEnergy + incomingEnergy, currentSmell + incomingSmell, math.max(lifespan, incomingLifespan)), ForminMetrics.empty()) + case (ForaminiferaCell(currentEnergy, currentSmell, lifespan, _), ForaminiferaCell(incomingEnergy, incomingSmell, incomingLifespan, pursuedSignalIndex)) => // TODO: here are place where two smells can intersect + (ForaminiferaCell(currentEnergy + incomingEnergy, currentSmell + incomingSmell, math.max(lifespan, incomingLifespan), pursuedSignalIndex), ForminMetrics.empty()) case (Obstacle, _) => (Obstacle, ForminMetrics.empty()) case (x, y) => throw new UnsupportedOperationException(s"Unresolved conflict: $x with $y") } 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..97d20fcc 100644 --- a/formin/src/test/scala/pl.edu.agh.formin/GridTest.scala +++ b/formin/src/test/scala/pl.edu.agh.formin/GridTest.scala @@ -21,8 +21,10 @@ class GridTest extends FlatSpecLike with Matchers with BeforeAndAfter { gridSize = 5, spawnChance = 0.1, foraminiferaSpawnChance = 0.5, - foraminiferaInitialSignal = Signal(-1), - algaeInitialSignal = Signal(1), + foraminiferaInitialSignal = List(Signal(-1), Signal(0)), + foraminiferaPursuedSignalIndex = 0, + algaeInitialSignal = List(Signal(1), Signal(0)), + algaePursuedSignalIndex = 0, guiType = GuiType.None, guiCellSize = 4, workersRoot = 1, 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..c679b2cb 100644 --- a/formin/src/test/scala/pl.edu.agh.formin/MovesControllerTest.scala +++ b/formin/src/test/scala/pl.edu.agh.formin/MovesControllerTest.scala @@ -24,8 +24,10 @@ class MovesControllerTest extends FlatSpecLike with Matchers with BeforeAndAfter gridSize = 5, spawnChance = 0.1, foraminiferaSpawnChance = 0.5, - foraminiferaInitialSignal = Signal(-1), - algaeInitialSignal = Signal(1), + foraminiferaInitialSignal = List(Signal(-1), Signal(0)), + foraminiferaPursuedSignalIndex = 0, + algaeInitialSignal = List(Signal(1), Signal(0)), + algaePursuedSignalIndex = 0, guiType = GuiType.None, guiCellSize = 4, workersRoot = 1, @@ -47,10 +49,10 @@ class MovesControllerTest extends FlatSpecLike with Matchers with BeforeAndAfter grid.cells(3)(2) = ForaminiferaAccessible.unapply(EmptyCell.Instance).withForaminifera(config.foraminiferaStartEnergy, 0) grid.cells(2)(3) = ForaminiferaAccessible.unapply(EmptyCell.Instance).withForaminifera(config.foraminiferaStartEnergy, 0) - grid.cells(2)(2).smell(0)(0) = Signal(20) - grid.cells(2)(2).smell(0)(2) = Signal(15) - grid.cells(2)(2).smell(1)(0) = Signal(-5) - grid.cells(2)(2).smell(2)(2) = Signal(-666) + grid.cells(2)(2).smell(0)(0) = SignalVector(Array(Signal(20), Signal(0))) + grid.cells(2)(2).smell(0)(2) = SignalVector(Array(Signal(15), Signal(0))) + grid.cells(2)(2).smell(1)(0) = SignalVector(Array(Signal(-5), Signal(0))) + grid.cells(2)(2).smell(2)(2) = SignalVector(Array(Signal(-666), Signal(0))) val (x, y, destination) = movesController.calculatePossibleDestinations(cell1, 2, 2, grid).next() @@ -66,10 +68,10 @@ class MovesControllerTest extends FlatSpecLike with Matchers with BeforeAndAfter grid.cells(3)(2) = ForaminiferaAccessible.unapply(EmptyCell.Instance).withForaminifera(config.foraminiferaStartEnergy, 0) grid.cells(2)(3) = ForaminiferaAccessible.unapply(EmptyCell.Instance).withForaminifera(config.foraminiferaStartEnergy, 0) - grid.cells(2)(2).smell(0)(0) = Signal(20) - grid.cells(2)(2).smell(0)(2) = Signal(15) - grid.cells(2)(2).smell(1)(0) = Signal(-5) - grid.cells(2)(2).smell(2)(2) = Signal(-666) + grid.cells(2)(2).smell(0)(0) = SignalVector(Array(Signal(20), Signal(0))) + grid.cells(2)(2).smell(0)(2) = SignalVector(Array(Signal(15), Signal(0))) + grid.cells(2)(2).smell(1)(0) = SignalVector(Array(Signal(-5), Signal(0))) + grid.cells(2)(2).smell(2)(2) = SignalVector(Array(Signal(-666), Signal(0))) val destinations = movesController.calculatePossibleDestinations(cell1, 2, 2, grid) val destination = movesController.selectDestinationCell(destinations,grid) @@ -84,10 +86,10 @@ class MovesControllerTest extends FlatSpecLike with Matchers with BeforeAndAfter grid.cells(3)(2) = ForaminiferaAccessible.unapply(EmptyCell.Instance).withForaminifera(config.foraminiferaStartEnergy, 0) grid.cells(2)(3) = ForaminiferaAccessible.unapply(EmptyCell.Instance).withForaminifera(config.foraminiferaStartEnergy, 0) - grid.cells(2)(2).smell(0)(0) = Signal(4) - grid.cells(2)(2).smell(0)(2) = Signal(15) - grid.cells(2)(2).smell(1)(0) = Signal(-5) - grid.cells(2)(2).smell(2)(2) = Signal(-666) + grid.cells(2)(2).smell(0)(0) = SignalVector(Array(Signal(4), Signal(0))) + grid.cells(2)(2).smell(0)(2) = SignalVector(Array(Signal(15), Signal(0))) + grid.cells(2)(2).smell(1)(0) = SignalVector(Array(Signal(-5), Signal(0))) + grid.cells(2)(2).smell(2)(2) = SignalVector(Array(Signal(-666), Signal(0))) val destinations = movesController.calculatePossibleDestinations(cell1, 2, 2, grid) val destination = movesController.selectDestinationCell(destinations,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..77a2e416 100644 --- a/formin/src/test/scala/pl.edu.agh.formin/ParallelTest.scala +++ b/formin/src/test/scala/pl.edu.agh.formin/ParallelTest.scala @@ -29,8 +29,10 @@ class ParallelTest extends FlatSpec with Matchers with Eventually with ScalaFutu gridSize = 5, spawnChance = 0.1, foraminiferaSpawnChance = 0.5, - foraminiferaInitialSignal = Signal(-1), - algaeInitialSignal = Signal(1), + foraminiferaInitialSignal = List(Signal(-1), Signal(0)), + foraminiferaPursuedSignalIndex = 0, + algaeInitialSignal = List(Signal(1), Signal(0)), + algaePursuedSignalIndex = 0, guiType = GuiType.None, guiCellSize = 4, workersRoot = 3, 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..5a37a3f8 100644 --- a/formin/src/test/scala/pl.edu.agh.formin/WorkerActorTest.scala +++ b/formin/src/test/scala/pl.edu.agh.formin/WorkerActorTest.scala @@ -26,8 +26,10 @@ class WorkerActorTest extends FlatSpecLike with Matchers with Eventually with Sc gridSize = 5, spawnChance = 0.1, foraminiferaSpawnChance = 0.5, - foraminiferaInitialSignal = Signal(-1), - algaeInitialSignal = Signal(1), + foraminiferaInitialSignal = List(Signal(-1), Signal(0)), + foraminiferaPursuedSignalIndex = 0, + algaeInitialSignal = List(Signal(1), Signal(0)), + algaePursuedSignalIndex = 0, guiType = GuiType.None, guiCellSize = 4, workersRoot = 2, diff --git a/torch/src/main/scala/pl.edu.agh.torch/algorithm/TorchMovesController.scala b/torch/src/main/scala/pl.edu.agh.torch/algorithm/TorchMovesController.scala index af044dfb..c48ca1e6 100644 --- a/torch/src/main/scala/pl.edu.agh.torch/algorithm/TorchMovesController.scala +++ b/torch/src/main/scala/pl.edu.agh.torch/algorithm/TorchMovesController.scala @@ -67,6 +67,9 @@ final class TorchMovesController(bufferZone: TreeSet[(Int, Int)])(implicit confi case (i, j) => cell.smell(i)(j) } .zipWithIndex + .map { + case (signalVector, index) => (signalVector(cell.pursuedSignalIndex), index) + } .sorted(implicitly[Ordering[(Signal, Int)]].reverse) .iterator .map { diff --git a/torch/src/main/scala/pl.edu.agh.torch/model/Human.scala b/torch/src/main/scala/pl.edu.agh.torch/model/Human.scala index cd9b1a41..5463804e 100644 --- a/torch/src/main/scala/pl.edu.agh.torch/model/Human.scala +++ b/torch/src/main/scala/pl.edu.agh.torch/model/Human.scala @@ -4,7 +4,7 @@ import pl.edu.agh.torch.config.TorchConfig import pl.edu.agh.xinuk.model.Cell.SmellArray import pl.edu.agh.xinuk.model.{BufferCell, EmptyCell, GridPart, SmellingCell} -final case class HumanCell(smell: SmellArray, crowd : List[HumanCell], speed : Int) (implicit config: TorchConfig) extends SmellingCell { +final case class HumanCell(smell: SmellArray, crowd : List[HumanCell], speed : Int, pursuedSignalIndex: Int) (implicit config: TorchConfig) extends SmellingCell { override type Self = HumanCell 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..ff18267d 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: (CellArray, Int, Int) => Vector[Option[SignalVector]], emptyCellFactory: => SmellingCell = EmptyCell.Instance)( movesControllerFactory: (TreeSet[(Int, Int)], ConfigType) => MovesController, cellToColor: PartialFunction[GridPart, Color] = PartialFunction.empty 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..6229762d 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 @@ -5,7 +5,7 @@ import pl.edu.agh.xinuk.model.Grid.{CellArray, SubcellCoordinates} object DefaultSmellPropagation { - def calculateSmellAddendsStandard(cells: CellArray, x: Int, y: Int): Vector[Option[Signal]] = { + def calculateSmellAddendsStandard(cells: CellArray, x: Int, y: Int): Vector[Option[SignalVector]] = { @inline def destinationCellSignal(i: Int, j: Int): Option[SmellArray] = { cells.lift(x + i - 1).flatMap(_.lift(y + j - 1).map(_.smell)) } @@ -20,7 +20,7 @@ object DefaultSmellPropagation { } } - def calculateSmellAddendsCircular(cells: CellArray, x: Int, y: Int): Vector[Option[Signal]] = { + def calculateSmellAddendsCircular(cells: CellArray, x: Int, y: Int): Vector[Option[SignalVector]] = { def sideToSide = 1.0 / 3 def sideToCorner = 1.0 / Math.sqrt(10) def cornerToSide = 1.0 / Math.sqrt(13) diff --git a/xinuk-core/src/main/scala/pl/edu/agh/xinuk/model/Grid.scala b/xinuk-core/src/main/scala/pl/edu/agh/xinuk/model/Grid.scala index b5af0599..641519c4 100644 --- a/xinuk-core/src/main/scala/pl/edu/agh/xinuk/model/Grid.scala +++ b/xinuk-core/src/main/scala/pl/edu/agh/xinuk/model/Grid.scala @@ -8,19 +8,19 @@ final case class Grid(cells: CellArray) extends AnyVal { import Grid._ - def propagatedSignal(calculateSmellAddends: (CellArray, Int, Int) => Vector[Option[Signal]], x: Int, y: Int)(implicit config: XinukConfig): GridPart = { + def propagatedSignal(calculateSmellAddends: (CellArray, Int, Int) => Vector[Option[SignalVector]], x: Int, y: Int)(implicit config: XinukConfig): GridPart = { val current = cells(x)(y) current match { case Obstacle => current case smelling: SmellMedium => val currentSmell = current.smell val addends = calculateSmellAddends(cells, x, y) - val (newSmell, _) = addends.foldLeft(Array.ofDim[Signal](Cell.Size, Cell.Size), 0) { case ((cell, index), signalOpt) => + val (newSmell, _) = addends.foldLeft(Array.ofDim[SignalVector](Cell.Size, Cell.Size), 0) { case ((cell, index), signalOpt) => val (i, j) = SubcellCoordinates(index) - cell(i)(j) = (currentSmell(i)(j) * config.signalAttenuationFactor) + (signalOpt.getOrElse(Signal.Zero) * config.signalSuppressionFactor) + cell(i)(j) = (currentSmell(i)(j) * config.signalAttenuationFactor) + (signalOpt.getOrElse(SignalVector.Zero) * config.signalSuppressionFactor) (cell, index + 1) } - newSmell(1)(1) = Signal.Zero + newSmell(1)(1) = SignalVector.Zero smelling.withSmell(newSmell) } } @@ -53,6 +53,33 @@ object Grid { } +final case class SignalVector(value: Array[Signal]) extends AnyVal {//with Ordered[SignalVector] { + def apply(index: Int): Signal = value(index) + + def +(other: SignalVector) = SignalVector(value.zipWithIndex.map{ + case (signal, index) => signal + other(index) + }) + + def -(other: SignalVector) = SignalVector(value.zipWithIndex.map{ + case (signal, index) => signal - other(index) + }) + + def *(scalar: Double) = SignalVector(value.map(_ * scalar)) + + def /(scalar: Double) = SignalVector(value.map(_ / scalar)) + +// override def compare(that: SignalVector): Int = Ordering.Iterable[Signal].compare(value, that.value) +} + +object SignalVector { + implicit class SignalVectorOps(signalList: List[Signal]) { + def toSignalVector: SignalVector = SignalVector(signalList.toArray) + } + + final val Signals = 2 + final val Zero = SignalVector(Array.fill(Signals)(Signal.Zero)) +} + final case class Signal(value: Double) extends AnyVal with Ordered[Signal] { def +(other: Signal) = Signal(value + other.value) @@ -94,9 +121,9 @@ trait SmellMedium extends GridPart { import Cell._ - final def smellWith(added: Signal): SmellArray = smell + added + final def smellWith(added: SignalVector): SmellArray = smell + added - final def smellWithout(deducted: Signal): SmellArray = { + final def smellWithout(deducted: SignalVector): SmellArray = { Array.tabulate(Cell.Size, Cell.Size)((i, j) => smell(i)(j) - deducted) } @@ -113,25 +140,25 @@ trait SmellingCell extends SmellMedium { object Cell { - type SmellArray = Array[Array[Signal]] + type SmellArray = Array[Array[SignalVector]] implicit class SmellArrayOps(private val arr: SmellArray) extends AnyVal { def +(other: SmellArray): SmellArray = { Array.tabulate(Cell.Size, Cell.Size)((x, y) => arr(x)(y) + other(x)(y)) } - def +(added: Signal): SmellArray = { + def +(added: SignalVector): SmellArray = { Array.tabulate(Cell.Size, Cell.Size)((i, j) => arr(i)(j) + added) } } final val Size: Int = 3 - def emptySignal: SmellArray = Array.fill(Cell.Size, Cell.Size)(Signal.Zero) + def emptySignal: SmellArray = Array.fill(Cell.Size, Cell.Size)(SignalVector.Zero) } case object Obstacle extends GridPart { - override val smell: SmellArray = Array.fill(Cell.Size, Cell.Size)(Signal.Zero) + override val smell: SmellArray = Array.fill(Cell.Size, Cell.Size)(SignalVector.Zero) } final case class BufferCell(cell: SmellingCell) extends SmellMedium with GridPart { diff --git a/xinuk-core/src/main/scala/pl/edu/agh/xinuk/simulation/WorkerActor.scala b/xinuk-core/src/main/scala/pl/edu/agh/xinuk/simulation/WorkerActor.scala index d63651a8..f7a309a8 100644 --- a/xinuk-core/src/main/scala/pl/edu/agh/xinuk/simulation/WorkerActor.scala +++ b/xinuk-core/src/main/scala/pl/edu/agh/xinuk/simulation/WorkerActor.scala @@ -18,7 +18,7 @@ class WorkerActor[ConfigType <: XinukConfig]( regionRef: => ActorRef, movesControllerFactory: (TreeSet[(Int, Int)], ConfigType) => MovesController, conflictResolver: ConflictResolver[ConfigType], - smellPropagationFunction: (CellArray, Int, Int) => Vector[Option[Signal]], + smellPropagationFunction: (CellArray, Int, Int) => Vector[Option[SignalVector]], emptyCellFactory: => SmellingCell = EmptyCell.Instance)(implicit config: ConfigType) extends Actor with Stash { import pl.edu.agh.xinuk.simulation.WorkerActor._ @@ -169,7 +169,7 @@ object WorkerActor { regionRef: => ActorRef, movesControllerFactory: (TreeSet[(Int, Int)], ConfigType) => MovesController, conflictResolver: ConflictResolver[ConfigType], - smellPropagationFunction: (CellArray, Int, Int) => Vector[Option[Signal]], + smellPropagationFunction: (CellArray, Int, Int) => Vector[Option[SignalVector]], emptyCellFactory: => SmellingCell = EmptyCell.Instance )(implicit config: ConfigType): Props = { Props(new WorkerActor(regionRef, movesControllerFactory, conflictResolver, smellPropagationFunction, emptyCellFactory)) From 1c288a0a352f43e71e0b83570f880444d676c039 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Zieli=C5=84ski?= Date: Sat, 6 Apr 2019 00:09:31 +0200 Subject: [PATCH 02/10] Fix other simulations and several unit tests --- .../scala/pl.edu.agh.formin/GridTest.scala | 50 +++++++++---------- fortwist/src/main/resources/reference.conf | 6 ++- .../algorithm/FortwistMovesController.scala | 12 +++-- .../agh/fortwist/config/FortwistConfig.scala | 6 ++- .../edu/agh/fortwist/model/Foraminifera.scala | 4 +- mock/src/main/resources/reference.conf | 3 +- .../main/scala/pl.edu.agh.mock/MockMain.scala | 10 +++- .../algorithm/MockMovesController.scala | 5 +- .../pl.edu.agh.mock/config/MockConfig.scala | 3 +- .../pl.edu.agh.mock/model/MockCell.scala | 4 +- torch/src/main/resources/reference.conf | 9 ++-- .../scala/pl.edu.agh.torch/TorchMain.scala | 2 +- .../algorithm/TorchMovesController.scala | 4 +- .../pl.edu.agh.torch/config/TorchConfig.scala | 9 ++-- .../scala/pl.edu.agh.torch/model/Escape.scala | 3 +- .../scala/pl.edu.agh.torch/model/Fire.scala | 10 ++-- .../scala/pl.edu.agh.torch/model/Human.scala | 5 +- .../parallel/TorchConflictResolver.scala | 10 ++-- 18 files changed, 90 insertions(+), 65 deletions(-) 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 97d20fcc..f1fe3cb5 100644 --- a/formin/src/test/scala/pl.edu.agh.formin/GridTest.scala +++ b/formin/src/test/scala/pl.edu.agh.formin/GridTest.scala @@ -64,13 +64,13 @@ class GridTest extends FlatSpecLike with Matchers with BeforeAndAfter { grid.cells(3)(2) = grid.propagatedSignal(DefaultSmellPropagation.calculateSmellAddendsStandard, 3, 2) grid.cells(3)(1) = grid.propagatedSignal(DefaultSmellPropagation.calculateSmellAddendsStandard, 3, 1) - grid.cells(2)(2).smell(0)(0).value shouldBe -1 - grid.cells(2)(2).smell(0)(1).value shouldBe -1 - grid.cells(2)(2).smell(0)(2).value shouldBe -1 + grid.cells(2)(2).smell(0)(0).value(0).value shouldBe -1 + grid.cells(2)(2).smell(0)(1).value(0).value shouldBe -1 + grid.cells(2)(2).smell(0)(2).value(0).value shouldBe -1 - grid.cells(3)(2).smell(0)(1).value shouldBe -1.5 + grid.cells(3)(2).smell(0)(1).value(0).value shouldBe -1.5 - grid.cells(3)(1).smell(0)(2).value shouldBe -0.5 + grid.cells(3)(1).smell(0)(2).value(0).value shouldBe -0.5 } it should "propagate signal correctly for one algae cell" in { @@ -78,13 +78,13 @@ class GridTest extends FlatSpecLike with Matchers with BeforeAndAfter { grid.cells(3)(2) = grid.propagatedSignal(DefaultSmellPropagation.calculateSmellAddendsStandard, 3, 2) grid.cells(3)(1) = grid.propagatedSignal(DefaultSmellPropagation.calculateSmellAddendsStandard, 3, 1) - grid.cells(2)(2).smell(0)(0).value shouldBe 1 - grid.cells(2)(2).smell(0)(1).value shouldBe 1 - grid.cells(2)(2).smell(0)(2).value shouldBe 1 + grid.cells(2)(2).smell(0)(0).value(0).value shouldBe 1 + grid.cells(2)(2).smell(0)(1).value(0).value shouldBe 1 + grid.cells(2)(2).smell(0)(2).value(0).value shouldBe 1 - grid.cells(3)(2).smell(0)(1).value shouldBe 1.5 + grid.cells(3)(2).smell(0)(1).value(0).value shouldBe 1.5 - grid.cells(3)(1).smell(0)(2).value shouldBe 0.5 + grid.cells(3)(1).smell(0)(2).value(0).value shouldBe 0.5 } it should "propagate signal correctly between algae and foraminifera cells" in { @@ -95,26 +95,26 @@ class GridTest extends FlatSpecLike with Matchers with BeforeAndAfter { grid.cells(2)(2) = gridCellWithAlgaeAfterSignalPropagation grid.cells(3)(2) = gridCellWithForaminiferaAfterSignalPropagation - grid.cells(2)(2).smell(2)(1).value shouldBe -0.5 - grid.cells(2)(2).smell(2)(0).value shouldBe 1 - grid.cells(2)(2).smell(2)(2).value shouldBe 1 - grid.cells(2)(2).smell(0)(0).value shouldBe 1 - grid.cells(2)(2).smell(0)(1).value shouldBe 1 - grid.cells(2)(2).smell(0)(2).value shouldBe 1 - - grid.cells(3)(2).smell(0)(0).value shouldBe -1 - grid.cells(3)(2).smell(0)(1).value shouldBe 0.5 - grid.cells(3)(2).smell(0)(2).value shouldBe -1 - grid.cells(3)(2).smell(2)(0).value shouldBe -1 - grid.cells(3)(2).smell(2)(1).value shouldBe -1 - grid.cells(3)(2).smell(2)(2).value shouldBe -1 + grid.cells(2)(2).smell(2)(1).value(0).value shouldBe -0.5 + grid.cells(2)(2).smell(2)(0).value(0).value shouldBe 1 + grid.cells(2)(2).smell(2)(2).value(0).value shouldBe 1 + grid.cells(2)(2).smell(0)(0).value(0).value shouldBe 1 + grid.cells(2)(2).smell(0)(1).value(0).value shouldBe 1 + grid.cells(2)(2).smell(0)(2).value(0).value shouldBe 1 + + grid.cells(3)(2).smell(0)(0).value(0).value shouldBe -1 + grid.cells(3)(2).smell(0)(1).value(0).value shouldBe 0.5 + grid.cells(3)(2).smell(0)(2).value(0).value shouldBe -1 + grid.cells(3)(2).smell(2)(0).value(0).value shouldBe -1 + grid.cells(3)(2).smell(2)(1).value(0).value shouldBe -1 + grid.cells(3)(2).smell(2)(2).value(0).value shouldBe -1 } 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(3)(2).smell(1)(1).value shouldBe -1 - grid.cells(4)(2).smell(0)(1).value shouldBe 0 + grid.cells(3)(2).smell(1)(1).value(0).value shouldBe -1 + grid.cells(4)(2).smell(0)(1).value(0).value shouldBe 0 } it should "calculate neighbour cells correctly for middle one" in { diff --git a/fortwist/src/main/resources/reference.conf b/fortwist/src/main/resources/reference.conf index 13a81ad9..3f84295d 100644 --- a/fortwist/src/main/resources/reference.conf +++ b/fortwist/src/main/resources/reference.conf @@ -33,8 +33,10 @@ fortwist { signalAttenuationFactor = 0.4 gridSize = 110 foraminiferaSpawnChance = 0.3 - foraminiferaInitialSignal = -1 - algaeSignalMultiplier = 1 + foraminiferaInitialSignal = [-1, 0] + foraminiferaPursuedSignalIndex = 0 + algaeSignalMultiplier = [1, 0] + algaePursuedSignalIndex = 0 guiType = basic guiCellSize = 6 workersRoot = 1 diff --git a/fortwist/src/main/scala/pl/edu/agh/fortwist/algorithm/FortwistMovesController.scala b/fortwist/src/main/scala/pl/edu/agh/fortwist/algorithm/FortwistMovesController.scala index 4d853ba6..8795a77e 100644 --- a/fortwist/src/main/scala/pl/edu/agh/fortwist/algorithm/FortwistMovesController.scala +++ b/fortwist/src/main/scala/pl/edu/agh/fortwist/algorithm/FortwistMovesController.scala @@ -10,6 +10,7 @@ import pl.edu.agh.xinuk.model._ import scala.collection.immutable.TreeSet import scala.util.Random +import pl.edu.agh.xinuk.model.SignalVector.SignalVectorOps final class FortwistMovesController(bufferZone: TreeSet[(Int, Int)])(implicit config: FortwistConfig) extends MovesController { @@ -32,7 +33,7 @@ final class FortwistMovesController(bufferZone: TreeSet[(Int, Int)])(implicit co if (random.nextDouble() < config.foraminiferaSpawnChance) { val foraminiferas = Vector(Foraminifera.create()) val cell = FortwistCell( - smell = Cell.emptySignal + config.foraminiferaInitialSignal + (config.algaeSignalMultiplier * config.algaeStartEnergy.value), + smell = Cell.emptySignal + config.foraminiferaInitialSignal.toSignalVector + (config.algaeSignalMultiplier.toSignalVector * config.algaeStartEnergy.value), foraminiferas = foraminiferas, algae = config.algaeStartEnergy ) @@ -70,8 +71,8 @@ final class FortwistMovesController(bufferZone: TreeSet[(Int, Int)])(implicit co def update(x: Int, y: Int)(op: FortwistCell => FortwistCell): Unit = { def updated(cell: FortwistCell): FortwistCell = { val afterOp = op(cell) - val smellAdjustment = (config.foraminiferaInitialSignal * afterOp.foraminiferas.size) + - (config.algaeSignalMultiplier * afterOp.algae.value) + val smellAdjustment = (config.foraminiferaInitialSignal.toSignalVector * afterOp.foraminiferas.size) + + (config.algaeSignalMultiplier.toSignalVector * afterOp.algae.value) afterOp.copy(smell = afterOp.smell + smellAdjustment) } @@ -147,8 +148,11 @@ final class FortwistMovesController(bufferZone: TreeSet[(Int, Int)])(implicit co def calculatePossibleDestinations(x: Int, y: Int, grid: Grid): Iterator[(Int, Int, GridPart)] = { val neighbourCellCoordinates = Grid.neighbourCellCoordinates(x, y) Grid.SubcellCoordinates - .map { case (i, j) => grid.cells(x)(y).smell(i)(j) + moves.get((x, y)).map(formins => config.foraminiferaInitialSignal * formins.size).getOrElse(Signal.Zero) } + .map { case (i, j) => grid.cells(x)(y).smell(i)(j) + moves.get((x, y)).map(formins => config.foraminiferaInitialSignal.toSignalVector * formins.size).getOrElse(SignalVector.Zero) } .zipWithIndex + .map { + case (signalVector, index) => (signalVector(foraminifera.pursuedSignalIndex), index) + } .sorted(implicitly[Ordering[(Signal, Int)]].reverse) .iterator .map { case (_, idx) => 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..5da41ba3 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 @@ -32,8 +32,10 @@ final case class FortwistConfig( signalAttenuationFactor: Double, gridSize: Int, foraminiferaSpawnChance: Double, - foraminiferaInitialSignal: Signal, - algaeSignalMultiplier: Signal, + foraminiferaInitialSignal: List[Signal], + foraminiferaPursuedSignalIndex: Int, + algaeSignalMultiplier: List[Signal], + algaePursuedSignalIndex: Int, guiType: GuiType, guiCellSize: Int, workersRoot: Int, diff --git a/fortwist/src/main/scala/pl/edu/agh/fortwist/model/Foraminifera.scala b/fortwist/src/main/scala/pl/edu/agh/fortwist/model/Foraminifera.scala index 271461f0..5133e635 100644 --- a/fortwist/src/main/scala/pl/edu/agh/fortwist/model/Foraminifera.scala +++ b/fortwist/src/main/scala/pl/edu/agh/fortwist/model/Foraminifera.scala @@ -4,10 +4,10 @@ import pl.edu.agh.fortwist.config.FortwistConfig import pl.edu.agh.xinuk.model.Cell.SmellArray import pl.edu.agh.xinuk.model._ -final case class Foraminifera(energy: Energy, lifespan: Long) +final case class Foraminifera(energy: Energy, lifespan: Long, pursuedSignalIndex: Int) object Foraminifera { - def create()(implicit config: FortwistConfig): Foraminifera = Foraminifera(config.foraminiferaStartEnergy, 0) + def create()(implicit config: FortwistConfig): Foraminifera = Foraminifera(config.foraminiferaStartEnergy, 0, config.foraminiferaPursuedSignalIndex) } final case class FortwistCell(smell: SmellArray, foraminiferas: Vector[Foraminifera], algae: Energy) extends SmellingCell { diff --git a/mock/src/main/resources/reference.conf b/mock/src/main/resources/reference.conf index c18e222a..01eaf024 100644 --- a/mock/src/main/resources/reference.conf +++ b/mock/src/main/resources/reference.conf @@ -33,6 +33,7 @@ mock { signalSpeedRatio = 2 iterationsNumber = 10000 - mockInitialSignal = 1 + mockInitialSignal = [1, 0] + mockPursuedSignalIndex = 0 } } \ No newline at end of file diff --git a/mock/src/main/scala/pl.edu.agh.mock/MockMain.scala b/mock/src/main/scala/pl.edu.agh.mock/MockMain.scala index 9c2f2a27..7ca38b3d 100644 --- a/mock/src/main/scala/pl.edu.agh.mock/MockMain.scala +++ b/mock/src/main/scala/pl.edu.agh.mock/MockMain.scala @@ -28,7 +28,10 @@ object MockMain extends LazyLogging { } private def cellToColorRegions(cell: SmellingCell): Color = { - val smellValue = cell.smell.map(_.map(_.value).max).max.toFloat + val smellValue = cell.smell + .map(signalVectors => + signalVectors.map(signalVector => + signalVector.value.map(signal => signal.value).max).max.toFloat).max val brightness = Math.pow(smellValue, 0.1).toFloat if (smellValue < 0.00001) { val hue = 1f @@ -50,7 +53,10 @@ object MockMain extends LazyLogging { } private def cellToColor(cell: SmellingCell): Color = { - val smellValue = cell.smell.map(_.map(_.value).max).max.toFloat + val smellValue = cell.smell + .map(signalVectors => + signalVectors.map(signalVector => + signalVector.value.map(signal => signal.value).max).max.toFloat).max val brightness = Math.pow(smellValue, 0.1).toFloat val hue = 1f val saturation = 0.69f diff --git a/mock/src/main/scala/pl.edu.agh.mock/algorithm/MockMovesController.scala b/mock/src/main/scala/pl.edu.agh.mock/algorithm/MockMovesController.scala index cbdac52a..2bde9a1c 100644 --- a/mock/src/main/scala/pl.edu.agh.mock/algorithm/MockMovesController.scala +++ b/mock/src/main/scala/pl.edu.agh.mock/algorithm/MockMovesController.scala @@ -8,6 +8,7 @@ import pl.edu.agh.xinuk.model._ import scala.collection.immutable.TreeSet import scala.util.Random +import pl.edu.agh.xinuk.model.SignalVector.SignalVectorOps final class MockMovesController(bufferZone: TreeSet[(Int, Int)])(implicit config: MockConfig) extends MovesController { @@ -16,7 +17,7 @@ final class MockMovesController(bufferZone: TreeSet[(Int, Int)])(implicit config override def initialGrid: (Grid, MockMetrics) = { val grid = Grid.empty(bufferZone) - grid.cells(config.gridSize / 4)(config.gridSize / 4) = MockCell.create(config.mockInitialSignal) + grid.cells(config.gridSize / 4)(config.gridSize / 4) = MockCell.create(config.mockInitialSignal.toSignalVector) val metrics = MockMetrics.empty() (grid, metrics) @@ -32,7 +33,7 @@ final class MockMovesController(bufferZone: TreeSet[(Int, Int)])(implicit config def moveCells(x: Int, y: Int, cell: GridPart): Unit = { val destination = (x + random.nextInt(3) - 1, y + random.nextInt(3) - 1) val vacatedCell = EmptyCell(cell.smell) - val occupiedCell = MockCell.create(config.mockInitialSignal) + val occupiedCell = MockCell.create(config.mockInitialSignal.toSignalVector) newGrid.cells(destination._1)(destination._2) match { case EmptyCell(_) => 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..c1d3854d 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,6 @@ final case class MockConfig( signalSpeedRatio: Int, iterationsNumber: Long, - mockInitialSignal: Signal + mockInitialSignal: List[Signal], + mockPursuedSignalIndex: Int ) extends XinukConfig \ No newline at end of file diff --git a/mock/src/main/scala/pl.edu.agh.mock/model/MockCell.scala b/mock/src/main/scala/pl.edu.agh.mock/model/MockCell.scala index f877fa76..9eb1ae99 100644 --- a/mock/src/main/scala/pl.edu.agh.mock/model/MockCell.scala +++ b/mock/src/main/scala/pl.edu.agh.mock/model/MockCell.scala @@ -1,7 +1,7 @@ package pl.edu.agh.mock.model import pl.edu.agh.xinuk.model.Cell.SmellArray -import pl.edu.agh.xinuk.model.{Cell, Signal, SmellingCell} +import pl.edu.agh.xinuk.model.{Cell, Signal, SignalVector, SmellingCell} final case class MockCell(smell: SmellArray) extends SmellingCell { override type Self = MockCell @@ -10,5 +10,5 @@ final case class MockCell(smell: SmellArray) extends SmellingCell { } object MockCell { - def create(initialSignal: Signal): MockCell = MockCell(Array.fill(Cell.Size, Cell.Size)(initialSignal)) + def create(initialSignal: SignalVector): MockCell = MockCell(Array.fill(Cell.Size, Cell.Size)(initialSignal)) } \ No newline at end of file diff --git a/torch/src/main/resources/reference.conf b/torch/src/main/resources/reference.conf index 5c6441e7..e160dd7f 100644 --- a/torch/src/main/resources/reference.conf +++ b/torch/src/main/resources/reference.conf @@ -33,9 +33,12 @@ torch { humanSpawnChance = 0.05 fireSpawnChance = 0.01 escapeSpawnChance = 0.02 - escapeInitialSignal = 1 - humanInitialSignal = -0.1 - fireInitialSignal = -0.001 + escapeInitialSignal = [1, 0] + escapePursuedSignalIndex = 0 + humanInitialSignal = [-0.1, 0] + humanPursuedSignalIndex = 0 + fireInitialSignal = [-0.001, 0] + firePursuedSignalIndex = 0 guiType = basic guiCellSize = 4 workersRoot = 1 diff --git a/torch/src/main/scala/pl.edu.agh.torch/TorchMain.scala b/torch/src/main/scala/pl.edu.agh.torch/TorchMain.scala index b5a464a0..e9a9c858 100644 --- a/torch/src/main/scala/pl.edu.agh.torch/TorchMain.scala +++ b/torch/src/main/scala/pl.edu.agh.torch/TorchMain.scala @@ -21,7 +21,7 @@ object TorchMain extends LazyLogging { private def cellToColor(cell: SmellingCell): Color = { cell match { - case HumanCell(_, _, _) => Color.BLUE + case HumanCell(_, _, _, _) => Color.BLUE case FireCell(_) => Color.ORANGE case EscapeCell(_) => new Color(139, 69, 19) case _ => Color.WHITE diff --git a/torch/src/main/scala/pl.edu.agh.torch/algorithm/TorchMovesController.scala b/torch/src/main/scala/pl.edu.agh.torch/algorithm/TorchMovesController.scala index c48ca1e6..c72fd211 100644 --- a/torch/src/main/scala/pl.edu.agh.torch/algorithm/TorchMovesController.scala +++ b/torch/src/main/scala/pl.edu.agh.torch/algorithm/TorchMovesController.scala @@ -119,7 +119,7 @@ final class TorchMovesController(bufferZone: TreeSet[(Int, Int)])(implicit confi val (newFireX, newFireY, newCell) = availableCells(random.nextInt(availableCells.size)) newGrid.cells(newFireX)(newFireY) = newCell grid.cells(newFireX)(newFireY) match { - case HumanCell(_, _, _) => + case HumanCell(_, _, _, _) => peopleDeaths += 1 case _ => } @@ -199,7 +199,7 @@ final class TorchMovesController(bufferZone: TreeSet[(Int, Int)])(implicit confi y <- 0 until config.gridSize } { grid.cells(x)(y) match { - case HumanCell(_, crowd, _) => + case HumanCell(_, crowd, _, _) => humanCount += 1 + crowd.size case FireCell(_) => fireCount += 1 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..47b2cfcb 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 @@ -15,9 +15,12 @@ final case class TorchConfig( humanSpawnChance: Double, fireSpawnChance: Double, escapeSpawnChance: Double, - humanInitialSignal: Signal, - fireInitialSignal: Signal, - escapeInitialSignal: Signal, + humanInitialSignal: List[Signal], + humanPursuedSignalIndex: Int, + fireInitialSignal: List[Signal], + firePursuedSignalIndex: Int, + escapeInitialSignal: List[Signal], + escapePursuedSignalIndex: Int, guiType: GuiType, guiCellSize: Int, workersRoot: Int, diff --git a/torch/src/main/scala/pl.edu.agh.torch/model/Escape.scala b/torch/src/main/scala/pl.edu.agh.torch/model/Escape.scala index f8e3d78c..8f8b777e 100644 --- a/torch/src/main/scala/pl.edu.agh.torch/model/Escape.scala +++ b/torch/src/main/scala/pl.edu.agh.torch/model/Escape.scala @@ -3,6 +3,7 @@ package pl.edu.agh.torch.model import pl.edu.agh.torch.config.TorchConfig import pl.edu.agh.xinuk.model.Cell.SmellArray import pl.edu.agh.xinuk.model.{EmptyCell, GridPart, SmellingCell} +import pl.edu.agh.xinuk.model.SignalVector.SignalVectorOps final case class EscapeCell(smell: SmellArray) extends SmellingCell { override type Self = EscapeCell @@ -17,7 +18,7 @@ object EscapeAccessible { def unapply(arg: EmptyCell)(implicit config: TorchConfig): EscapeAccessible[EscapeCell] = new EscapeAccessible[EscapeCell] { - override def withEscape(): EscapeCell = EscapeCell(arg.smellWith(config.escapeInitialSignal)) + override def withEscape(): EscapeCell = EscapeCell(arg.smellWith(config.escapeInitialSignal.toSignalVector)) } def unapply(arg: GridPart)(implicit config: TorchConfig): Option[EscapeAccessible[GridPart]] = arg match { diff --git a/torch/src/main/scala/pl.edu.agh.torch/model/Fire.scala b/torch/src/main/scala/pl.edu.agh.torch/model/Fire.scala index 76a0672b..60a0b5e0 100644 --- a/torch/src/main/scala/pl.edu.agh.torch/model/Fire.scala +++ b/torch/src/main/scala/pl.edu.agh.torch/model/Fire.scala @@ -3,7 +3,7 @@ package pl.edu.agh.torch.model import pl.edu.agh.torch.config.TorchConfig import pl.edu.agh.xinuk.model.Cell.SmellArray import pl.edu.agh.xinuk.model.{BufferCell, EmptyCell, GridPart, SmellingCell} - +import pl.edu.agh.xinuk.model.SignalVector.SignalVectorOps final case class FireCell(smell: SmellArray) extends SmellingCell { override type Self = FireCell @@ -18,22 +18,22 @@ object FireAccessible { def unapply(arg: EmptyCell)(implicit config: TorchConfig): FireAccessible[FireCell] = new FireAccessible[FireCell] { - override def withFire(): FireCell = FireCell(arg.smellWith(config.fireInitialSignal)) + override def withFire(): FireCell = FireCell(arg.smellWith(config.fireInitialSignal.toSignalVector)) } def unapply(arg: HumanCell)(implicit config: TorchConfig): FireAccessible[FireCell] = new FireAccessible[FireCell] { - override def withFire(): FireCell = FireCell(arg.smellWith(config.fireInitialSignal)) + override def withFire(): FireCell = FireCell(arg.smellWith(config.fireInitialSignal.toSignalVector)) } def unapply(arg: EscapeCell)(implicit config: TorchConfig): FireAccessible[FireCell] = new FireAccessible[FireCell] { - override def withFire(): FireCell = FireCell(arg.smellWith(config.fireInitialSignal)) + override def withFire(): FireCell = FireCell(arg.smellWith(config.fireInitialSignal.toSignalVector)) } def unapply(arg: BufferCell)(implicit config: TorchConfig): FireAccessible[BufferCell] = new FireAccessible[BufferCell] { - override def withFire(): BufferCell = BufferCell(FireCell(arg.smellWith(config.fireInitialSignal))) + override def withFire(): BufferCell = BufferCell(FireCell(arg.smellWith(config.fireInitialSignal.toSignalVector))) } def unapply(arg: GridPart)(implicit config: TorchConfig): Option[FireAccessible[GridPart]] = arg match { diff --git a/torch/src/main/scala/pl.edu.agh.torch/model/Human.scala b/torch/src/main/scala/pl.edu.agh.torch/model/Human.scala index 5463804e..ea4a5ce1 100644 --- a/torch/src/main/scala/pl.edu.agh.torch/model/Human.scala +++ b/torch/src/main/scala/pl.edu.agh.torch/model/Human.scala @@ -3,6 +3,7 @@ package pl.edu.agh.torch.model import pl.edu.agh.torch.config.TorchConfig import pl.edu.agh.xinuk.model.Cell.SmellArray import pl.edu.agh.xinuk.model.{BufferCell, EmptyCell, GridPart, SmellingCell} +import pl.edu.agh.xinuk.model.SignalVector.SignalVectorOps final case class HumanCell(smell: SmellArray, crowd : List[HumanCell], speed : Int, pursuedSignalIndex: Int) (implicit config: TorchConfig) extends SmellingCell { @@ -18,7 +19,7 @@ object HumanAccessible { def unapply(arg: EmptyCell)(implicit config: TorchConfig): HumanAccessible[HumanCell] = new HumanAccessible[HumanCell] { - override def withHuman(crowd: List[HumanCell], speed: Int): HumanCell = HumanCell(arg.smellWith(config.humanInitialSignal), crowd, speed) + override def withHuman(crowd: List[HumanCell], speed: Int): HumanCell = HumanCell(arg.smellWith(config.humanInitialSignal.toSignalVector), crowd, speed, config.humanPursuedSignalIndex) } def unapply(arg: EscapeCell): HumanAccessible[EscapeCell] = @@ -28,7 +29,7 @@ object HumanAccessible { def unapply(arg: BufferCell)(implicit config: TorchConfig): HumanAccessible[BufferCell] = new HumanAccessible[BufferCell] { - override def withHuman(crowd: List[HumanCell], speed: Int): BufferCell = BufferCell(HumanCell(arg.smellWith(config.humanInitialSignal), crowd, speed)) + override def withHuman(crowd: List[HumanCell], speed: Int): BufferCell = BufferCell(HumanCell(arg.smellWith(config.humanInitialSignal.toSignalVector), crowd, speed, config.humanPursuedSignalIndex)) } def unapply(arg: GridPart)(implicit config: TorchConfig): Option[HumanAccessible[GridPart]] = arg match { diff --git a/torch/src/main/scala/pl.edu.agh.torch/model/parallel/TorchConflictResolver.scala b/torch/src/main/scala/pl.edu.agh.torch/model/parallel/TorchConflictResolver.scala index 268afd25..e3e63ee6 100644 --- a/torch/src/main/scala/pl.edu.agh.torch/model/parallel/TorchConflictResolver.scala +++ b/torch/src/main/scala/pl.edu.agh.torch/model/parallel/TorchConflictResolver.scala @@ -16,18 +16,18 @@ object TorchConflictResolver extends ConflictResolver[TorchConfig] { (incomingCell.withSmell(incomingCell.smell + currentSmell), TorchMetrics.empty()) case (currentCell: SmellingCell, EmptyCell(incomingSmell)) => (currentCell.withSmell(currentCell.smell + incomingSmell), TorchMetrics.empty()) - case (EscapeCell(currentSmell), HumanCell(_, _, _)) => + case (EscapeCell(currentSmell), HumanCell(_, _, _, _)) => (EscapeCell(currentSmell), TorchMetrics(0, 0, 0, 0, 1)) case (EscapeCell(_), FireCell(incomingCell)) => (FireCell(incomingCell), TorchMetrics.empty()) case (FireCell(currentSmell), FireCell(incomingSmell)) => (FireCell(currentSmell + incomingSmell), TorchMetrics.empty()) - case (FireCell(currentSmell), HumanCell(incomingSmell, _, _)) => + case (FireCell(currentSmell), HumanCell(incomingSmell, _, _, _)) => (FireCell(currentSmell + incomingSmell), TorchMetrics(0, 0, 0, 1, 0)) - case (HumanCell(currentSmell, _, _), FireCell(incomingSmell)) => + case (HumanCell(currentSmell, _, _, _), FireCell(incomingSmell)) => (FireCell(currentSmell + incomingSmell), TorchMetrics(0, 0, 0, 1, 0)) - case (HumanCell(currentSmell, currentCrowd, currentSpeed), another@HumanCell(incomingSmell, incomingCrowd, _)) => - (HumanCell(currentSmell + incomingSmell, currentCrowd ++ incomingCrowd ++ List(another), currentSpeed), TorchMetrics.empty()) + case (HumanCell(currentSmell, currentCrowd, currentSpeed, pursuedSignalIndex), another@HumanCell(incomingSmell, incomingCrowd, _, _)) => + (HumanCell(currentSmell + incomingSmell, currentCrowd ++ incomingCrowd ++ List(another), currentSpeed, pursuedSignalIndex), TorchMetrics.empty()) case (Obstacle, _) => (Obstacle, TorchMetrics.empty()) case (x, y) => throw new UnsupportedOperationException(s"Unresolved conflict: $x with $y") } From c475d00d13237f162b6ecedf98a499cd0943a4f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Zieli=C5=84ski?= Date: Sat, 6 Apr 2019 14:31:32 +0200 Subject: [PATCH 03/10] Mock school module based on formin --- build.sbt | 5 +- formin/src/main/resources/reference.conf | 6 +- .../agh/{formin => school}/ForminMain.scala | 10 +- .../algorithm/ForminMovesController.scala | 8 +- .../config/ForminConfig.scala | 2 +- .../agh/{formin => school}/model/Algae.scala | 4 +- .../model/Foraminifera.scala | 4 +- .../parallel/ForminConflictResolver.scala | 8 +- .../simulation/ForminMetrics.scala | 2 +- .../GridTest.scala | 6 +- .../MovesControllerTest.scala | 8 +- .../ParallelTest.scala | 10 +- .../WorkerActorTest.scala | 8 +- school/src/main/resources/reference.conf | 48 +++++ .../scala/pl/edu/agh/school/SchoolMain.scala | 43 ++++ .../algorithm/SchoolMovesController.scala | 198 ++++++++++++++++++ .../edu/agh/school/config/SchoolConfig.scala | 45 ++++ .../scala/pl/edu/agh/school/model/Algae.scala | 35 ++++ .../edu/agh/school/model/Foraminifera.scala | 41 ++++ .../parallel/SchoolConflictResolver.scala | 31 +++ .../agh/school/simulation/SchoolMetrics.scala | 44 ++++ 21 files changed, 526 insertions(+), 40 deletions(-) rename formin/src/main/scala/pl/edu/agh/{formin => school}/ForminMain.scala (81%) rename formin/src/main/scala/pl/edu/agh/{formin => school}/algorithm/ForminMovesController.scala (97%) rename formin/src/main/scala/pl/edu/agh/{formin => school}/config/ForminConfig.scala (98%) rename formin/src/main/scala/pl/edu/agh/{formin => school}/model/Algae.scala (94%) rename formin/src/main/scala/pl/edu/agh/{formin => school}/model/Foraminifera.scala (96%) rename formin/src/main/scala/pl/edu/agh/{formin => school}/model/parallel/ForminConflictResolver.scala (92%) rename formin/src/main/scala/pl/edu/agh/{formin => school}/simulation/ForminMetrics.scala (98%) rename formin/src/test/scala/{pl.edu.agh.formin => school}/GridTest.scala (98%) rename formin/src/test/scala/{pl.edu.agh.formin => school}/MovesControllerTest.scala (96%) rename formin/src/test/scala/{pl.edu.agh.formin => school}/ParallelTest.scala (98%) rename formin/src/test/scala/{pl.edu.agh.formin => school}/WorkerActorTest.scala (95%) create mode 100644 school/src/main/resources/reference.conf create mode 100644 school/src/main/scala/pl/edu/agh/school/SchoolMain.scala create mode 100644 school/src/main/scala/pl/edu/agh/school/algorithm/SchoolMovesController.scala create mode 100644 school/src/main/scala/pl/edu/agh/school/config/SchoolConfig.scala create mode 100644 school/src/main/scala/pl/edu/agh/school/model/Algae.scala create mode 100644 school/src/main/scala/pl/edu/agh/school/model/Foraminifera.scala create mode 100644 school/src/main/scala/pl/edu/agh/school/model/parallel/SchoolConflictResolver.scala create mode 100644 school/src/main/scala/pl/edu/agh/school/simulation/SchoolMetrics.scala diff --git a/build.sbt b/build.sbt index f435ab3f..b55a522a 100644 --- a/build.sbt +++ b/build.sbt @@ -37,7 +37,7 @@ inThisBuild(Seq( )) lazy val xinuk = project.in(file(".")) - .aggregate(`xinuk-core`, formin, fortwist, torch) + .aggregate(`xinuk-core`, formin, fortwist, torch, school) .disablePlugins(AssemblyPlugin) lazy val `xinuk-core` = project @@ -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 school = modelProject("school")("pl.edu.agh.school.SchoolMain") diff --git a/formin/src/main/resources/reference.conf b/formin/src/main/resources/reference.conf index 3190372b..0d721cd1 100644 --- a/formin/src/main/resources/reference.conf +++ b/formin/src/main/resources/reference.conf @@ -14,9 +14,9 @@ clustering { xinuk { classes = [ - "pl.edu.agh.formin.model.AlgaeCell", - "pl.edu.agh.formin.model.ForaminiferaCell", - "pl.edu.agh.formin.simulation.ForminMetrics", + "pl.edu.agh.school.model.AlgaeCell", + "pl.edu.agh.school.model.ForaminiferaCell", + "pl.edu.agh.school.simulation.ForminMetrics", ] } diff --git a/formin/src/main/scala/pl/edu/agh/formin/ForminMain.scala b/formin/src/main/scala/pl/edu/agh/school/ForminMain.scala similarity index 81% rename from formin/src/main/scala/pl/edu/agh/formin/ForminMain.scala rename to formin/src/main/scala/pl/edu/agh/school/ForminMain.scala index e1e5d70a..d814bbd3 100644 --- a/formin/src/main/scala/pl/edu/agh/formin/ForminMain.scala +++ b/formin/src/main/scala/pl/edu/agh/school/ForminMain.scala @@ -1,12 +1,12 @@ -package pl.edu.agh.formin +package pl.edu.agh.school import java.awt.Color import com.typesafe.scalalogging.LazyLogging -import pl.edu.agh.formin.algorithm.ForminMovesController -import pl.edu.agh.formin.config.ForminConfig -import pl.edu.agh.formin.model.parallel.ForminConflictResolver -import pl.edu.agh.formin.model.{AlgaeCell, ForaminiferaCell} +import pl.edu.agh.school.algorithm.ForminMovesController +import pl.edu.agh.school.config.ForminConfig +import pl.edu.agh.school.model.parallel.ForminConflictResolver +import pl.edu.agh.school.model.{AlgaeCell, ForaminiferaCell} import pl.edu.agh.xinuk.Simulation import pl.edu.agh.xinuk.model.{DefaultSmellPropagation, SmellingCell} diff --git a/formin/src/main/scala/pl/edu/agh/formin/algorithm/ForminMovesController.scala b/formin/src/main/scala/pl/edu/agh/school/algorithm/ForminMovesController.scala similarity index 97% rename from formin/src/main/scala/pl/edu/agh/formin/algorithm/ForminMovesController.scala rename to formin/src/main/scala/pl/edu/agh/school/algorithm/ForminMovesController.scala index afef4adf..9c88a375 100644 --- a/formin/src/main/scala/pl/edu/agh/formin/algorithm/ForminMovesController.scala +++ b/formin/src/main/scala/pl/edu/agh/school/algorithm/ForminMovesController.scala @@ -1,11 +1,11 @@ -package pl.edu.agh.formin.algorithm +package pl.edu.agh.school.algorithm import com.avsystem.commons import com.avsystem.commons.SharedExtensions._ import com.avsystem.commons.misc.Opt -import pl.edu.agh.formin.config.ForminConfig -import pl.edu.agh.formin.model._ -import pl.edu.agh.formin.simulation.ForminMetrics +import pl.edu.agh.school.config.ForminConfig +import pl.edu.agh.school.model._ +import pl.edu.agh.school.simulation.ForminMetrics import pl.edu.agh.xinuk.algorithm.MovesController import pl.edu.agh.xinuk.model._ diff --git a/formin/src/main/scala/pl/edu/agh/formin/config/ForminConfig.scala b/formin/src/main/scala/pl/edu/agh/school/config/ForminConfig.scala similarity index 98% rename from formin/src/main/scala/pl/edu/agh/formin/config/ForminConfig.scala rename to formin/src/main/scala/pl/edu/agh/school/config/ForminConfig.scala index 5f890b57..bfa07dd5 100644 --- a/formin/src/main/scala/pl/edu/agh/formin/config/ForminConfig.scala +++ b/formin/src/main/scala/pl/edu/agh/school/config/ForminConfig.scala @@ -1,4 +1,4 @@ -package pl.edu.agh.formin.config +package pl.edu.agh.school.config import pl.edu.agh.xinuk.config.{GuiType, XinukConfig} import pl.edu.agh.xinuk.model.{Energy, Signal, SignalVector} diff --git a/formin/src/main/scala/pl/edu/agh/formin/model/Algae.scala b/formin/src/main/scala/pl/edu/agh/school/model/Algae.scala similarity index 94% rename from formin/src/main/scala/pl/edu/agh/formin/model/Algae.scala rename to formin/src/main/scala/pl/edu/agh/school/model/Algae.scala index 39bdc7f4..1a542b29 100644 --- a/formin/src/main/scala/pl/edu/agh/formin/model/Algae.scala +++ b/formin/src/main/scala/pl/edu/agh/school/model/Algae.scala @@ -1,6 +1,6 @@ -package pl.edu.agh.formin.model +package pl.edu.agh.school.model -import pl.edu.agh.formin.config.ForminConfig +import pl.edu.agh.school.config.ForminConfig import pl.edu.agh.xinuk.model.Cell.SmellArray import pl.edu.agh.xinuk.model.{BufferCell, EmptyCell, GridPart, SmellingCell} import pl.edu.agh.xinuk.model.SignalVector.SignalVectorOps diff --git a/formin/src/main/scala/pl/edu/agh/formin/model/Foraminifera.scala b/formin/src/main/scala/pl/edu/agh/school/model/Foraminifera.scala similarity index 96% rename from formin/src/main/scala/pl/edu/agh/formin/model/Foraminifera.scala rename to formin/src/main/scala/pl/edu/agh/school/model/Foraminifera.scala index 586a7f5f..f8d87552 100644 --- a/formin/src/main/scala/pl/edu/agh/formin/model/Foraminifera.scala +++ b/formin/src/main/scala/pl/edu/agh/school/model/Foraminifera.scala @@ -1,6 +1,6 @@ -package pl.edu.agh.formin.model +package pl.edu.agh.school.model -import pl.edu.agh.formin.config.ForminConfig +import pl.edu.agh.school.config.ForminConfig import pl.edu.agh.xinuk.model.Cell.SmellArray import pl.edu.agh.xinuk.model._ import pl.edu.agh.xinuk.model.SignalVector.SignalVectorOps diff --git a/formin/src/main/scala/pl/edu/agh/formin/model/parallel/ForminConflictResolver.scala b/formin/src/main/scala/pl/edu/agh/school/model/parallel/ForminConflictResolver.scala similarity index 92% rename from formin/src/main/scala/pl/edu/agh/formin/model/parallel/ForminConflictResolver.scala rename to formin/src/main/scala/pl/edu/agh/school/model/parallel/ForminConflictResolver.scala index 3de86c1c..4137fad3 100644 --- a/formin/src/main/scala/pl/edu/agh/formin/model/parallel/ForminConflictResolver.scala +++ b/formin/src/main/scala/pl/edu/agh/school/model/parallel/ForminConflictResolver.scala @@ -1,8 +1,8 @@ -package pl.edu.agh.formin.model.parallel +package pl.edu.agh.school.model.parallel -import pl.edu.agh.formin.config.ForminConfig -import pl.edu.agh.formin.model._ -import pl.edu.agh.formin.simulation.ForminMetrics +import pl.edu.agh.school.config.ForminConfig +import pl.edu.agh.school.model._ +import pl.edu.agh.school.simulation.ForminMetrics import pl.edu.agh.xinuk.model._ import pl.edu.agh.xinuk.model.parallel.ConflictResolver diff --git a/formin/src/main/scala/pl/edu/agh/formin/simulation/ForminMetrics.scala b/formin/src/main/scala/pl/edu/agh/school/simulation/ForminMetrics.scala similarity index 98% rename from formin/src/main/scala/pl/edu/agh/formin/simulation/ForminMetrics.scala rename to formin/src/main/scala/pl/edu/agh/school/simulation/ForminMetrics.scala index 6763c455..f7f5500f 100644 --- a/formin/src/main/scala/pl/edu/agh/formin/simulation/ForminMetrics.scala +++ b/formin/src/main/scala/pl/edu/agh/school/simulation/ForminMetrics.scala @@ -1,4 +1,4 @@ -package pl.edu.agh.formin.simulation +package pl.edu.agh.school.simulation import pl.edu.agh.xinuk.simulation.Metrics diff --git a/formin/src/test/scala/pl.edu.agh.formin/GridTest.scala b/formin/src/test/scala/school/GridTest.scala similarity index 98% rename from formin/src/test/scala/pl.edu.agh.formin/GridTest.scala rename to formin/src/test/scala/school/GridTest.scala index f1fe3cb5..3be8617a 100644 --- a/formin/src/test/scala/pl.edu.agh.formin/GridTest.scala +++ b/formin/src/test/scala/school/GridTest.scala @@ -1,8 +1,8 @@ -package pl.edu.agh.formin +package pl.edu.agh.school import org.scalatest.{BeforeAndAfter, FlatSpecLike, Matchers} -import pl.edu.agh.formin.config.ForminConfig -import pl.edu.agh.formin.model._ +import pl.edu.agh.school.config.ForminConfig +import pl.edu.agh.school.model._ import pl.edu.agh.xinuk.config.GuiType import pl.edu.agh.xinuk.model._ diff --git a/formin/src/test/scala/pl.edu.agh.formin/MovesControllerTest.scala b/formin/src/test/scala/school/MovesControllerTest.scala similarity index 96% rename from formin/src/test/scala/pl.edu.agh.formin/MovesControllerTest.scala rename to formin/src/test/scala/school/MovesControllerTest.scala index c679b2cb..011ac6b0 100644 --- a/formin/src/test/scala/pl.edu.agh.formin/MovesControllerTest.scala +++ b/formin/src/test/scala/school/MovesControllerTest.scala @@ -1,10 +1,10 @@ -package pl.edu.agh.formin +package pl.edu.agh.school import com.avsystem.commons.misc.Opt import org.scalatest.{BeforeAndAfter, FlatSpecLike, Matchers} -import pl.edu.agh.formin.algorithm.ForminMovesController -import pl.edu.agh.formin.config.ForminConfig -import pl.edu.agh.formin.model.ForaminiferaAccessible +import pl.edu.agh.school.algorithm.ForminMovesController +import pl.edu.agh.school.config.ForminConfig +import pl.edu.agh.school.model.ForaminiferaAccessible import pl.edu.agh.xinuk.config.GuiType import pl.edu.agh.xinuk.model._ diff --git a/formin/src/test/scala/pl.edu.agh.formin/ParallelTest.scala b/formin/src/test/scala/school/ParallelTest.scala similarity index 98% rename from formin/src/test/scala/pl.edu.agh.formin/ParallelTest.scala rename to formin/src/test/scala/school/ParallelTest.scala index 77a2e416..f2cbc139 100644 --- a/formin/src/test/scala/pl.edu.agh.formin/ParallelTest.scala +++ b/formin/src/test/scala/school/ParallelTest.scala @@ -1,13 +1,13 @@ -package pl.edu.agh.formin +package pl.edu.agh.school import akka.actor.ActorSystem import akka.testkit.{TestActorRef, TestProbe} import org.scalatest.concurrent.{Eventually, ScalaFutures} import org.scalatest.{FlatSpec, Ignore, Matchers} -import pl.edu.agh.formin.algorithm.ForminMovesController -import pl.edu.agh.formin.config.ForminConfig -import pl.edu.agh.formin.model.parallel.ForminConflictResolver -import pl.edu.agh.formin.model.{AlgaeAccessible, AlgaeCell, ForaminiferaAccessible, ForaminiferaCell} +import pl.edu.agh.school.algorithm.ForminMovesController +import pl.edu.agh.school.config.ForminConfig +import pl.edu.agh.school.model.parallel.ForminConflictResolver +import pl.edu.agh.school.model.{AlgaeAccessible, AlgaeCell, ForaminiferaAccessible, ForaminiferaCell} import pl.edu.agh.xinuk.config.GuiType import pl.edu.agh.xinuk.model._ import pl.edu.agh.xinuk.model.parallel.{Neighbour, NeighbourPosition} diff --git a/formin/src/test/scala/pl.edu.agh.formin/WorkerActorTest.scala b/formin/src/test/scala/school/WorkerActorTest.scala similarity index 95% rename from formin/src/test/scala/pl.edu.agh.formin/WorkerActorTest.scala rename to formin/src/test/scala/school/WorkerActorTest.scala index 5a37a3f8..6c2f1a11 100644 --- a/formin/src/test/scala/pl.edu.agh.formin/WorkerActorTest.scala +++ b/formin/src/test/scala/school/WorkerActorTest.scala @@ -1,12 +1,12 @@ -package pl.edu.agh.formin +package pl.edu.agh.school import akka.actor.ActorSystem import akka.testkit.TestProbe import org.scalatest.concurrent.{Eventually, PatienceConfiguration, ScalaFutures} import org.scalatest.{FlatSpecLike, Matchers} -import pl.edu.agh.formin.algorithm.ForminMovesController -import pl.edu.agh.formin.config.ForminConfig -import pl.edu.agh.formin.model.parallel.ForminConflictResolver +import pl.edu.agh.school.algorithm.ForminMovesController +import pl.edu.agh.school.config.ForminConfig +import pl.edu.agh.school.model.parallel.ForminConflictResolver import pl.edu.agh.xinuk.config.GuiType import pl.edu.agh.xinuk.model._ import pl.edu.agh.xinuk.model.parallel.{Neighbour, NeighbourPosition} diff --git a/school/src/main/resources/reference.conf b/school/src/main/resources/reference.conf new file mode 100644 index 00000000..14caa72a --- /dev/null +++ b/school/src/main/resources/reference.conf @@ -0,0 +1,48 @@ +application { + name = school +} + +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.school.model.AlgaeCell", + "pl.edu.agh.school.model.ForaminiferaCell", + "pl.edu.agh.school.simulation.SchoolMetrics", + ] +} + +school { + config { + foraminiferaStartEnergy = 0.5 + foraminiferaReproductionCost = 0.5 + foraminiferaReproductionThreshold = 1 + foraminiferaLifeActivityCost = 0.1 + algaeReproductionFrequency = 2 + algaeEnergeticCapacity = 0.6 + signalSpeedRatio = 2 + signalSuppressionFactor = 0.5 + signalAttenuationFactor = 1 + gridSize = 50 + spawnChance = 0.1 + foraminiferaSpawnChance = 0.3 + foraminiferaInitialSignal = [-1, 0] + foraminiferaPursuedSignalIndex = 0 + algaeInitialSignal = [1, 0] + algaePursuedSignalIndex = 0 + guiType = basic + guiCellSize = 4 + workersRoot = 2 + iterationsNumber = 10000 + isSupervisor = true + shardingMod = 144 + } +} \ No newline at end of file diff --git a/school/src/main/scala/pl/edu/agh/school/SchoolMain.scala b/school/src/main/scala/pl/edu/agh/school/SchoolMain.scala new file mode 100644 index 00000000..46a274b1 --- /dev/null +++ b/school/src/main/scala/pl/edu/agh/school/SchoolMain.scala @@ -0,0 +1,43 @@ +package pl.edu.agh.school + +import java.awt.Color + +import com.typesafe.scalalogging.LazyLogging +import pl.edu.agh.school.algorithm.SchoolMovesController +import pl.edu.agh.school.config.SchoolConfig +import pl.edu.agh.school.model.parallel.SchoolConflictResolver +import pl.edu.agh.school.model.{AlgaeCell, ForaminiferaCell} +import pl.edu.agh.xinuk.Simulation +import pl.edu.agh.xinuk.model.{DefaultSmellPropagation, SmellingCell} + +object SchoolMain extends LazyLogging { + private val configPrefix = "formin" + private val metricHeaders = Vector( // TODO metric headers (it's just for information, but nonetheless...) + "foraminiferaCount", + "algaeCount", + "foraminiferaDeaths", + "foraminiferaTotalEnergy", + "foraminiferaReproductionsCount", + "consumedAlgaeCount", + "foraminiferaTotalLifespan", + "algaeTotalLifespan" + ) + + private def cellToColor(cell: SmellingCell): Color = { + cell match { + case AlgaeCell(_, _) => new Color(0, 128, 0) + case ForaminiferaCell(_, _, _, _) => new Color(139, 69, 19) + case _ => Color.WHITE + } + } + + def main(args: Array[String]): Unit = { + import pl.edu.agh.xinuk.config.ValueReaders._ + new Simulation[SchoolConfig](configPrefix, metricHeaders, SchoolConflictResolver, + DefaultSmellPropagation.calculateSmellAddendsStandard)(new SchoolMovesController(_)(_), + { case cell: SmellingCell => cellToColor(cell) } + ).start() + } + +} + diff --git a/school/src/main/scala/pl/edu/agh/school/algorithm/SchoolMovesController.scala b/school/src/main/scala/pl/edu/agh/school/algorithm/SchoolMovesController.scala new file mode 100644 index 00000000..4dc65222 --- /dev/null +++ b/school/src/main/scala/pl/edu/agh/school/algorithm/SchoolMovesController.scala @@ -0,0 +1,198 @@ +package pl.edu.agh.school.algorithm + +import com.avsystem.commons +import com.avsystem.commons.SharedExtensions._ +import com.avsystem.commons.misc.Opt +import pl.edu.agh.school.config.SchoolConfig +import pl.edu.agh.school.model._ +import pl.edu.agh.school.simulation.SchoolMetrics +import pl.edu.agh.xinuk.algorithm.MovesController +import pl.edu.agh.xinuk.model._ + +import scala.collection.immutable.TreeSet +import scala.util.Random + +final class SchoolMovesController(bufferZone: TreeSet[(Int, Int)])(implicit config: SchoolConfig) extends MovesController { + + private var grid: Grid = _ + + private val random = new Random(System.nanoTime()) + + override def initialGrid: (Grid, SchoolMetrics) = { + grid = Grid.empty(bufferZone) + var foraminiferaCount = 0L + var algaeCount = 0L + for { + x <- 0 until config.gridSize + y <- 0 until config.gridSize + if x != 0 && y != 0 && x != config.gridSize - 1 && y != config.gridSize - 1 + } { + if (random.nextDouble() < config.spawnChance) { + grid.cells(x)(y) = + if (random.nextDouble() < config.foraminiferaSpawnChance) { + foraminiferaCount += 1 + ForaminiferaAccessible.unapply(EmptyCell.Instance).withForaminifera(config.foraminiferaStartEnergy, 0) + } + else { + algaeCount += 1 + AlgaeAccessible.unapply(EmptyCell.Instance).withAlgae(0) + } + } + } + val metrics = SchoolMetrics(foraminiferaCount, algaeCount, 0, config.foraminiferaStartEnergy.value * foraminiferaCount, 0, 0, 0, 0) + (grid, metrics) + } + + + def calculatePossibleDestinations(cell: ForaminiferaCell, x: Int, y: Int, grid: Grid): Iterator[(Int, Int, GridPart)] = { + val neighbourCellCoordinates = Grid.neighbourCellCoordinates(x, y) + Grid.SubcellCoordinates + .map { case (i, j) => cell.smell(i)(j) } + .zipWithIndex + .map { + case (signalVector, index) => (signalVector(cell.pursuedSignalIndex), index) + } + .sorted(implicitly[Ordering[(Signal, Int)]].reverse) + .iterator + .map { case (_, idx) => + val (i, j) = neighbourCellCoordinates(idx) + (i, j, grid.cells(i)(j)) + } + } + + def selectDestinationCell(possibleDestinations: Iterator[(Int, Int, GridPart)], newGrid: Grid): commons.Opt[(Int, Int, GridPart)] = { + possibleDestinations + .map { case (i, j, current) => (i, j, current, newGrid.cells(i)(j)) } + .collectFirstOpt { + case (i, j, currentCell@ForaminiferaAccessible(_), ForaminiferaAccessible(_)) => + (i, j, currentCell) + } + } + + override def makeMoves(iteration: Long, grid: Grid): (Grid, SchoolMetrics) = { + this.grid = grid + val newGrid = Grid.empty(bufferZone) + + var foraminiferaCount = 0L + var algaeCount = 0L + var foraminiferaDeaths = 0L + var foraminiferaReproductionsCount = 0L + var consumedAlgaeCount = 0L + var foraminiferaTotalLifespan = 0L + var algaeTotalLifespan = 0L + var foraminiferaTotalEnergy = 0.0 + + def isEmptyIn(grid: Grid)(i: Int, j: Int): Boolean = { + grid.cells(i)(j) match { + case EmptyCell(_) | BufferCell(EmptyCell(_)) => true + case _ => false + } + } + + def reproduce(x: Int, y: Int)(creator: PartialFunction[GridPart, GridPart]): Unit = { + val emptyCells = + Grid.neighbourCellCoordinates(x, y).flatMap { + case (i, j) => + grid.cells(i)(j).opt + .filter(_ => creator.isDefinedAt(newGrid.cells(i)(j))) //use the same availability criteria on new grid + .collect(creator) + .map((i, j, _)) + } + if (emptyCells.nonEmpty) { + val (newAlgaeX, newAlgaeY, newCell) = emptyCells(random.nextInt(emptyCells.size)) + newGrid.cells(newAlgaeX)(newAlgaeY) = newCell + } + } + + def makeMove(x: Int, y: Int): Unit = { + this.grid.cells(x)(y) match { + case Obstacle => + newGrid.cells(x)(y) = Obstacle + case cell@(EmptyCell(_) | BufferCell(_)) => + if (isEmptyIn(newGrid)(x, y)) { + newGrid.cells(x)(y) = cell + } + case cell: AlgaeCell => + if (iteration % config.algaeReproductionFrequency == 0) { + reproduce(x, y) { case AlgaeAccessible(accessible) => accessible.withAlgae(0) } + } + if (isEmptyIn(newGrid)(x, y)) { + newGrid.cells(x)(y) = cell.copy(lifespan = cell.lifespan + 1) + } + case cell: ForaminiferaCell => + if (cell.energy < config.foraminiferaLifeActivityCost) { + killForaminifera(cell, x, y) + } else if (cell.energy > config.foraminiferaReproductionThreshold) { + reproduceForaminifera(cell, x, y) + } else { + moveForaminifera(cell, x, y) + } + } + } + + def killForaminifera(cell: ForaminiferaCell, x: Int, y: Int): Unit = { + foraminiferaDeaths += 1 + foraminiferaTotalLifespan += cell.lifespan + val vacated = EmptyCell(cell.smell) + newGrid.cells(x)(y) = vacated + grid.cells(x)(y) = vacated + } + + def reproduceForaminifera(cell: ForaminiferaCell, x: Int, y: Int): Unit = { + reproduce(x, y) { case ForaminiferaAccessible(accessible) => accessible.withForaminifera(config.foraminiferaStartEnergy, 0) } + newGrid.cells(x)(y) = cell.copy(energy = cell.energy - config.foraminiferaReproductionCost, lifespan = cell.lifespan + 1) + foraminiferaReproductionsCount += 1 + } + + def moveForaminifera(cell: ForaminiferaCell, x: Int, y: Int): Unit = { + val destinations = calculatePossibleDestinations(cell, x, y, grid) + val destination = selectDestinationCell(destinations, newGrid) + destination match { + case Opt((_, _, AlgaeCell(_, lifespan))) => + consumedAlgaeCount += 1 + algaeTotalLifespan += lifespan + case Opt((_, _, BufferCell(AlgaeCell(_, lifespan)))) => + consumedAlgaeCount += 1 + algaeTotalLifespan += lifespan + case _ => + } + destination match { + case Opt((i, j, ForaminiferaAccessible(destination))) => + newGrid.cells(i)(j) = destination.withForaminifera(cell.energy - config.foraminiferaLifeActivityCost, cell.lifespan + 1) + val vacated = EmptyCell(cell.smell) + newGrid.cells(x)(y) = vacated + grid.cells(x)(y) = vacated + case Opt((i, j, inaccessibleDestination)) => + throw new RuntimeException(s"Foraminifera selected inaccessible destination ($i,$j): $inaccessibleDestination") + case Opt.Empty => + newGrid.cells(x)(y) = cell.copy(cell.energy - config.foraminiferaLifeActivityCost, lifespan = cell.lifespan + 1) + grid.cells(x)(y) + } + } + + for { + x <- 0 until config.gridSize + y <- 0 until config.gridSize + } { + this.grid.cells(x)(y) match { + case ForaminiferaCell(energy, _, _, _) => + foraminiferaTotalEnergy += energy.value + foraminiferaCount += 1 + case BufferCell(ForaminiferaCell(energy, _, _, _)) => + foraminiferaTotalEnergy += energy.value + foraminiferaCount += 1 + case AlgaeCell(_, _) | BufferCell(AlgaeCell(_, _)) => + algaeCount += 1 + case _ => + } + } + + for { + x <- 0 until config.gridSize + y <- 0 until config.gridSize + } makeMove(x, y) + + val metrics = SchoolMetrics(foraminiferaCount, algaeCount, foraminiferaDeaths, foraminiferaTotalEnergy, foraminiferaReproductionsCount, consumedAlgaeCount, foraminiferaTotalLifespan, algaeTotalLifespan) + (newGrid, metrics) + } +} \ No newline at end of file diff --git a/school/src/main/scala/pl/edu/agh/school/config/SchoolConfig.scala b/school/src/main/scala/pl/edu/agh/school/config/SchoolConfig.scala new file mode 100644 index 00000000..e5f02b4a --- /dev/null +++ b/school/src/main/scala/pl/edu/agh/school/config/SchoolConfig.scala @@ -0,0 +1,45 @@ +package pl.edu.agh.school.config + +import pl.edu.agh.xinuk.config.{GuiType, XinukConfig} +import pl.edu.agh.xinuk.model.{Energy, Signal, SignalVector} + +/* +FSE - foraminifera start energy; FSE ∈ [0,1] && FSE ∈ R. +FRC - foraminifera reproduction cost; FRC ∈ [0,1] && FRC ∈ R. +FRT - foraminifera reproduction threshold; FRT ∈ [0,1] && FRT ∈ R. +FLAC - foraminifera life activities(vegetation and movement) cost; FLAC ∈ [0,1] && FLAC ∈ R. +ARF - algae reproduction frequency; ARF ∈ N. +AEC - algae energetic capacity; AEC ∈ [0,1] && AEC ∈ R. +SSR - signal speed ratio; SSR ∈ N. Foraminifera speed is 1. +SPF - global suppression factor of the signal; SPF ∈ [0,1] && SPF ∈ R. +GS - grid size; GS ∈ N, where map size is GSxGS. +SC - spawn chance, SC ∈ [0,1] && SC ∈ R +FSC - foraminifera spawn chance; FAR ∈ N. +FSSV - foraminifera start signal value; FSSV ∈ [0,1] && FSSV ∈ R. +ASSV - algae start signal value; ASSV ∈ [0,1] && ASSV ∈ R. + */ + +final case class SchoolConfig( + foraminiferaStartEnergy: Energy, + foraminiferaReproductionCost: Energy, + foraminiferaReproductionThreshold: Energy, + foraminiferaLifeActivityCost: Energy, + algaeReproductionFrequency: Int, + algaeEnergeticCapacity: Energy, + signalSpeedRatio: Int, + signalSuppressionFactor: Double, + signalAttenuationFactor: Double, + gridSize: Int, + spawnChance: Double, + foraminiferaSpawnChance: Double, + foraminiferaInitialSignal: List[Signal], + foraminiferaPursuedSignalIndex: Int, + algaeInitialSignal: List[Signal], + algaePursuedSignalIndex: Int, + guiType: GuiType, + guiCellSize: Int, + workersRoot: Int, + iterationsNumber: Long, + isSupervisor: Boolean, + shardingMod: Int + ) extends XinukConfig diff --git a/school/src/main/scala/pl/edu/agh/school/model/Algae.scala b/school/src/main/scala/pl/edu/agh/school/model/Algae.scala new file mode 100644 index 00000000..e17c6004 --- /dev/null +++ b/school/src/main/scala/pl/edu/agh/school/model/Algae.scala @@ -0,0 +1,35 @@ +package pl.edu.agh.school.model + +import pl.edu.agh.school.config.SchoolConfig +import pl.edu.agh.xinuk.model.Cell.SmellArray +import pl.edu.agh.xinuk.model.{BufferCell, EmptyCell, GridPart, SmellingCell} +import pl.edu.agh.xinuk.model.SignalVector.SignalVectorOps + +final case class AlgaeCell(smell: SmellArray, lifespan: Long) extends SmellingCell { + override type Self = AlgaeCell + + override def withSmell(smell: SmellArray): AlgaeCell = copy(smell = smell) +} + +trait AlgaeAccessible[+T <: GridPart] { + def withAlgae(lifespan: Long): T +} + +object AlgaeAccessible { + + def unapply(arg: EmptyCell)(implicit config: SchoolConfig): AlgaeAccessible[AlgaeCell] = + new AlgaeAccessible[AlgaeCell] { + override def withAlgae(lifespan: Long): AlgaeCell = AlgaeCell(arg.smellWith(config.algaeInitialSignal.toSignalVector), lifespan) + } + + def unapply(arg: BufferCell)(implicit config: SchoolConfig): AlgaeAccessible[BufferCell] = + new AlgaeAccessible[BufferCell] { + override def withAlgae(lifespan: Long): BufferCell = BufferCell(AlgaeCell(arg.smellWith(config.algaeInitialSignal.toSignalVector), lifespan)) + } + + def unapply(arg: GridPart)(implicit config: SchoolConfig): Option[AlgaeAccessible[GridPart]] = arg match { + case cell: EmptyCell => Some(unapply(cell)) + case cell: BufferCell => Some(unapply(cell)) + case _ => None + } +} diff --git a/school/src/main/scala/pl/edu/agh/school/model/Foraminifera.scala b/school/src/main/scala/pl/edu/agh/school/model/Foraminifera.scala new file mode 100644 index 00000000..15c637ac --- /dev/null +++ b/school/src/main/scala/pl/edu/agh/school/model/Foraminifera.scala @@ -0,0 +1,41 @@ +package pl.edu.agh.school.model + +import pl.edu.agh.school.config.SchoolConfig +import pl.edu.agh.xinuk.model.Cell.SmellArray +import pl.edu.agh.xinuk.model._ +import pl.edu.agh.xinuk.model.SignalVector.SignalVectorOps + +final case class ForaminiferaCell(energy: Energy, smell: SmellArray, lifespan: Long, pursuedSignalIndex: Int) extends SmellingCell { + override type Self = ForaminiferaCell + + override def withSmell(smell: SmellArray): ForaminiferaCell = copy(smell = smell) +} + +trait ForaminiferaAccessible[+T <: GridPart] { + def withForaminifera(energy: Energy, lifespan: Long): T +} + +object ForaminiferaAccessible { + + def unapply(arg: AlgaeCell)(implicit config: SchoolConfig): ForaminiferaAccessible[ForaminiferaCell] = + new ForaminiferaAccessible[ForaminiferaCell] { + override def withForaminifera(energy: Energy, lifespan: Long): ForaminiferaCell = ForaminiferaCell(energy + config.algaeEnergeticCapacity, arg.smellWith(config.foraminiferaInitialSignal.toSignalVector), lifespan, config.foraminiferaPursuedSignalIndex) + } + + def unapply(arg: EmptyCell)(implicit config: SchoolConfig): ForaminiferaAccessible[ForaminiferaCell] = + new ForaminiferaAccessible[ForaminiferaCell] { + override def withForaminifera(energy: Energy, lifespan: Long): ForaminiferaCell = ForaminiferaCell(energy, arg.smellWith(config.foraminiferaInitialSignal.toSignalVector), lifespan, config.foraminiferaPursuedSignalIndex) + } + + def unapply(arg: BufferCell)(implicit config: SchoolConfig): ForaminiferaAccessible[BufferCell] = + new ForaminiferaAccessible[BufferCell] { + override def withForaminifera(energy: Energy, lifespan: Long): BufferCell = BufferCell(ForaminiferaCell(energy, arg.smellWith(config.foraminiferaInitialSignal.toSignalVector), lifespan, config.foraminiferaPursuedSignalIndex)) + } + + def unapply(arg: GridPart)(implicit config: SchoolConfig): Option[ForaminiferaAccessible[GridPart]] = arg match { + case cell: AlgaeCell => Some(unapply(cell)) + case cell: EmptyCell => Some(unapply(cell)) + case cell: BufferCell => Some(unapply(cell)) + case _ => None + } +} diff --git a/school/src/main/scala/pl/edu/agh/school/model/parallel/SchoolConflictResolver.scala b/school/src/main/scala/pl/edu/agh/school/model/parallel/SchoolConflictResolver.scala new file mode 100644 index 00000000..254d6ebc --- /dev/null +++ b/school/src/main/scala/pl/edu/agh/school/model/parallel/SchoolConflictResolver.scala @@ -0,0 +1,31 @@ +package pl.edu.agh.school.model.parallel + +import pl.edu.agh.school.config.SchoolConfig +import pl.edu.agh.school.model._ +import pl.edu.agh.school.simulation.SchoolMetrics +import pl.edu.agh.xinuk.model._ +import pl.edu.agh.xinuk.model.parallel.ConflictResolver + +object SchoolConflictResolver extends ConflictResolver[SchoolConfig] { + + import Cell._ + + override def resolveConflict(current: GridPart, incoming: SmellingCell)(implicit config: SchoolConfig): (GridPart, SchoolMetrics) = { + (current, incoming) match { + case (EmptyCell(currentSmell), incomingCell) => + (incomingCell.withSmell(incomingCell.smell + currentSmell), SchoolMetrics.empty()) + case (currentCell: SmellingCell, EmptyCell(incomingSmell)) => + (currentCell.withSmell(currentCell.smell + incomingSmell), SchoolMetrics.empty()) + case (AlgaeCell(currentSmell, currentLifespan), ForaminiferaCell(energy, incomingSmell, incomingLifespan, pursuedSignalIndex)) => + (ForaminiferaCell(energy + config.algaeEnergeticCapacity, incomingSmell + currentSmell, incomingLifespan, pursuedSignalIndex), SchoolMetrics(0, 0, 0, 0, 0, 1, 0, currentLifespan)) + case (ForaminiferaCell(energy, currentSmell, currentLifespan, pursuedSignalIndex), AlgaeCell(incomingSmell, incomingLifespan)) => + (ForaminiferaCell(energy + config.algaeEnergeticCapacity, incomingSmell + currentSmell, currentLifespan, pursuedSignalIndex), SchoolMetrics(0, 0, 0, 0, 0, 1, 0, incomingLifespan)) + case (AlgaeCell(currentSmell, lifespan), AlgaeCell(incomingSmell, incomingLifespan)) => + (AlgaeCell(currentSmell + incomingSmell, math.max(lifespan, incomingLifespan)), SchoolMetrics.empty()) + case (ForaminiferaCell(currentEnergy, currentSmell, lifespan, _), ForaminiferaCell(incomingEnergy, incomingSmell, incomingLifespan, pursuedSignalIndex)) => // TODO: here are place where two smells can intersect + (ForaminiferaCell(currentEnergy + incomingEnergy, currentSmell + incomingSmell, math.max(lifespan, incomingLifespan), pursuedSignalIndex), SchoolMetrics.empty()) + case (Obstacle, _) => (Obstacle, SchoolMetrics.empty()) + case (x, y) => throw new UnsupportedOperationException(s"Unresolved conflict: $x with $y") + } + } +} diff --git a/school/src/main/scala/pl/edu/agh/school/simulation/SchoolMetrics.scala b/school/src/main/scala/pl/edu/agh/school/simulation/SchoolMetrics.scala new file mode 100644 index 00000000..7252d3fe --- /dev/null +++ b/school/src/main/scala/pl/edu/agh/school/simulation/SchoolMetrics.scala @@ -0,0 +1,44 @@ +package pl.edu.agh.school.simulation + +import pl.edu.agh.xinuk.simulation.Metrics + +final case class SchoolMetrics(foraminiferaCount: Long, + algaeCount: Long, + foraminiferaDeaths: Long, + foraminiferaTotalEnergy: Double, + foraminiferaReproductionsCount: Long, + consumedAlgaeCount: Long, + foraminiferaTotalLifespan: Long, + algaeTotalLifespan: Long) extends Metrics { + + override def log: String = { + s"$foraminiferaCount;$algaeCount;$foraminiferaDeaths;$foraminiferaTotalEnergy;$foraminiferaReproductionsCount;$consumedAlgaeCount;$foraminiferaTotalLifespan;$algaeTotalLifespan" + } + + override def series: Vector[(String, Double)] = Vector( + "Foraminifera" -> foraminiferaCount, + "Algae" -> algaeCount + ) + + override def +(other: Metrics): SchoolMetrics = { + other match { + case SchoolMetrics.EMPTY => this + case SchoolMetrics(otherForaminiferaCount, otherAlgaeCount, otherForaminiferaDeaths, otherForaminiferaTotalEnergy, + otherForaminiferaReproductionsCount, otherConsumedAlgaeCount, otherForaminiferaTotalLifespan, + otherAlgaeTotalLifespan) => + SchoolMetrics(foraminiferaCount + otherForaminiferaCount, algaeCount + otherAlgaeCount, + foraminiferaDeaths + otherForaminiferaDeaths, foraminiferaTotalEnergy + otherForaminiferaTotalEnergy, + foraminiferaReproductionsCount + otherForaminiferaReproductionsCount, + consumedAlgaeCount + otherConsumedAlgaeCount, foraminiferaTotalLifespan + otherForaminiferaTotalLifespan, + algaeTotalLifespan + otherAlgaeTotalLifespan) + case null => this + case _ => throw new UnsupportedOperationException(s"Cannot add: non-ForminMetrics to ForminMetrics") + } + } +} + +object SchoolMetrics { + private val EMPTY = SchoolMetrics(0, 0, 0, 0, 0, 0, 0, 0) + + def empty(): SchoolMetrics = EMPTY +} \ No newline at end of file From 222856e30bba902329f3714a3d402e85daf01555 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Zieli=C5=84ski?= Date: Sun, 7 Apr 2019 22:03:03 +0200 Subject: [PATCH 04/10] Fix code to be able to run simulation --- school/src/main/resources/reference.conf | 18 +++++--- .../scala/pl/edu/agh/school/SchoolMain.scala | 11 +++-- .../algorithm/SchoolMovesController.scala | 24 +++++------ .../edu/agh/school/config/SchoolConfig.scala | 12 ++++-- .../pl/edu/agh/school/model/Cleaner.scala | 41 +++++++++++++++++++ .../scala/pl/edu/agh/school/model/Dirt.scala | 41 +++++++++++++++++++ .../edu/agh/school/model/Foraminifera.scala | 41 ------------------- .../model/{Algae.scala => Student.scala} | 14 +++---- .../pl/edu/agh/school/model/Teacher.scala | 41 +++++++++++++++++++ .../scala/pl/edu/agh/school/model/Wall.scala | 41 +++++++++++++++++++ .../parallel/SchoolConflictResolver.scala | 16 ++++---- 11 files changed, 218 insertions(+), 82 deletions(-) create mode 100644 school/src/main/scala/pl/edu/agh/school/model/Cleaner.scala create mode 100644 school/src/main/scala/pl/edu/agh/school/model/Dirt.scala delete mode 100644 school/src/main/scala/pl/edu/agh/school/model/Foraminifera.scala rename school/src/main/scala/pl/edu/agh/school/model/{Algae.scala => Student.scala} (60%) create mode 100644 school/src/main/scala/pl/edu/agh/school/model/Teacher.scala create mode 100644 school/src/main/scala/pl/edu/agh/school/model/Wall.scala diff --git a/school/src/main/resources/reference.conf b/school/src/main/resources/reference.conf index 14caa72a..11f7cbbb 100644 --- a/school/src/main/resources/reference.conf +++ b/school/src/main/resources/reference.conf @@ -14,8 +14,8 @@ clustering { xinuk { classes = [ - "pl.edu.agh.school.model.AlgaeCell", - "pl.edu.agh.school.model.ForaminiferaCell", + "pl.edu.agh.school.model.StudentCell", + "pl.edu.agh.school.model.DirtCell", "pl.edu.agh.school.simulation.SchoolMetrics", ] } @@ -34,10 +34,16 @@ school { gridSize = 50 spawnChance = 0.1 foraminiferaSpawnChance = 0.3 - foraminiferaInitialSignal = [-1, 0] - foraminiferaPursuedSignalIndex = 0 - algaeInitialSignal = [1, 0] - algaePursuedSignalIndex = 0 + + cleanerInitialSignal: [-1, 0] + cleanerInitialSignalIndex: 0 + dirtInitialSignal: [1, 0] + dirtSignalIndex: 0 + studentInitialSignal: [0, 1] + studentSignalIndex: 1 + teacherInitialSignal: [0, -1] + teacherSignalIndex: 1 + guiType = basic guiCellSize = 4 workersRoot = 2 diff --git a/school/src/main/scala/pl/edu/agh/school/SchoolMain.scala b/school/src/main/scala/pl/edu/agh/school/SchoolMain.scala index 46a274b1..0090327c 100644 --- a/school/src/main/scala/pl/edu/agh/school/SchoolMain.scala +++ b/school/src/main/scala/pl/edu/agh/school/SchoolMain.scala @@ -6,12 +6,12 @@ import com.typesafe.scalalogging.LazyLogging import pl.edu.agh.school.algorithm.SchoolMovesController import pl.edu.agh.school.config.SchoolConfig import pl.edu.agh.school.model.parallel.SchoolConflictResolver -import pl.edu.agh.school.model.{AlgaeCell, ForaminiferaCell} +import pl.edu.agh.school.model._ import pl.edu.agh.xinuk.Simulation import pl.edu.agh.xinuk.model.{DefaultSmellPropagation, SmellingCell} object SchoolMain extends LazyLogging { - private val configPrefix = "formin" + private val configPrefix = "school" private val metricHeaders = Vector( // TODO metric headers (it's just for information, but nonetheless...) "foraminiferaCount", "algaeCount", @@ -25,8 +25,11 @@ object SchoolMain extends LazyLogging { private def cellToColor(cell: SmellingCell): Color = { cell match { - case AlgaeCell(_, _) => new Color(0, 128, 0) - case ForaminiferaCell(_, _, _, _) => new Color(139, 69, 19) + case CleanerCell(_, _, _, _) => new Color(66, 134, 244) + case DirtCell(_, _, _, _) => new Color(132, 86, 7) + case StudentCell(_, _, _) => new Color(16, 234, 23) + case TeacherCell(_, _, _, _) => new Color(132, 7, 7) + case WallCell(_) => new Color(135, 135, 135) case _ => Color.WHITE } } diff --git a/school/src/main/scala/pl/edu/agh/school/algorithm/SchoolMovesController.scala b/school/src/main/scala/pl/edu/agh/school/algorithm/SchoolMovesController.scala index 4dc65222..8e01c46b 100644 --- a/school/src/main/scala/pl/edu/agh/school/algorithm/SchoolMovesController.scala +++ b/school/src/main/scala/pl/edu/agh/school/algorithm/SchoolMovesController.scala @@ -44,13 +44,13 @@ final class SchoolMovesController(bufferZone: TreeSet[(Int, Int)])(implicit conf } - def calculatePossibleDestinations(cell: ForaminiferaCell, x: Int, y: Int, grid: Grid): Iterator[(Int, Int, GridPart)] = { + def calculatePossibleDestinations(cell: DirtCell, x: Int, y: Int, grid: Grid): Iterator[(Int, Int, GridPart)] = { val neighbourCellCoordinates = Grid.neighbourCellCoordinates(x, y) Grid.SubcellCoordinates .map { case (i, j) => cell.smell(i)(j) } .zipWithIndex .map { - case (signalVector, index) => (signalVector(cell.pursuedSignalIndex), index) + case (signalVector, index) => (signalVector(cell.signalIndex), index) } .sorted(implicitly[Ordering[(Signal, Int)]].reverse) .iterator @@ -112,14 +112,14 @@ final class SchoolMovesController(bufferZone: TreeSet[(Int, Int)])(implicit conf if (isEmptyIn(newGrid)(x, y)) { newGrid.cells(x)(y) = cell } - case cell: AlgaeCell => + case cell: StudentCell => if (iteration % config.algaeReproductionFrequency == 0) { reproduce(x, y) { case AlgaeAccessible(accessible) => accessible.withAlgae(0) } } if (isEmptyIn(newGrid)(x, y)) { newGrid.cells(x)(y) = cell.copy(lifespan = cell.lifespan + 1) } - case cell: ForaminiferaCell => + case cell: DirtCell => if (cell.energy < config.foraminiferaLifeActivityCost) { killForaminifera(cell, x, y) } else if (cell.energy > config.foraminiferaReproductionThreshold) { @@ -130,7 +130,7 @@ final class SchoolMovesController(bufferZone: TreeSet[(Int, Int)])(implicit conf } } - def killForaminifera(cell: ForaminiferaCell, x: Int, y: Int): Unit = { + def killForaminifera(cell: DirtCell, x: Int, y: Int): Unit = { foraminiferaDeaths += 1 foraminiferaTotalLifespan += cell.lifespan val vacated = EmptyCell(cell.smell) @@ -138,20 +138,20 @@ final class SchoolMovesController(bufferZone: TreeSet[(Int, Int)])(implicit conf grid.cells(x)(y) = vacated } - def reproduceForaminifera(cell: ForaminiferaCell, x: Int, y: Int): Unit = { + def reproduceForaminifera(cell: DirtCell, x: Int, y: Int): Unit = { reproduce(x, y) { case ForaminiferaAccessible(accessible) => accessible.withForaminifera(config.foraminiferaStartEnergy, 0) } newGrid.cells(x)(y) = cell.copy(energy = cell.energy - config.foraminiferaReproductionCost, lifespan = cell.lifespan + 1) foraminiferaReproductionsCount += 1 } - def moveForaminifera(cell: ForaminiferaCell, x: Int, y: Int): Unit = { + def moveForaminifera(cell: DirtCell, x: Int, y: Int): Unit = { val destinations = calculatePossibleDestinations(cell, x, y, grid) val destination = selectDestinationCell(destinations, newGrid) destination match { - case Opt((_, _, AlgaeCell(_, lifespan))) => + case Opt((_, _, StudentCell(_, lifespan, _))) => consumedAlgaeCount += 1 algaeTotalLifespan += lifespan - case Opt((_, _, BufferCell(AlgaeCell(_, lifespan)))) => + case Opt((_, _, BufferCell(StudentCell(_, lifespan, _)))) => consumedAlgaeCount += 1 algaeTotalLifespan += lifespan case _ => @@ -175,13 +175,13 @@ final class SchoolMovesController(bufferZone: TreeSet[(Int, Int)])(implicit conf y <- 0 until config.gridSize } { this.grid.cells(x)(y) match { - case ForaminiferaCell(energy, _, _, _) => + case DirtCell(energy, _, _, _) => foraminiferaTotalEnergy += energy.value foraminiferaCount += 1 - case BufferCell(ForaminiferaCell(energy, _, _, _)) => + case BufferCell(DirtCell(energy, _, _, _)) => foraminiferaTotalEnergy += energy.value foraminiferaCount += 1 - case AlgaeCell(_, _) | BufferCell(AlgaeCell(_, _)) => + case StudentCell(_, _, _) | BufferCell(StudentCell(_, _, _)) => algaeCount += 1 case _ => } diff --git a/school/src/main/scala/pl/edu/agh/school/config/SchoolConfig.scala b/school/src/main/scala/pl/edu/agh/school/config/SchoolConfig.scala index e5f02b4a..984e73c0 100644 --- a/school/src/main/scala/pl/edu/agh/school/config/SchoolConfig.scala +++ b/school/src/main/scala/pl/edu/agh/school/config/SchoolConfig.scala @@ -32,10 +32,14 @@ final case class SchoolConfig( gridSize: Int, spawnChance: Double, foraminiferaSpawnChance: Double, - foraminiferaInitialSignal: List[Signal], - foraminiferaPursuedSignalIndex: Int, - algaeInitialSignal: List[Signal], - algaePursuedSignalIndex: Int, + cleanerInitialSignal: List[Signal], + cleanerInitialSignalIndex: Int, + dirtInitialSignal: List[Signal], + dirtSignalIndex: Int, + studentInitialSignal: List[Signal], + studentSignalIndex: Int, + teacherInitialSignal: List[Signal], + teacherSignalIndex: Int, guiType: GuiType, guiCellSize: Int, workersRoot: Int, diff --git a/school/src/main/scala/pl/edu/agh/school/model/Cleaner.scala b/school/src/main/scala/pl/edu/agh/school/model/Cleaner.scala new file mode 100644 index 00000000..229ad618 --- /dev/null +++ b/school/src/main/scala/pl/edu/agh/school/model/Cleaner.scala @@ -0,0 +1,41 @@ +package pl.edu.agh.school.model + +import pl.edu.agh.school.config.SchoolConfig +import pl.edu.agh.xinuk.model.Cell.SmellArray +import pl.edu.agh.xinuk.model._ +import pl.edu.agh.xinuk.model.SignalVector.SignalVectorOps + +final case class CleanerCell(energy: Energy, smell: SmellArray, lifespan: Long, pursuedSignalIndex: Int) extends SmellingCell { + override type Self = CleanerCell + + override def withSmell(smell: SmellArray): CleanerCell = copy(smell = smell) +} + +trait ForaminiferaAccessible[+T <: GridPart] { + def withForaminifera(energy: Energy, lifespan: Long): T +} + +object ForaminiferaAccessible { + + def unapply(arg: StudentCell)(implicit config: SchoolConfig): ForaminiferaAccessible[CleanerCell] = + new ForaminiferaAccessible[CleanerCell] { + override def withForaminifera(energy: Energy, lifespan: Long): CleanerCell = CleanerCell(energy + config.algaeEnergeticCapacity, arg.smellWith(config.cleanerInitialSignal.toSignalVector), lifespan, config.cleanerInitialSignalIndex) + } + + def unapply(arg: EmptyCell)(implicit config: SchoolConfig): ForaminiferaAccessible[CleanerCell] = + new ForaminiferaAccessible[CleanerCell] { + override def withForaminifera(energy: Energy, lifespan: Long): CleanerCell = CleanerCell(energy, arg.smellWith(config.cleanerInitialSignal.toSignalVector), lifespan, config.cleanerInitialSignalIndex) + } + + def unapply(arg: BufferCell)(implicit config: SchoolConfig): ForaminiferaAccessible[BufferCell] = + new ForaminiferaAccessible[BufferCell] { + override def withForaminifera(energy: Energy, lifespan: Long): BufferCell = BufferCell(CleanerCell(energy, arg.smellWith(config.cleanerInitialSignal.toSignalVector), lifespan, config.cleanerInitialSignalIndex)) + } + + def unapply(arg: GridPart)(implicit config: SchoolConfig): Option[ForaminiferaAccessible[GridPart]] = arg match { + case cell: StudentCell => Some(unapply(cell)) + case cell: EmptyCell => Some(unapply(cell)) + case cell: BufferCell => Some(unapply(cell)) + case _ => None + } +} diff --git a/school/src/main/scala/pl/edu/agh/school/model/Dirt.scala b/school/src/main/scala/pl/edu/agh/school/model/Dirt.scala new file mode 100644 index 00000000..07a67bc1 --- /dev/null +++ b/school/src/main/scala/pl/edu/agh/school/model/Dirt.scala @@ -0,0 +1,41 @@ +package pl.edu.agh.school.model + +import pl.edu.agh.school.config.SchoolConfig +import pl.edu.agh.xinuk.model.Cell.SmellArray +import pl.edu.agh.xinuk.model._ +import pl.edu.agh.xinuk.model.SignalVector.SignalVectorOps + +final case class DirtCell(energy: Energy, smell: SmellArray, lifespan: Long, signalIndex: Int) extends SmellingCell { + override type Self = DirtCell + + override def withSmell(smell: SmellArray): DirtCell = copy(smell = smell) +} + +trait DirtAccessible[+T <: GridPart] { + def withDirt(energy: Energy, lifespan: Long): T +} + +object DirtAccessible { + + def unapply(arg: StudentCell)(implicit config: SchoolConfig): DirtAccessible[DirtCell] = + new DirtAccessible[DirtCell] { + override def withDirt(energy: Energy, lifespan: Long): DirtCell = DirtCell(energy + config.algaeEnergeticCapacity, arg.smellWith(config.cleanerInitialSignal.toSignalVector), lifespan, config.cleanerInitialSignalIndex) + } + + def unapply(arg: EmptyCell)(implicit config: SchoolConfig): DirtAccessible[DirtCell] = + new DirtAccessible[DirtCell] { + override def withDirt(energy: Energy, lifespan: Long): DirtCell = DirtCell(energy, arg.smellWith(config.cleanerInitialSignal.toSignalVector), lifespan, config.cleanerInitialSignalIndex) + } + + def unapply(arg: BufferCell)(implicit config: SchoolConfig): DirtAccessible[BufferCell] = + new DirtAccessible[BufferCell] { + override def withDirt(energy: Energy, lifespan: Long): BufferCell = BufferCell(DirtCell(energy, arg.smellWith(config.cleanerInitialSignal.toSignalVector), lifespan, config.cleanerInitialSignalIndex)) + } + + def unapply(arg: GridPart)(implicit config: SchoolConfig): Option[DirtAccessible[GridPart]] = arg match { + case cell: StudentCell => Some(unapply(cell)) + case cell: EmptyCell => Some(unapply(cell)) + case cell: BufferCell => Some(unapply(cell)) + case _ => None + } +} diff --git a/school/src/main/scala/pl/edu/agh/school/model/Foraminifera.scala b/school/src/main/scala/pl/edu/agh/school/model/Foraminifera.scala deleted file mode 100644 index 15c637ac..00000000 --- a/school/src/main/scala/pl/edu/agh/school/model/Foraminifera.scala +++ /dev/null @@ -1,41 +0,0 @@ -package pl.edu.agh.school.model - -import pl.edu.agh.school.config.SchoolConfig -import pl.edu.agh.xinuk.model.Cell.SmellArray -import pl.edu.agh.xinuk.model._ -import pl.edu.agh.xinuk.model.SignalVector.SignalVectorOps - -final case class ForaminiferaCell(energy: Energy, smell: SmellArray, lifespan: Long, pursuedSignalIndex: Int) extends SmellingCell { - override type Self = ForaminiferaCell - - override def withSmell(smell: SmellArray): ForaminiferaCell = copy(smell = smell) -} - -trait ForaminiferaAccessible[+T <: GridPart] { - def withForaminifera(energy: Energy, lifespan: Long): T -} - -object ForaminiferaAccessible { - - def unapply(arg: AlgaeCell)(implicit config: SchoolConfig): ForaminiferaAccessible[ForaminiferaCell] = - new ForaminiferaAccessible[ForaminiferaCell] { - override def withForaminifera(energy: Energy, lifespan: Long): ForaminiferaCell = ForaminiferaCell(energy + config.algaeEnergeticCapacity, arg.smellWith(config.foraminiferaInitialSignal.toSignalVector), lifespan, config.foraminiferaPursuedSignalIndex) - } - - def unapply(arg: EmptyCell)(implicit config: SchoolConfig): ForaminiferaAccessible[ForaminiferaCell] = - new ForaminiferaAccessible[ForaminiferaCell] { - override def withForaminifera(energy: Energy, lifespan: Long): ForaminiferaCell = ForaminiferaCell(energy, arg.smellWith(config.foraminiferaInitialSignal.toSignalVector), lifespan, config.foraminiferaPursuedSignalIndex) - } - - def unapply(arg: BufferCell)(implicit config: SchoolConfig): ForaminiferaAccessible[BufferCell] = - new ForaminiferaAccessible[BufferCell] { - override def withForaminifera(energy: Energy, lifespan: Long): BufferCell = BufferCell(ForaminiferaCell(energy, arg.smellWith(config.foraminiferaInitialSignal.toSignalVector), lifespan, config.foraminiferaPursuedSignalIndex)) - } - - def unapply(arg: GridPart)(implicit config: SchoolConfig): Option[ForaminiferaAccessible[GridPart]] = arg match { - case cell: AlgaeCell => Some(unapply(cell)) - case cell: EmptyCell => Some(unapply(cell)) - case cell: BufferCell => Some(unapply(cell)) - case _ => None - } -} diff --git a/school/src/main/scala/pl/edu/agh/school/model/Algae.scala b/school/src/main/scala/pl/edu/agh/school/model/Student.scala similarity index 60% rename from school/src/main/scala/pl/edu/agh/school/model/Algae.scala rename to school/src/main/scala/pl/edu/agh/school/model/Student.scala index e17c6004..cf6dea9a 100644 --- a/school/src/main/scala/pl/edu/agh/school/model/Algae.scala +++ b/school/src/main/scala/pl/edu/agh/school/model/Student.scala @@ -5,10 +5,10 @@ import pl.edu.agh.xinuk.model.Cell.SmellArray import pl.edu.agh.xinuk.model.{BufferCell, EmptyCell, GridPart, SmellingCell} import pl.edu.agh.xinuk.model.SignalVector.SignalVectorOps -final case class AlgaeCell(smell: SmellArray, lifespan: Long) extends SmellingCell { - override type Self = AlgaeCell +final case class StudentCell(smell: SmellArray, lifespan: Long, signalIndex: Int) extends SmellingCell { + override type Self = StudentCell - override def withSmell(smell: SmellArray): AlgaeCell = copy(smell = smell) + override def withSmell(smell: SmellArray): StudentCell = copy(smell = smell) } trait AlgaeAccessible[+T <: GridPart] { @@ -17,14 +17,14 @@ trait AlgaeAccessible[+T <: GridPart] { object AlgaeAccessible { - def unapply(arg: EmptyCell)(implicit config: SchoolConfig): AlgaeAccessible[AlgaeCell] = - new AlgaeAccessible[AlgaeCell] { - override def withAlgae(lifespan: Long): AlgaeCell = AlgaeCell(arg.smellWith(config.algaeInitialSignal.toSignalVector), lifespan) + def unapply(arg: EmptyCell)(implicit config: SchoolConfig): AlgaeAccessible[StudentCell] = + new AlgaeAccessible[StudentCell] { + override def withAlgae(lifespan: Long): StudentCell = StudentCell(arg.smellWith(config.studentInitialSignal.toSignalVector), lifespan, config.studentSignalIndex) } def unapply(arg: BufferCell)(implicit config: SchoolConfig): AlgaeAccessible[BufferCell] = new AlgaeAccessible[BufferCell] { - override def withAlgae(lifespan: Long): BufferCell = BufferCell(AlgaeCell(arg.smellWith(config.algaeInitialSignal.toSignalVector), lifespan)) + override def withAlgae(lifespan: Long): BufferCell = BufferCell(StudentCell(arg.smellWith(config.studentInitialSignal.toSignalVector), lifespan, config.studentSignalIndex)) } def unapply(arg: GridPart)(implicit config: SchoolConfig): Option[AlgaeAccessible[GridPart]] = arg match { diff --git a/school/src/main/scala/pl/edu/agh/school/model/Teacher.scala b/school/src/main/scala/pl/edu/agh/school/model/Teacher.scala new file mode 100644 index 00000000..e5f1b4d9 --- /dev/null +++ b/school/src/main/scala/pl/edu/agh/school/model/Teacher.scala @@ -0,0 +1,41 @@ +package pl.edu.agh.school.model + +import pl.edu.agh.school.config.SchoolConfig +import pl.edu.agh.xinuk.model.Cell.SmellArray +import pl.edu.agh.xinuk.model._ +import pl.edu.agh.xinuk.model.SignalVector.SignalVectorOps + +final case class TeacherCell(energy: Energy, smell: SmellArray, lifespan: Long, signalIndex: Int) extends SmellingCell { + override type Self = TeacherCell + + override def withSmell(smell: SmellArray): TeacherCell = copy(smell = smell) +} + +trait TeacherAccessible[+T <: GridPart] { + def withTeacher(energy: Energy, lifespan: Long): T +} + +object TeacherAccessible { + + def unapply(arg: StudentCell)(implicit config: SchoolConfig): TeacherAccessible[TeacherCell] = + new TeacherAccessible[TeacherCell] { + override def withTeacher(energy: Energy, lifespan: Long): TeacherCell = TeacherCell(energy + config.algaeEnergeticCapacity, arg.smellWith(config.teacherInitialSignal.toSignalVector), lifespan, config.teacherSignalIndex) + } + + def unapply(arg: EmptyCell)(implicit config: SchoolConfig): TeacherAccessible[TeacherCell] = + new TeacherAccessible[TeacherCell] { + override def withTeacher(energy: Energy, lifespan: Long): TeacherCell = TeacherCell(energy, arg.smellWith(config.teacherInitialSignal.toSignalVector), lifespan, config.teacherSignalIndex) + } + + def unapply(arg: BufferCell)(implicit config: SchoolConfig): TeacherAccessible[BufferCell] = + new TeacherAccessible[BufferCell] { + override def withTeacher(energy: Energy, lifespan: Long): BufferCell = BufferCell(TeacherCell(energy, arg.smellWith(config.teacherInitialSignal.toSignalVector), lifespan, config.teacherSignalIndex)) + } + + def unapply(arg: GridPart)(implicit config: SchoolConfig): Option[TeacherAccessible[GridPart]] = arg match { + case cell: StudentCell => Some(unapply(cell)) + case cell: EmptyCell => Some(unapply(cell)) + case cell: BufferCell => Some(unapply(cell)) + case _ => None + } +} diff --git a/school/src/main/scala/pl/edu/agh/school/model/Wall.scala b/school/src/main/scala/pl/edu/agh/school/model/Wall.scala new file mode 100644 index 00000000..9b71d534 --- /dev/null +++ b/school/src/main/scala/pl/edu/agh/school/model/Wall.scala @@ -0,0 +1,41 @@ +package pl.edu.agh.school.model + +import pl.edu.agh.school.config.SchoolConfig +import pl.edu.agh.xinuk.model.Cell.SmellArray +import pl.edu.agh.xinuk.model._ +import pl.edu.agh.xinuk.model.SignalVector.SignalVectorOps + +final case class WallCell(smell: SmellArray) extends SmellingCell { + override type Self = WallCell + + override def withSmell(smell: SmellArray): WallCell = copy(smell = smell) +} + +trait WallAccessible[+T <: GridPart] { + def withWall(energy: Energy, lifespan: Long): T +} + +object WallAccessible { + + def unapply(arg: StudentCell)(implicit config: SchoolConfig): WallAccessible[WallCell] = + new WallAccessible[WallCell] { + override def withWall(energy: Energy, lifespan: Long): WallCell = WallCell(arg.smellWith(config.cleanerInitialSignal.toSignalVector)) + } + + def unapply(arg: EmptyCell)(implicit config: SchoolConfig): WallAccessible[WallCell] = + new WallAccessible[WallCell] { + override def withWall(energy: Energy, lifespan: Long): WallCell = WallCell(arg.smellWith(config.cleanerInitialSignal.toSignalVector)) + } + + def unapply(arg: BufferCell)(implicit config: SchoolConfig): WallAccessible[BufferCell] = + new WallAccessible[BufferCell] { + override def withWall(energy: Energy, lifespan: Long): BufferCell = BufferCell(WallCell(arg.smellWith(config.cleanerInitialSignal.toSignalVector))) + } + + def unapply(arg: GridPart)(implicit config: SchoolConfig): Option[WallAccessible[GridPart]] = arg match { + case cell: StudentCell => Some(unapply(cell)) + case cell: EmptyCell => Some(unapply(cell)) + case cell: BufferCell => Some(unapply(cell)) + case _ => None + } +} diff --git a/school/src/main/scala/pl/edu/agh/school/model/parallel/SchoolConflictResolver.scala b/school/src/main/scala/pl/edu/agh/school/model/parallel/SchoolConflictResolver.scala index 254d6ebc..a94746a7 100644 --- a/school/src/main/scala/pl/edu/agh/school/model/parallel/SchoolConflictResolver.scala +++ b/school/src/main/scala/pl/edu/agh/school/model/parallel/SchoolConflictResolver.scala @@ -16,14 +16,14 @@ object SchoolConflictResolver extends ConflictResolver[SchoolConfig] { (incomingCell.withSmell(incomingCell.smell + currentSmell), SchoolMetrics.empty()) case (currentCell: SmellingCell, EmptyCell(incomingSmell)) => (currentCell.withSmell(currentCell.smell + incomingSmell), SchoolMetrics.empty()) - case (AlgaeCell(currentSmell, currentLifespan), ForaminiferaCell(energy, incomingSmell, incomingLifespan, pursuedSignalIndex)) => - (ForaminiferaCell(energy + config.algaeEnergeticCapacity, incomingSmell + currentSmell, incomingLifespan, pursuedSignalIndex), SchoolMetrics(0, 0, 0, 0, 0, 1, 0, currentLifespan)) - case (ForaminiferaCell(energy, currentSmell, currentLifespan, pursuedSignalIndex), AlgaeCell(incomingSmell, incomingLifespan)) => - (ForaminiferaCell(energy + config.algaeEnergeticCapacity, incomingSmell + currentSmell, currentLifespan, pursuedSignalIndex), SchoolMetrics(0, 0, 0, 0, 0, 1, 0, incomingLifespan)) - case (AlgaeCell(currentSmell, lifespan), AlgaeCell(incomingSmell, incomingLifespan)) => - (AlgaeCell(currentSmell + incomingSmell, math.max(lifespan, incomingLifespan)), SchoolMetrics.empty()) - case (ForaminiferaCell(currentEnergy, currentSmell, lifespan, _), ForaminiferaCell(incomingEnergy, incomingSmell, incomingLifespan, pursuedSignalIndex)) => // TODO: here are place where two smells can intersect - (ForaminiferaCell(currentEnergy + incomingEnergy, currentSmell + incomingSmell, math.max(lifespan, incomingLifespan), pursuedSignalIndex), SchoolMetrics.empty()) + case (StudentCell(currentSmell, currentLifespan, _), DirtCell(energy, incomingSmell, incomingLifespan, pursuedSignalIndex)) => + (DirtCell(energy + config.algaeEnergeticCapacity, incomingSmell + currentSmell, incomingLifespan, pursuedSignalIndex), SchoolMetrics(0, 0, 0, 0, 0, 1, 0, currentLifespan)) + case (DirtCell(energy, currentSmell, currentLifespan, pursuedSignalIndex), StudentCell(incomingSmell, incomingLifespan, _)) => + (DirtCell(energy + config.algaeEnergeticCapacity, incomingSmell + currentSmell, currentLifespan, pursuedSignalIndex), SchoolMetrics(0, 0, 0, 0, 0, 1, 0, incomingLifespan)) + case (StudentCell(currentSmell, lifespan, signalIndex), StudentCell(incomingSmell, incomingLifespan, _)) => + (StudentCell(currentSmell + incomingSmell, math.max(lifespan, incomingLifespan), signalIndex), SchoolMetrics.empty()) + case (DirtCell(currentEnergy, currentSmell, lifespan, _), DirtCell(incomingEnergy, incomingSmell, incomingLifespan, pursuedSignalIndex)) => // TODO: here are place where two smells can intersect + (DirtCell(currentEnergy + incomingEnergy, currentSmell + incomingSmell, math.max(lifespan, incomingLifespan), pursuedSignalIndex), SchoolMetrics.empty()) case (Obstacle, _) => (Obstacle, SchoolMetrics.empty()) case (x, y) => throw new UnsupportedOperationException(s"Unresolved conflict: $x with $y") } From 0eac0c13cf6ce405ed7591466427420260c6a48e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Zieli=C5=84ski?= Date: Sun, 14 Apr 2019 23:48:37 +0200 Subject: [PATCH 05/10] Implement sample school simulation with 1 teacher, 1 cleaner, 1 static dirt and 1 static student - 2 smells in action --- school/src/main/resources/reference.conf | 4 +- .../scala/pl/edu/agh/school/SchoolMain.scala | 10 +- .../algorithm/SchoolMovesController.scala | 145 +++++++++++++----- .../pl/edu/agh/school/model/Cleaner.scala | 30 ++-- .../scala/pl/edu/agh/school/model/Dirt.scala | 6 +- .../pl/edu/agh/school/model/Student.scala | 20 +-- .../parallel/SchoolConflictResolver.scala | 12 +- .../agh/school/simulation/SchoolMetrics.scala | 3 + .../scala/pl/edu/agh/xinuk/model/Grid.scala | 3 +- 9 files changed, 149 insertions(+), 84 deletions(-) diff --git a/school/src/main/resources/reference.conf b/school/src/main/resources/reference.conf index 11f7cbbb..cceca6c0 100644 --- a/school/src/main/resources/reference.conf +++ b/school/src/main/resources/reference.conf @@ -31,7 +31,7 @@ school { signalSpeedRatio = 2 signalSuppressionFactor = 0.5 signalAttenuationFactor = 1 - gridSize = 50 + gridSize = 100 spawnChance = 0.1 foraminiferaSpawnChance = 0.3 @@ -46,7 +46,7 @@ school { guiType = basic guiCellSize = 4 - workersRoot = 2 + workersRoot = 1 iterationsNumber = 10000 isSupervisor = true shardingMod = 144 diff --git a/school/src/main/scala/pl/edu/agh/school/SchoolMain.scala b/school/src/main/scala/pl/edu/agh/school/SchoolMain.scala index 0090327c..530d5d00 100644 --- a/school/src/main/scala/pl/edu/agh/school/SchoolMain.scala +++ b/school/src/main/scala/pl/edu/agh/school/SchoolMain.scala @@ -25,11 +25,11 @@ object SchoolMain extends LazyLogging { private def cellToColor(cell: SmellingCell): Color = { cell match { - case CleanerCell(_, _, _, _) => new Color(66, 134, 244) - case DirtCell(_, _, _, _) => new Color(132, 86, 7) - case StudentCell(_, _, _) => new Color(16, 234, 23) - case TeacherCell(_, _, _, _) => new Color(132, 7, 7) - case WallCell(_) => new Color(135, 135, 135) + case CleanerCell(_, _, _, _) => new Color(66, 134, 244) // blue + case DirtCell(_, _, _, _) => new Color(132, 86, 7) // brown + case StudentCell(_, _, _) => new Color(16, 234, 23) // green + case TeacherCell(_, _, _, _) => new Color(255, 10, 10) // red + case WallCell(_) => new Color(135, 135, 135) // gray case _ => Color.WHITE } } diff --git a/school/src/main/scala/pl/edu/agh/school/algorithm/SchoolMovesController.scala b/school/src/main/scala/pl/edu/agh/school/algorithm/SchoolMovesController.scala index 8e01c46b..caf8a6e2 100644 --- a/school/src/main/scala/pl/edu/agh/school/algorithm/SchoolMovesController.scala +++ b/school/src/main/scala/pl/edu/agh/school/algorithm/SchoolMovesController.scala @@ -8,6 +8,7 @@ import pl.edu.agh.school.model._ import pl.edu.agh.school.simulation.SchoolMetrics import pl.edu.agh.xinuk.algorithm.MovesController import pl.edu.agh.xinuk.model._ +import pl.edu.agh.xinuk.simulation.Metrics import scala.collection.immutable.TreeSet import scala.util.Random @@ -18,10 +19,30 @@ final class SchoolMovesController(bufferZone: TreeSet[(Int, Int)])(implicit conf private val random = new Random(System.nanoTime()) - override def initialGrid: (Grid, SchoolMetrics) = { + override def initialGrid: (Grid, Metrics) = { grid = Grid.empty(bufferZone) - var foraminiferaCount = 0L - var algaeCount = 0L + + var gridSize = config.gridSize + + // left - top + grid.cells(gridSize/2)(gridSize/4) = TeacherAccessible.unapply(EmptyCell.Instance).withTeacher(Energy(0), 0) + // left - bottom + grid.cells(gridSize/4)(gridSize/4) = CleanerAccessible.unapply(EmptyCell.Instance).withCleaner(Energy(0), 0) + + // right - top + grid.cells(gridSize/4)(gridSize*3/4) = StudentAccessible.unapply(EmptyCell.Instance).withStudent(0) + // right - bottom + grid.cells(gridSize/2)(gridSize*3/4) = DirtAccessible.unapply(EmptyCell.Instance).withDirt(Energy(0), 0) + + val metrics = SchoolMetrics(1, 1, 0, 0, 0, 0, 0, 0) + (grid, metrics) + } + + def initialGrid2: (Grid, SchoolMetrics) = { +// override def initialGrid: (Grid, SchoolMetrics) = { + grid = Grid.empty(bufferZone) + var studentsCount = 0L + var teachersCount = 0L for { x <- 0 until config.gridSize y <- 0 until config.gridSize @@ -30,21 +51,38 @@ final class SchoolMovesController(bufferZone: TreeSet[(Int, Int)])(implicit conf if (random.nextDouble() < config.spawnChance) { grid.cells(x)(y) = if (random.nextDouble() < config.foraminiferaSpawnChance) { - foraminiferaCount += 1 - ForaminiferaAccessible.unapply(EmptyCell.Instance).withForaminifera(config.foraminiferaStartEnergy, 0) + studentsCount += 1 + CleanerAccessible.unapply(EmptyCell.Instance).withCleaner(config.foraminiferaStartEnergy, 0) } else { - algaeCount += 1 - AlgaeAccessible.unapply(EmptyCell.Instance).withAlgae(0) + teachersCount += 1 + StudentAccessible.unapply(EmptyCell.Instance).withStudent(0) } } } - val metrics = SchoolMetrics(foraminiferaCount, algaeCount, 0, config.foraminiferaStartEnergy.value * foraminiferaCount, 0, 0, 0, 0) + val metrics = SchoolMetrics(studentsCount, teachersCount, 0, config.foraminiferaStartEnergy.value * studentsCount, 0, 0, 0, 0) + // metrics are used to measure actual statistics for eg. current students count (grid, metrics) } - def calculatePossibleDestinations(cell: DirtCell, x: Int, y: Int, grid: Grid): Iterator[(Int, Int, GridPart)] = { + def calculatePossibleDestinations(cell: CleanerCell, x: Int, y: Int, grid: Grid): Iterator[(Int, Int, GridPart)] = { + val neighbourCellCoordinates = Grid.neighbourCellCoordinates(x, y) + Grid.SubcellCoordinates + .map { case (i, j) => cell.smell(i)(j) } + .zipWithIndex + .map { + case (signalVector, index) => (signalVector(cell.signalIndex), index) + } + .sorted(implicitly[Ordering[(Signal, Int)]].reverse) + .iterator + .map { case (_, idx) => + val (i, j) = neighbourCellCoordinates(idx) + (i, j, grid.cells(i)(j)) + } + } + + def calculatePossibleDestinations(cell: TeacherCell, x: Int, y: Int, grid: Grid): Iterator[(Int, Int, GridPart)] = { val neighbourCellCoordinates = Grid.neighbourCellCoordinates(x, y) Grid.SubcellCoordinates .map { case (i, j) => cell.smell(i)(j) } @@ -64,7 +102,9 @@ final class SchoolMovesController(bufferZone: TreeSet[(Int, Int)])(implicit conf possibleDestinations .map { case (i, j, current) => (i, j, current, newGrid.cells(i)(j)) } .collectFirstOpt { - case (i, j, currentCell@ForaminiferaAccessible(_), ForaminiferaAccessible(_)) => + case (i, j, currentCell @ TeacherAccessible(_), TeacherAccessible(_)) => + (i, j, currentCell) + case (i, j, currentCell @ CleanerAccessible(_), CleanerAccessible(_)) => (i, j, currentCell) } } @@ -112,25 +152,35 @@ final class SchoolMovesController(bufferZone: TreeSet[(Int, Int)])(implicit conf if (isEmptyIn(newGrid)(x, y)) { newGrid.cells(x)(y) = cell } + case cell: StudentCell => - if (iteration % config.algaeReproductionFrequency == 0) { - reproduce(x, y) { case AlgaeAccessible(accessible) => accessible.withAlgae(0) } - } - if (isEmptyIn(newGrid)(x, y)) { - newGrid.cells(x)(y) = cell.copy(lifespan = cell.lifespan + 1) + if(isEmptyIn(newGrid)(x,y)) { + newGrid.cells(x)(y) = cell } case cell: DirtCell => - if (cell.energy < config.foraminiferaLifeActivityCost) { - killForaminifera(cell, x, y) - } else if (cell.energy > config.foraminiferaReproductionThreshold) { - reproduceForaminifera(cell, x, y) - } else { - moveForaminifera(cell, x, y) + if(isEmptyIn(newGrid)(x,y)) { + newGrid.cells(x)(y) = cell } + case cell: CleanerCell => +// if (iteration % config.algaeReproductionFrequency == 0) { +// reproduce(x, y) { case AlgaeAccessible(accessible) => accessible.withAlgae(0) } +// } + moveCleaner(cell, x, y) +// if (isEmptyIn(newGrid)(x, y)) { +// newGrid.cells(x)(y) = cell.copy(lifespan = cell.lifespan + 1) +// } + case cell: TeacherCell => +// if (cell.energy < config.foraminiferaLifeActivityCost) { +// killForaminifera(cell, x, y) +// } else if (cell.energy > config.foraminiferaReproductionThreshold) { +// reproduceForaminifera(cell, x, y) +// } else { + moveTeacher(cell, x, y) +// } } } - def killForaminifera(cell: DirtCell, x: Int, y: Int): Unit = { + def killForaminifera(cell: CleanerCell, x: Int, y: Int): Unit = { foraminiferaDeaths += 1 foraminiferaTotalLifespan += cell.lifespan val vacated = EmptyCell(cell.smell) @@ -138,35 +188,48 @@ final class SchoolMovesController(bufferZone: TreeSet[(Int, Int)])(implicit conf grid.cells(x)(y) = vacated } - def reproduceForaminifera(cell: DirtCell, x: Int, y: Int): Unit = { - reproduce(x, y) { case ForaminiferaAccessible(accessible) => accessible.withForaminifera(config.foraminiferaStartEnergy, 0) } + def reproduceForaminifera(cell: CleanerCell, x: Int, y: Int): Unit = { + reproduce(x, y) { case DirtAccessible(accessible) => accessible.withDirt(config.foraminiferaStartEnergy, 0) } newGrid.cells(x)(y) = cell.copy(energy = cell.energy - config.foraminiferaReproductionCost, lifespan = cell.lifespan + 1) foraminiferaReproductionsCount += 1 } - def moveForaminifera(cell: DirtCell, x: Int, y: Int): Unit = { + def moveCleaner(cell: CleanerCell, x: Int, y: Int): Unit = { val destinations = calculatePossibleDestinations(cell, x, y, grid) val destination = selectDestinationCell(destinations, newGrid) - destination match { - case Opt((_, _, StudentCell(_, lifespan, _))) => - consumedAlgaeCount += 1 - algaeTotalLifespan += lifespan - case Opt((_, _, BufferCell(StudentCell(_, lifespan, _)))) => - consumedAlgaeCount += 1 - algaeTotalLifespan += lifespan - case _ => + destination match { + case Opt((i, j, CleanerAccessible(dest))) => + var newCleaner = dest.withCleaner(cell.energy, cell.lifespan) + newGrid.cells(i)(j) = newCleaner + newGrid.cells(i)(j) match { + case DirtCell(_, _, _, _) => +// dirtCleaned += 1 + println("DIRT CLEANED!") + case _ => + } + case Opt((i, j, inaccessibleDestination)) => + throw new RuntimeException(s"Cleaner selected inaccessible destination ($i,$j): $inaccessibleDestination") + case Opt.Empty => + newGrid.cells(x)(y) = cell.copy(cell.energy, cell.smell, cell.lifespan, cell.signalIndex) + } } + + def moveTeacher(cell: TeacherCell, x: Int, y: Int): Unit = { + val destinations = calculatePossibleDestinations(cell, x, y, grid) + val destination = selectDestinationCell(destinations, newGrid) destination match { - case Opt((i, j, ForaminiferaAccessible(destination))) => - newGrid.cells(i)(j) = destination.withForaminifera(cell.energy - config.foraminiferaLifeActivityCost, cell.lifespan + 1) - val vacated = EmptyCell(cell.smell) - newGrid.cells(x)(y) = vacated - grid.cells(x)(y) = vacated + case Opt((i, j, TeacherAccessible(dest))) => + newGrid.cells(i)(j) = dest.withTeacher(cell.energy, cell.lifespan) + newGrid.cells(i)(j) match { + case StudentCell(_, _, _) => + // studentCleaned += 1 + println("STUDENT CAUGHT!") + case _ => + } case Opt((i, j, inaccessibleDestination)) => - throw new RuntimeException(s"Foraminifera selected inaccessible destination ($i,$j): $inaccessibleDestination") + throw new RuntimeException(s"Teacher selected inaccessible destination ($i,$j): $inaccessibleDestination") case Opt.Empty => - newGrid.cells(x)(y) = cell.copy(cell.energy - config.foraminiferaLifeActivityCost, lifespan = cell.lifespan + 1) - grid.cells(x)(y) + newGrid.cells(x)(y) = cell.copy(cell.energy, cell.smell, cell.lifespan, cell.signalIndex) } } diff --git a/school/src/main/scala/pl/edu/agh/school/model/Cleaner.scala b/school/src/main/scala/pl/edu/agh/school/model/Cleaner.scala index 229ad618..084424fe 100644 --- a/school/src/main/scala/pl/edu/agh/school/model/Cleaner.scala +++ b/school/src/main/scala/pl/edu/agh/school/model/Cleaner.scala @@ -5,35 +5,35 @@ import pl.edu.agh.xinuk.model.Cell.SmellArray import pl.edu.agh.xinuk.model._ import pl.edu.agh.xinuk.model.SignalVector.SignalVectorOps -final case class CleanerCell(energy: Energy, smell: SmellArray, lifespan: Long, pursuedSignalIndex: Int) extends SmellingCell { +final case class CleanerCell(energy: Energy, smell: SmellArray, lifespan: Long, signalIndex: Int) extends SmellingCell { override type Self = CleanerCell override def withSmell(smell: SmellArray): CleanerCell = copy(smell = smell) } -trait ForaminiferaAccessible[+T <: GridPart] { - def withForaminifera(energy: Energy, lifespan: Long): T +trait CleanerAccessible[+T <: GridPart] { + def withCleaner(energy: Energy, lifespan: Long): T } -object ForaminiferaAccessible { +object CleanerAccessible { - def unapply(arg: StudentCell)(implicit config: SchoolConfig): ForaminiferaAccessible[CleanerCell] = - new ForaminiferaAccessible[CleanerCell] { - override def withForaminifera(energy: Energy, lifespan: Long): CleanerCell = CleanerCell(energy + config.algaeEnergeticCapacity, arg.smellWith(config.cleanerInitialSignal.toSignalVector), lifespan, config.cleanerInitialSignalIndex) + def unapply(arg: DirtCell)(implicit config: SchoolConfig): CleanerAccessible[CleanerCell] = + new CleanerAccessible[CleanerCell] { + override def withCleaner(energy: Energy, lifespan: Long): CleanerCell = CleanerCell(energy + config.algaeEnergeticCapacity, arg.smellWith(config.cleanerInitialSignal.toSignalVector), lifespan, config.cleanerInitialSignalIndex) } - def unapply(arg: EmptyCell)(implicit config: SchoolConfig): ForaminiferaAccessible[CleanerCell] = - new ForaminiferaAccessible[CleanerCell] { - override def withForaminifera(energy: Energy, lifespan: Long): CleanerCell = CleanerCell(energy, arg.smellWith(config.cleanerInitialSignal.toSignalVector), lifespan, config.cleanerInitialSignalIndex) + def unapply(arg: EmptyCell)(implicit config: SchoolConfig): CleanerAccessible[CleanerCell] = + new CleanerAccessible[CleanerCell] { + override def withCleaner(energy: Energy, lifespan: Long): CleanerCell = CleanerCell(energy, arg.smellWith(config.cleanerInitialSignal.toSignalVector), lifespan, config.cleanerInitialSignalIndex) } - def unapply(arg: BufferCell)(implicit config: SchoolConfig): ForaminiferaAccessible[BufferCell] = - new ForaminiferaAccessible[BufferCell] { - override def withForaminifera(energy: Energy, lifespan: Long): BufferCell = BufferCell(CleanerCell(energy, arg.smellWith(config.cleanerInitialSignal.toSignalVector), lifespan, config.cleanerInitialSignalIndex)) + def unapply(arg: BufferCell)(implicit config: SchoolConfig): CleanerAccessible[BufferCell] = + new CleanerAccessible[BufferCell] { + override def withCleaner(energy: Energy, lifespan: Long): BufferCell = BufferCell(CleanerCell(energy, arg.smellWith(config.cleanerInitialSignal.toSignalVector), lifespan, config.cleanerInitialSignalIndex)) } - def unapply(arg: GridPart)(implicit config: SchoolConfig): Option[ForaminiferaAccessible[GridPart]] = arg match { - case cell: StudentCell => Some(unapply(cell)) + def unapply(arg: GridPart)(implicit config: SchoolConfig): Option[CleanerAccessible[GridPart]] = arg match { + case cell: DirtCell => Some(unapply(cell)) case cell: EmptyCell => Some(unapply(cell)) case cell: BufferCell => Some(unapply(cell)) case _ => None diff --git a/school/src/main/scala/pl/edu/agh/school/model/Dirt.scala b/school/src/main/scala/pl/edu/agh/school/model/Dirt.scala index 07a67bc1..288df349 100644 --- a/school/src/main/scala/pl/edu/agh/school/model/Dirt.scala +++ b/school/src/main/scala/pl/edu/agh/school/model/Dirt.scala @@ -19,17 +19,17 @@ object DirtAccessible { def unapply(arg: StudentCell)(implicit config: SchoolConfig): DirtAccessible[DirtCell] = new DirtAccessible[DirtCell] { - override def withDirt(energy: Energy, lifespan: Long): DirtCell = DirtCell(energy + config.algaeEnergeticCapacity, arg.smellWith(config.cleanerInitialSignal.toSignalVector), lifespan, config.cleanerInitialSignalIndex) + override def withDirt(energy: Energy, lifespan: Long): DirtCell = DirtCell(energy + config.algaeEnergeticCapacity, arg.smellWith(config.dirtInitialSignal.toSignalVector), lifespan, config.dirtSignalIndex) } def unapply(arg: EmptyCell)(implicit config: SchoolConfig): DirtAccessible[DirtCell] = new DirtAccessible[DirtCell] { - override def withDirt(energy: Energy, lifespan: Long): DirtCell = DirtCell(energy, arg.smellWith(config.cleanerInitialSignal.toSignalVector), lifespan, config.cleanerInitialSignalIndex) + override def withDirt(energy: Energy, lifespan: Long): DirtCell = DirtCell(energy, arg.smellWith(config.dirtInitialSignal.toSignalVector), lifespan, config.dirtSignalIndex) } def unapply(arg: BufferCell)(implicit config: SchoolConfig): DirtAccessible[BufferCell] = new DirtAccessible[BufferCell] { - override def withDirt(energy: Energy, lifespan: Long): BufferCell = BufferCell(DirtCell(energy, arg.smellWith(config.cleanerInitialSignal.toSignalVector), lifespan, config.cleanerInitialSignalIndex)) + override def withDirt(energy: Energy, lifespan: Long): BufferCell = BufferCell(DirtCell(energy, arg.smellWith(config.dirtInitialSignal.toSignalVector), lifespan, config.dirtSignalIndex)) } def unapply(arg: GridPart)(implicit config: SchoolConfig): Option[DirtAccessible[GridPart]] = arg match { diff --git a/school/src/main/scala/pl/edu/agh/school/model/Student.scala b/school/src/main/scala/pl/edu/agh/school/model/Student.scala index cf6dea9a..2bc20e85 100644 --- a/school/src/main/scala/pl/edu/agh/school/model/Student.scala +++ b/school/src/main/scala/pl/edu/agh/school/model/Student.scala @@ -11,23 +11,23 @@ final case class StudentCell(smell: SmellArray, lifespan: Long, signalIndex: Int override def withSmell(smell: SmellArray): StudentCell = copy(smell = smell) } -trait AlgaeAccessible[+T <: GridPart] { - def withAlgae(lifespan: Long): T +trait StudentAccessible[+T <: GridPart] { + def withStudent(lifespan: Long): T } -object AlgaeAccessible { +object StudentAccessible { - def unapply(arg: EmptyCell)(implicit config: SchoolConfig): AlgaeAccessible[StudentCell] = - new AlgaeAccessible[StudentCell] { - override def withAlgae(lifespan: Long): StudentCell = StudentCell(arg.smellWith(config.studentInitialSignal.toSignalVector), lifespan, config.studentSignalIndex) + def unapply(arg: EmptyCell)(implicit config: SchoolConfig): StudentAccessible[StudentCell] = + new StudentAccessible[StudentCell] { + override def withStudent(lifespan: Long): StudentCell = StudentCell(arg.smellWith(config.studentInitialSignal.toSignalVector), lifespan, config.studentSignalIndex) } - def unapply(arg: BufferCell)(implicit config: SchoolConfig): AlgaeAccessible[BufferCell] = - new AlgaeAccessible[BufferCell] { - override def withAlgae(lifespan: Long): BufferCell = BufferCell(StudentCell(arg.smellWith(config.studentInitialSignal.toSignalVector), lifespan, config.studentSignalIndex)) + def unapply(arg: BufferCell)(implicit config: SchoolConfig): StudentAccessible[BufferCell] = + new StudentAccessible[BufferCell] { + override def withStudent(lifespan: Long): BufferCell = BufferCell(StudentCell(arg.smellWith(config.studentInitialSignal.toSignalVector), lifespan, config.studentSignalIndex)) } - def unapply(arg: GridPart)(implicit config: SchoolConfig): Option[AlgaeAccessible[GridPart]] = arg match { + def unapply(arg: GridPart)(implicit config: SchoolConfig): Option[StudentAccessible[GridPart]] = arg match { case cell: EmptyCell => Some(unapply(cell)) case cell: BufferCell => Some(unapply(cell)) case _ => None diff --git a/school/src/main/scala/pl/edu/agh/school/model/parallel/SchoolConflictResolver.scala b/school/src/main/scala/pl/edu/agh/school/model/parallel/SchoolConflictResolver.scala index a94746a7..2391718e 100644 --- a/school/src/main/scala/pl/edu/agh/school/model/parallel/SchoolConflictResolver.scala +++ b/school/src/main/scala/pl/edu/agh/school/model/parallel/SchoolConflictResolver.scala @@ -16,14 +16,14 @@ object SchoolConflictResolver extends ConflictResolver[SchoolConfig] { (incomingCell.withSmell(incomingCell.smell + currentSmell), SchoolMetrics.empty()) case (currentCell: SmellingCell, EmptyCell(incomingSmell)) => (currentCell.withSmell(currentCell.smell + incomingSmell), SchoolMetrics.empty()) - case (StudentCell(currentSmell, currentLifespan, _), DirtCell(energy, incomingSmell, incomingLifespan, pursuedSignalIndex)) => - (DirtCell(energy + config.algaeEnergeticCapacity, incomingSmell + currentSmell, incomingLifespan, pursuedSignalIndex), SchoolMetrics(0, 0, 0, 0, 0, 1, 0, currentLifespan)) - case (DirtCell(energy, currentSmell, currentLifespan, pursuedSignalIndex), StudentCell(incomingSmell, incomingLifespan, _)) => - (DirtCell(energy + config.algaeEnergeticCapacity, incomingSmell + currentSmell, currentLifespan, pursuedSignalIndex), SchoolMetrics(0, 0, 0, 0, 0, 1, 0, incomingLifespan)) + case (StudentCell(currentSmell, currentLifespan, _), CleanerCell(energy, incomingSmell, incomingLifespan, pursuedSignalIndex)) => + (CleanerCell(energy + config.algaeEnergeticCapacity, incomingSmell + currentSmell, incomingLifespan, pursuedSignalIndex), SchoolMetrics(0, 0, 0, 0, 0, 1, 0, currentLifespan)) + case (CleanerCell(energy, currentSmell, currentLifespan, pursuedSignalIndex), StudentCell(incomingSmell, incomingLifespan, _)) => + (CleanerCell(energy + config.algaeEnergeticCapacity, incomingSmell + currentSmell, currentLifespan, pursuedSignalIndex), SchoolMetrics(0, 0, 0, 0, 0, 1, 0, incomingLifespan)) case (StudentCell(currentSmell, lifespan, signalIndex), StudentCell(incomingSmell, incomingLifespan, _)) => (StudentCell(currentSmell + incomingSmell, math.max(lifespan, incomingLifespan), signalIndex), SchoolMetrics.empty()) - case (DirtCell(currentEnergy, currentSmell, lifespan, _), DirtCell(incomingEnergy, incomingSmell, incomingLifespan, pursuedSignalIndex)) => // TODO: here are place where two smells can intersect - (DirtCell(currentEnergy + incomingEnergy, currentSmell + incomingSmell, math.max(lifespan, incomingLifespan), pursuedSignalIndex), SchoolMetrics.empty()) + case (CleanerCell(currentEnergy, currentSmell, lifespan, _), CleanerCell(incomingEnergy, incomingSmell, incomingLifespan, pursuedSignalIndex)) => // TODO: here are place where two smells can intersect + (CleanerCell(currentEnergy + incomingEnergy, currentSmell + incomingSmell, math.max(lifespan, incomingLifespan), pursuedSignalIndex), SchoolMetrics.empty()) case (Obstacle, _) => (Obstacle, SchoolMetrics.empty()) case (x, y) => throw new UnsupportedOperationException(s"Unresolved conflict: $x with $y") } diff --git a/school/src/main/scala/pl/edu/agh/school/simulation/SchoolMetrics.scala b/school/src/main/scala/pl/edu/agh/school/simulation/SchoolMetrics.scala index 7252d3fe..508a1e07 100644 --- a/school/src/main/scala/pl/edu/agh/school/simulation/SchoolMetrics.scala +++ b/school/src/main/scala/pl/edu/agh/school/simulation/SchoolMetrics.scala @@ -2,6 +2,9 @@ package pl.edu.agh.school.simulation import pl.edu.agh.xinuk.simulation.Metrics +/** + * Metrics are used to measure actual statistics for eg. current students count + */ final case class SchoolMetrics(foraminiferaCount: Long, algaeCount: Long, foraminiferaDeaths: Long, diff --git a/xinuk-core/src/main/scala/pl/edu/agh/xinuk/model/Grid.scala b/xinuk-core/src/main/scala/pl/edu/agh/xinuk/model/Grid.scala index 641519c4..aa945707 100644 --- a/xinuk-core/src/main/scala/pl/edu/agh/xinuk/model/Grid.scala +++ b/xinuk-core/src/main/scala/pl/edu/agh/xinuk/model/Grid.scala @@ -53,7 +53,7 @@ object Grid { } -final case class SignalVector(value: Array[Signal]) extends AnyVal {//with Ordered[SignalVector] { +final case class SignalVector(value: Array[Signal]) extends AnyVal { def apply(index: Int): Signal = value(index) def +(other: SignalVector) = SignalVector(value.zipWithIndex.map{ @@ -68,7 +68,6 @@ final case class SignalVector(value: Array[Signal]) extends AnyVal {//with Order def /(scalar: Double) = SignalVector(value.map(_ / scalar)) -// override def compare(that: SignalVector): Int = Ordering.Iterable[Signal].compare(value, that.value) } object SignalVector { From 086435bf08dcfd9c26ce53b40ab4a9bc1102b17e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Zieli=C5=84ski?= Date: Sun, 28 Apr 2019 17:26:44 +0200 Subject: [PATCH 06/10] Add movement to student and dirt leaving by him --- .../algorithm/SchoolMovesController.scala | 61 +++++++++++++++++-- .../pl/edu/agh/school/model/Student.scala | 5 ++ 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/school/src/main/scala/pl/edu/agh/school/algorithm/SchoolMovesController.scala b/school/src/main/scala/pl/edu/agh/school/algorithm/SchoolMovesController.scala index caf8a6e2..62f71543 100644 --- a/school/src/main/scala/pl/edu/agh/school/algorithm/SchoolMovesController.scala +++ b/school/src/main/scala/pl/edu/agh/school/algorithm/SchoolMovesController.scala @@ -98,6 +98,22 @@ final class SchoolMovesController(bufferZone: TreeSet[(Int, Int)])(implicit conf } } + def calculatePossibleDestinations(cell: StudentCell, x: Int, y: Int, grid: Grid): Iterator[(Int, Int, GridPart)] = { + val neighbourCellCoordinates = Grid.neighbourCellCoordinates(x, y) + Grid.SubcellCoordinates + .map { case (i, j) => cell.smell(i)(j) } + .zipWithIndex + .map { + case (signalVector, index) => (signalVector(config.dirtSignalIndex), index) // make student go away from dirt + } + .sorted(implicitly[Ordering[(Signal, Int)]]) + .iterator + .map { case (_, idx) => + val (i, j) = neighbourCellCoordinates(idx) + (i, j, grid.cells(i)(j)) + } + } + def selectDestinationCell(possibleDestinations: Iterator[(Int, Int, GridPart)], newGrid: Grid): commons.Opt[(Int, Int, GridPart)] = { possibleDestinations .map { case (i, j, current) => (i, j, current, newGrid.cells(i)(j)) } @@ -106,6 +122,18 @@ final class SchoolMovesController(bufferZone: TreeSet[(Int, Int)])(implicit conf (i, j, currentCell) case (i, j, currentCell @ CleanerAccessible(_), CleanerAccessible(_)) => (i, j, currentCell) + case (i, j, currentCell @ StudentAccessible(_), StudentAccessible(_)) => + (i, j, currentCell) + } + } + + def selectStudentDestinationCell(possibleDestinations: Iterator[(Int, Int, GridPart)], newGrid: Grid): commons.Opt[(Int, Int, GridPart)] = { + possibleDestinations + .map { case (i, j, current) => (i, j, current, newGrid.cells(i)(j)) } + .filter(_ => new Random().nextBoolean()) + .collectFirstOpt { + case (i, j, currentCell @ StudentAccessible(_), StudentAccessible(_)) => + (i, j, currentCell) } } @@ -145,6 +173,7 @@ final class SchoolMovesController(bufferZone: TreeSet[(Int, Int)])(implicit conf } def makeMove(x: Int, y: Int): Unit = { + // order in this pattern match is important (cleaner and teacher must be last matched) this.grid.cells(x)(y) match { case Obstacle => newGrid.cells(x)(y) = Obstacle @@ -153,14 +182,12 @@ final class SchoolMovesController(bufferZone: TreeSet[(Int, Int)])(implicit conf newGrid.cells(x)(y) = cell } - case cell: StudentCell => - if(isEmptyIn(newGrid)(x,y)) { - newGrid.cells(x)(y) = cell - } case cell: DirtCell => if(isEmptyIn(newGrid)(x,y)) { newGrid.cells(x)(y) = cell } + case cell: StudentCell => + moveStudent(cell, x, y) case cell: CleanerCell => // if (iteration % config.algaeReproductionFrequency == 0) { // reproduce(x, y) { case AlgaeAccessible(accessible) => accessible.withAlgae(0) } @@ -233,6 +260,32 @@ final class SchoolMovesController(bufferZone: TreeSet[(Int, Int)])(implicit conf } } + def moveStudent(cell: StudentCell, x: Int, y: Int): Unit = { + val destinations = calculatePossibleDestinations(cell, x, y, grid) + val destination = selectStudentDestinationCell(destinations, newGrid) + destination match { + case Opt((i, j, StudentAccessible(dest))) => + val studentLastCell = newGrid.cells(x)(y) + if (new Random().nextInt(5) == 0) spawnDirtIfPossible(studentLastCell, x, y) + newGrid.cells(i)(j) = dest.withStudent(cell.lifespan) + newGrid.cells(i)(j) match { + case DirtCell(_, _, _, _) => + case _ => + } + case Opt((i, j, inaccessibleDestination)) => + throw new RuntimeException(s"Student selected inaccessible destination ($i,$j): $inaccessibleDestination") + case Opt.Empty => + newGrid.cells(x)(y) = cell.copy(cell.smell, cell.lifespan, cell.signalIndex) + } + } + + def spawnDirtIfPossible(cell: GridPart, x: Int, y: Int): Unit = + cell match { + case DirtAccessible(c) => + newGrid.cells(x)(y) = c.withDirt(Energy(0), 0) + case _ => + } + for { x <- 0 until config.gridSize y <- 0 until config.gridSize diff --git a/school/src/main/scala/pl/edu/agh/school/model/Student.scala b/school/src/main/scala/pl/edu/agh/school/model/Student.scala index 2bc20e85..8ef49031 100644 --- a/school/src/main/scala/pl/edu/agh/school/model/Student.scala +++ b/school/src/main/scala/pl/edu/agh/school/model/Student.scala @@ -27,6 +27,11 @@ object StudentAccessible { override def withStudent(lifespan: Long): BufferCell = BufferCell(StudentCell(arg.smellWith(config.studentInitialSignal.toSignalVector), lifespan, config.studentSignalIndex)) } + def unapply(arg: DirtCell)(implicit config: SchoolConfig): StudentAccessible[StudentCell] = + new StudentAccessible[StudentCell] { + override def withStudent(lifespan: Long): StudentCell = StudentCell(arg.smellWith(config.studentInitialSignal.toSignalVector), lifespan, config.studentSignalIndex) + } + def unapply(arg: GridPart)(implicit config: SchoolConfig): Option[StudentAccessible[GridPart]] = arg match { case cell: EmptyCell => Some(unapply(cell)) case cell: BufferCell => Some(unapply(cell)) From 1c214ab666908501986cd4f0977c9a60b362706e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Wr=C3=B3bel?= Date: Thu, 2 May 2019 19:26:52 +0200 Subject: [PATCH 07/10] Add generating agents based on parameters from config Some refactoring Some changes in metrics --- formin/src/main/resources/reference.conf | 2 +- school/src/main/resources/reference.conf | 26 ++- .../scala/pl/edu/agh/school/SchoolMain.scala | 2 +- .../algorithm/SchoolMovesController.scala | 178 +++++++++--------- .../edu/agh/school/config/SchoolConfig.scala | 22 ++- .../pl/edu/agh/school/model/Cleaner.scala | 12 +- .../scala/pl/edu/agh/school/model/Dirt.scala | 12 +- .../pl/edu/agh/school/model/Teacher.scala | 12 +- .../parallel/SchoolConflictResolver.scala | 11 +- .../agh/school/simulation/SchoolMetrics.scala | 51 ++--- 10 files changed, 174 insertions(+), 154 deletions(-) diff --git a/formin/src/main/resources/reference.conf b/formin/src/main/resources/reference.conf index 0d721cd1..6dbb339a 100644 --- a/formin/src/main/resources/reference.conf +++ b/formin/src/main/resources/reference.conf @@ -45,4 +45,4 @@ formin { isSupervisor = true shardingMod = 144 } -} \ No newline at end of file +} diff --git a/school/src/main/resources/reference.conf b/school/src/main/resources/reference.conf index cceca6c0..138a7d9f 100644 --- a/school/src/main/resources/reference.conf +++ b/school/src/main/resources/reference.conf @@ -22,25 +22,31 @@ xinuk { school { config { - foraminiferaStartEnergy = 0.5 - foraminiferaReproductionCost = 0.5 - foraminiferaReproductionThreshold = 1 - foraminiferaLifeActivityCost = 0.1 - algaeReproductionFrequency = 2 - algaeEnergeticCapacity = 0.6 +// foraminiferaStartEnergy = 0.5 +// foraminiferaReproductionCost = 0.5 +// foraminiferaReproductionThreshold = 1 +// foraminiferaLifeActivityCost = 0.1 +// algaeReproductionFrequency = 2 +// algaeEnergeticCapacity = 0.6 signalSpeedRatio = 2 - signalSuppressionFactor = 0.5 - signalAttenuationFactor = 1 + signalSuppressionFactor = 0.75 + signalAttenuationFactor = 1.2 gridSize = 100 spawnChance = 0.1 - foraminiferaSpawnChance = 0.3 + cleanerSpawnChance = 0.5 cleanerInitialSignal: [-1, 0] cleanerInitialSignalIndex: 0 + + dirtInitialEnergy: 0.6 dirtInitialSignal: [1, 0] dirtSignalIndex: 0 + + studentSpawnChance: 0.05 studentInitialSignal: [0, 1] studentSignalIndex: 1 + + teacherSpawnChance: 0.2 teacherInitialSignal: [0, -1] teacherSignalIndex: 1 @@ -51,4 +57,4 @@ school { isSupervisor = true shardingMod = 144 } -} \ No newline at end of file +} diff --git a/school/src/main/scala/pl/edu/agh/school/SchoolMain.scala b/school/src/main/scala/pl/edu/agh/school/SchoolMain.scala index 530d5d00..2d34a5e0 100644 --- a/school/src/main/scala/pl/edu/agh/school/SchoolMain.scala +++ b/school/src/main/scala/pl/edu/agh/school/SchoolMain.scala @@ -37,7 +37,7 @@ object SchoolMain extends LazyLogging { def main(args: Array[String]): Unit = { import pl.edu.agh.xinuk.config.ValueReaders._ new Simulation[SchoolConfig](configPrefix, metricHeaders, SchoolConflictResolver, - DefaultSmellPropagation.calculateSmellAddendsStandard)(new SchoolMovesController(_)(_), + DefaultSmellPropagation.calculateSmellAddendsCircular)(new SchoolMovesController(_)(_), { case cell: SmellingCell => cellToColor(cell) } ).start() } diff --git a/school/src/main/scala/pl/edu/agh/school/algorithm/SchoolMovesController.scala b/school/src/main/scala/pl/edu/agh/school/algorithm/SchoolMovesController.scala index 62f71543..225fb08b 100644 --- a/school/src/main/scala/pl/edu/agh/school/algorithm/SchoolMovesController.scala +++ b/school/src/main/scala/pl/edu/agh/school/algorithm/SchoolMovesController.scala @@ -19,28 +19,28 @@ final class SchoolMovesController(bufferZone: TreeSet[(Int, Int)])(implicit conf private val random = new Random(System.nanoTime()) - override def initialGrid: (Grid, Metrics) = { + def initialGrid3: (Grid, Metrics) = { grid = Grid.empty(bufferZone) var gridSize = config.gridSize // left - top - grid.cells(gridSize/2)(gridSize/4) = TeacherAccessible.unapply(EmptyCell.Instance).withTeacher(Energy(0), 0) + grid.cells(gridSize / 2)(gridSize / 4) = TeacherAccessible.unapply(EmptyCell.Instance).withTeacher(Energy(0), 0) // left - bottom - grid.cells(gridSize/4)(gridSize/4) = CleanerAccessible.unapply(EmptyCell.Instance).withCleaner(Energy(0), 0) + grid.cells(gridSize / 4)(gridSize / 4) = CleanerAccessible.unapply(EmptyCell.Instance).withCleaner(Energy(0), 0) // right - top - grid.cells(gridSize/4)(gridSize*3/4) = StudentAccessible.unapply(EmptyCell.Instance).withStudent(0) + grid.cells(gridSize / 4)(gridSize * 3 / 4) = StudentAccessible.unapply(EmptyCell.Instance).withStudent(0) // right - bottom - grid.cells(gridSize/2)(gridSize*3/4) = DirtAccessible.unapply(EmptyCell.Instance).withDirt(Energy(0), 0) + grid.cells(gridSize / 2)(gridSize * 3 / 4) = DirtAccessible.unapply(EmptyCell.Instance).withDirt(Energy(0), 0) - val metrics = SchoolMetrics(1, 1, 0, 0, 0, 0, 0, 0) + val metrics = SchoolMetrics(1, 1, 1, 0) (grid, metrics) } - def initialGrid2: (Grid, SchoolMetrics) = { -// override def initialGrid: (Grid, SchoolMetrics) = { + override def initialGrid: (Grid, SchoolMetrics) = { grid = Grid.empty(bufferZone) + var cleanersCount = 0L var studentsCount = 0L var teachersCount = 0L for { @@ -49,18 +49,27 @@ final class SchoolMovesController(bufferZone: TreeSet[(Int, Int)])(implicit conf if x != 0 && y != 0 && x != config.gridSize - 1 && y != config.gridSize - 1 } { if (random.nextDouble() < config.spawnChance) { - grid.cells(x)(y) = - if (random.nextDouble() < config.foraminiferaSpawnChance) { - studentsCount += 1 - CleanerAccessible.unapply(EmptyCell.Instance).withCleaner(config.foraminiferaStartEnergy, 0) - } - else { - teachersCount += 1 - StudentAccessible.unapply(EmptyCell.Instance).withStudent(0) - } + val chance = random.nextDouble() + Math.abs(random.nextInt()) % 3 match { + case 0 => + if (chance < config.cleanerSpawnChance) { + cleanersCount += 1 + grid.cells(x)(y) = CleanerAccessible.unapply(EmptyCell.Instance).withCleaner(Energy(0), 0) + } + case 1 => + if (chance < config.teacherSpawnChance) { + teachersCount += 1 + grid.cells(x)(y) = TeacherAccessible.unapply(EmptyCell.Instance).withTeacher(Energy(0), 0) + } + case 2 => + if (chance < config.studentSpawnChance) { + studentsCount += 1 + grid.cells(x)(y) = StudentAccessible.unapply(EmptyCell.Instance).withStudent(0) + } + } } } - val metrics = SchoolMetrics(studentsCount, teachersCount, 0, config.foraminiferaStartEnergy.value * studentsCount, 0, 0, 0, 0) + val metrics = SchoolMetrics(studentsCount, teachersCount, cleanersCount, 0) // metrics are used to measure actual statistics for eg. current students count (grid, metrics) } @@ -68,13 +77,14 @@ final class SchoolMovesController(bufferZone: TreeSet[(Int, Int)])(implicit conf def calculatePossibleDestinations(cell: CleanerCell, x: Int, y: Int, grid: Grid): Iterator[(Int, Int, GridPart)] = { val neighbourCellCoordinates = Grid.neighbourCellCoordinates(x, y) - Grid.SubcellCoordinates + val tuples = Grid.SubcellCoordinates .map { case (i, j) => cell.smell(i)(j) } .zipWithIndex .map { case (signalVector, index) => (signalVector(cell.signalIndex), index) } .sorted(implicitly[Ordering[(Signal, Int)]].reverse) + tuples .iterator .map { case (_, idx) => val (i, j) = neighbourCellCoordinates(idx) @@ -118,11 +128,11 @@ final class SchoolMovesController(bufferZone: TreeSet[(Int, Int)])(implicit conf possibleDestinations .map { case (i, j, current) => (i, j, current, newGrid.cells(i)(j)) } .collectFirstOpt { - case (i, j, currentCell @ TeacherAccessible(_), TeacherAccessible(_)) => + case (i, j, currentCell@TeacherAccessible(_), TeacherAccessible(_)) => (i, j, currentCell) - case (i, j, currentCell @ CleanerAccessible(_), CleanerAccessible(_)) => + case (i, j, currentCell@CleanerAccessible(_), CleanerAccessible(_)) => (i, j, currentCell) - case (i, j, currentCell @ StudentAccessible(_), StudentAccessible(_)) => + case (i, j, currentCell@StudentAccessible(_), StudentAccessible(_)) => (i, j, currentCell) } } @@ -132,7 +142,7 @@ final class SchoolMovesController(bufferZone: TreeSet[(Int, Int)])(implicit conf .map { case (i, j, current) => (i, j, current, newGrid.cells(i)(j)) } .filter(_ => new Random().nextBoolean()) .collectFirstOpt { - case (i, j, currentCell @ StudentAccessible(_), StudentAccessible(_)) => + case (i, j, currentCell@StudentAccessible(_), StudentAccessible(_)) => (i, j, currentCell) } } @@ -141,7 +151,7 @@ final class SchoolMovesController(bufferZone: TreeSet[(Int, Int)])(implicit conf this.grid = grid val newGrid = Grid.empty(bufferZone) - var foraminiferaCount = 0L + var studentsCount = 0L var algaeCount = 0L var foraminiferaDeaths = 0L var foraminiferaReproductionsCount = 0L @@ -157,20 +167,20 @@ final class SchoolMovesController(bufferZone: TreeSet[(Int, Int)])(implicit conf } } - def reproduce(x: Int, y: Int)(creator: PartialFunction[GridPart, GridPart]): Unit = { - val emptyCells = - Grid.neighbourCellCoordinates(x, y).flatMap { - case (i, j) => - grid.cells(i)(j).opt - .filter(_ => creator.isDefinedAt(newGrid.cells(i)(j))) //use the same availability criteria on new grid - .collect(creator) - .map((i, j, _)) - } - if (emptyCells.nonEmpty) { - val (newAlgaeX, newAlgaeY, newCell) = emptyCells(random.nextInt(emptyCells.size)) - newGrid.cells(newAlgaeX)(newAlgaeY) = newCell - } - } + // def reproduce(x: Int, y: Int)(creator: PartialFunction[GridPart, GridPart]): Unit = { + //// val emptyCells = + //// Grid.neighbourCellCoordinates(x, y).flatMap { + //// case (i, j) => + //// grid.cells(i)(j).opt + //// .filter(_ => creator.isDefinedAt(newGrid.cells(i)(j))) //use the same availability criteria on new grid + //// .collect(creator) + //// .map((i, j, _)) + //// } + //// if (emptyCells.nonEmpty) { + //// val (newAlgaeX, newAlgaeY, newCell) = emptyCells(random.nextInt(emptyCells.size)) + //// newGrid.cells(newAlgaeX)(newAlgaeY) = newCell + //// } + //// } def makeMove(x: Int, y: Int): Unit = { // order in this pattern match is important (cleaner and teacher must be last matched) @@ -183,63 +193,63 @@ final class SchoolMovesController(bufferZone: TreeSet[(Int, Int)])(implicit conf } case cell: DirtCell => - if(isEmptyIn(newGrid)(x,y)) { + if (isEmptyIn(newGrid)(x, y)) { newGrid.cells(x)(y) = cell } case cell: StudentCell => moveStudent(cell, x, y) case cell: CleanerCell => -// if (iteration % config.algaeReproductionFrequency == 0) { -// reproduce(x, y) { case AlgaeAccessible(accessible) => accessible.withAlgae(0) } -// } + // if (iteration % config.algaeReproductionFrequency == 0) { + // reproduce(x, y) { case AlgaeAccessible(accessible) => accessible.withAlgae(0) } + // } moveCleaner(cell, x, y) -// if (isEmptyIn(newGrid)(x, y)) { -// newGrid.cells(x)(y) = cell.copy(lifespan = cell.lifespan + 1) -// } + // if (isEmptyIn(newGrid)(x, y)) { + // newGrid.cells(x)(y) = cell.copy(lifespan = cell.lifespan + 1) + // } case cell: TeacherCell => -// if (cell.energy < config.foraminiferaLifeActivityCost) { -// killForaminifera(cell, x, y) -// } else if (cell.energy > config.foraminiferaReproductionThreshold) { -// reproduceForaminifera(cell, x, y) -// } else { - moveTeacher(cell, x, y) -// } + // if (cell.energy < config.foraminiferaLifeActivityCost) { + // killForaminifera(cell, x, y) + // } else if (cell.energy > config.foraminiferaReproductionThreshold) { + // reproduceForaminifera(cell, x, y) + // } else { + moveTeacher(cell, x, y) + // } } } - def killForaminifera(cell: CleanerCell, x: Int, y: Int): Unit = { - foraminiferaDeaths += 1 - foraminiferaTotalLifespan += cell.lifespan - val vacated = EmptyCell(cell.smell) - newGrid.cells(x)(y) = vacated - grid.cells(x)(y) = vacated - } + // def killForaminifera(cell: CleanerCell, x: Int, y: Int): Unit = { + // foraminiferaDeaths += 1 + // foraminiferaTotalLifespan += cell.lifespan + // val vacated = EmptyCell(cell.smell) + // newGrid.cells(x)(y) = vacated + // grid.cells(x)(y) = vacated + // } - def reproduceForaminifera(cell: CleanerCell, x: Int, y: Int): Unit = { - reproduce(x, y) { case DirtAccessible(accessible) => accessible.withDirt(config.foraminiferaStartEnergy, 0) } - newGrid.cells(x)(y) = cell.copy(energy = cell.energy - config.foraminiferaReproductionCost, lifespan = cell.lifespan + 1) - foraminiferaReproductionsCount += 1 - } + // def reproduceForaminifera(cell: CleanerCell, x: Int, y: Int): Unit = { + // reproduce(x, y) { case DirtAccessible(accessible) => accessible.withDirt(config.foraminiferaStartEnergy, 0) } + // newGrid.cells(x)(y) = cell.copy(energy = cell.energy - config.foraminiferaReproductionCost, lifespan = cell.lifespan + 1) + // foraminiferaReproductionsCount += 1 + // } def moveCleaner(cell: CleanerCell, x: Int, y: Int): Unit = { val destinations = calculatePossibleDestinations(cell, x, y, grid) val destination = selectDestinationCell(destinations, newGrid) - destination match { - case Opt((i, j, CleanerAccessible(dest))) => - var newCleaner = dest.withCleaner(cell.energy, cell.lifespan) - newGrid.cells(i)(j) = newCleaner - newGrid.cells(i)(j) match { - case DirtCell(_, _, _, _) => -// dirtCleaned += 1 - println("DIRT CLEANED!") - case _ => - } - case Opt((i, j, inaccessibleDestination)) => - throw new RuntimeException(s"Cleaner selected inaccessible destination ($i,$j): $inaccessibleDestination") - case Opt.Empty => - newGrid.cells(x)(y) = cell.copy(cell.energy, cell.smell, cell.lifespan, cell.signalIndex) - } + destination match { + case Opt((i, j, CleanerAccessible(dest))) => + val newCleaner = dest.withCleaner(cell.energy, cell.lifespan) + newGrid.cells(i)(j) = newCleaner + newGrid.cells(i)(j) match { + case DirtCell(_, _, _, _) => + // dirtCleaned += 1 + println("DIRT CLEANED!") + case _ => + } + case Opt((i, j, inaccessibleDestination)) => + //throw new RuntimeException(s"Cleaner selected inaccessible destination ($i,$j): $inaccessibleDestination") + case Opt.Empty => + newGrid.cells(x)(y) = cell.copy(cell.energy, cell.smell, cell.lifespan, cell.signalIndex) } + } def moveTeacher(cell: TeacherCell, x: Int, y: Int): Unit = { val destinations = calculatePossibleDestinations(cell, x, y, grid) @@ -254,7 +264,7 @@ final class SchoolMovesController(bufferZone: TreeSet[(Int, Int)])(implicit conf case _ => } case Opt((i, j, inaccessibleDestination)) => - throw new RuntimeException(s"Teacher selected inaccessible destination ($i,$j): $inaccessibleDestination") + //throw new RuntimeException(s"Teacher selected inaccessible destination ($i,$j): $inaccessibleDestination") case Opt.Empty => newGrid.cells(x)(y) = cell.copy(cell.energy, cell.smell, cell.lifespan, cell.signalIndex) } @@ -293,10 +303,10 @@ final class SchoolMovesController(bufferZone: TreeSet[(Int, Int)])(implicit conf this.grid.cells(x)(y) match { case DirtCell(energy, _, _, _) => foraminiferaTotalEnergy += energy.value - foraminiferaCount += 1 + studentsCount += 1 case BufferCell(DirtCell(energy, _, _, _)) => foraminiferaTotalEnergy += energy.value - foraminiferaCount += 1 + studentsCount += 1 case StudentCell(_, _, _) | BufferCell(StudentCell(_, _, _)) => algaeCount += 1 case _ => @@ -308,7 +318,7 @@ final class SchoolMovesController(bufferZone: TreeSet[(Int, Int)])(implicit conf y <- 0 until config.gridSize } makeMove(x, y) - val metrics = SchoolMetrics(foraminiferaCount, algaeCount, foraminiferaDeaths, foraminiferaTotalEnergy, foraminiferaReproductionsCount, consumedAlgaeCount, foraminiferaTotalLifespan, algaeTotalLifespan) + val metrics = SchoolMetrics(studentsCount, algaeCount, 0, foraminiferaDeaths) (newGrid, metrics) } -} \ No newline at end of file +} diff --git a/school/src/main/scala/pl/edu/agh/school/config/SchoolConfig.scala b/school/src/main/scala/pl/edu/agh/school/config/SchoolConfig.scala index 984e73c0..6b109f70 100644 --- a/school/src/main/scala/pl/edu/agh/school/config/SchoolConfig.scala +++ b/school/src/main/scala/pl/edu/agh/school/config/SchoolConfig.scala @@ -20,26 +20,34 @@ ASSV - algae start signal value; ASSV ∈ [0,1] && ASSV ∈ R. */ final case class SchoolConfig( - foraminiferaStartEnergy: Energy, - foraminiferaReproductionCost: Energy, - foraminiferaReproductionThreshold: Energy, - foraminiferaLifeActivityCost: Energy, - algaeReproductionFrequency: Int, - algaeEnergeticCapacity: Energy, + // foraminiferaStartEnergy: Energy, + // foraminiferaReproductionCost: Energy, + // foraminiferaReproductionThreshold: Energy, + // foraminiferaLifeActivityCost: Energy, + // algaeReproductionFrequency: Int, + // algaeEnergeticCapacity: Energy, signalSpeedRatio: Int, signalSuppressionFactor: Double, signalAttenuationFactor: Double, gridSize: Int, spawnChance: Double, - foraminiferaSpawnChance: Double, + + cleanerSpawnChance: Double, cleanerInitialSignal: List[Signal], cleanerInitialSignalIndex: Int, + + dirtInitialEnergy: Double, dirtInitialSignal: List[Signal], dirtSignalIndex: Int, + + studentSpawnChance: Double, studentInitialSignal: List[Signal], studentSignalIndex: Int, + + teacherSpawnChance: Double, teacherInitialSignal: List[Signal], teacherSignalIndex: Int, + guiType: GuiType, guiCellSize: Int, workersRoot: Int, diff --git a/school/src/main/scala/pl/edu/agh/school/model/Cleaner.scala b/school/src/main/scala/pl/edu/agh/school/model/Cleaner.scala index 084424fe..efe87189 100644 --- a/school/src/main/scala/pl/edu/agh/school/model/Cleaner.scala +++ b/school/src/main/scala/pl/edu/agh/school/model/Cleaner.scala @@ -18,19 +18,13 @@ trait CleanerAccessible[+T <: GridPart] { object CleanerAccessible { def unapply(arg: DirtCell)(implicit config: SchoolConfig): CleanerAccessible[CleanerCell] = - new CleanerAccessible[CleanerCell] { - override def withCleaner(energy: Energy, lifespan: Long): CleanerCell = CleanerCell(energy + config.algaeEnergeticCapacity, arg.smellWith(config.cleanerInitialSignal.toSignalVector), lifespan, config.cleanerInitialSignalIndex) - } + (energy: Energy, lifespan: Long) => CleanerCell(energy, arg.smellWith(config.cleanerInitialSignal.toSignalVector), lifespan, config.cleanerInitialSignalIndex) def unapply(arg: EmptyCell)(implicit config: SchoolConfig): CleanerAccessible[CleanerCell] = - new CleanerAccessible[CleanerCell] { - override def withCleaner(energy: Energy, lifespan: Long): CleanerCell = CleanerCell(energy, arg.smellWith(config.cleanerInitialSignal.toSignalVector), lifespan, config.cleanerInitialSignalIndex) - } + (energy: Energy, lifespan: Long) => CleanerCell(energy, arg.smellWith(config.cleanerInitialSignal.toSignalVector), lifespan, config.cleanerInitialSignalIndex) def unapply(arg: BufferCell)(implicit config: SchoolConfig): CleanerAccessible[BufferCell] = - new CleanerAccessible[BufferCell] { - override def withCleaner(energy: Energy, lifespan: Long): BufferCell = BufferCell(CleanerCell(energy, arg.smellWith(config.cleanerInitialSignal.toSignalVector), lifespan, config.cleanerInitialSignalIndex)) - } + (energy: Energy, lifespan: Long) => BufferCell(CleanerCell(energy, arg.smellWith(config.cleanerInitialSignal.toSignalVector), lifespan, config.cleanerInitialSignalIndex)) def unapply(arg: GridPart)(implicit config: SchoolConfig): Option[CleanerAccessible[GridPart]] = arg match { case cell: DirtCell => Some(unapply(cell)) diff --git a/school/src/main/scala/pl/edu/agh/school/model/Dirt.scala b/school/src/main/scala/pl/edu/agh/school/model/Dirt.scala index 288df349..ce4eb3f4 100644 --- a/school/src/main/scala/pl/edu/agh/school/model/Dirt.scala +++ b/school/src/main/scala/pl/edu/agh/school/model/Dirt.scala @@ -18,19 +18,13 @@ trait DirtAccessible[+T <: GridPart] { object DirtAccessible { def unapply(arg: StudentCell)(implicit config: SchoolConfig): DirtAccessible[DirtCell] = - new DirtAccessible[DirtCell] { - override def withDirt(energy: Energy, lifespan: Long): DirtCell = DirtCell(energy + config.algaeEnergeticCapacity, arg.smellWith(config.dirtInitialSignal.toSignalVector), lifespan, config.dirtSignalIndex) - } + (energy: Energy, lifespan: Long) => DirtCell(energy, arg.smellWith(config.dirtInitialSignal.toSignalVector), lifespan, config.dirtSignalIndex) def unapply(arg: EmptyCell)(implicit config: SchoolConfig): DirtAccessible[DirtCell] = - new DirtAccessible[DirtCell] { - override def withDirt(energy: Energy, lifespan: Long): DirtCell = DirtCell(energy, arg.smellWith(config.dirtInitialSignal.toSignalVector), lifespan, config.dirtSignalIndex) - } + (energy: Energy, lifespan: Long) => DirtCell(energy, arg.smellWith(config.dirtInitialSignal.toSignalVector), lifespan, config.dirtSignalIndex) def unapply(arg: BufferCell)(implicit config: SchoolConfig): DirtAccessible[BufferCell] = - new DirtAccessible[BufferCell] { - override def withDirt(energy: Energy, lifespan: Long): BufferCell = BufferCell(DirtCell(energy, arg.smellWith(config.dirtInitialSignal.toSignalVector), lifespan, config.dirtSignalIndex)) - } + (energy: Energy, lifespan: Long) => BufferCell(DirtCell(energy, arg.smellWith(config.dirtInitialSignal.toSignalVector), lifespan, config.dirtSignalIndex)) def unapply(arg: GridPart)(implicit config: SchoolConfig): Option[DirtAccessible[GridPart]] = arg match { case cell: StudentCell => Some(unapply(cell)) diff --git a/school/src/main/scala/pl/edu/agh/school/model/Teacher.scala b/school/src/main/scala/pl/edu/agh/school/model/Teacher.scala index e5f1b4d9..fa7b5550 100644 --- a/school/src/main/scala/pl/edu/agh/school/model/Teacher.scala +++ b/school/src/main/scala/pl/edu/agh/school/model/Teacher.scala @@ -18,19 +18,13 @@ trait TeacherAccessible[+T <: GridPart] { object TeacherAccessible { def unapply(arg: StudentCell)(implicit config: SchoolConfig): TeacherAccessible[TeacherCell] = - new TeacherAccessible[TeacherCell] { - override def withTeacher(energy: Energy, lifespan: Long): TeacherCell = TeacherCell(energy + config.algaeEnergeticCapacity, arg.smellWith(config.teacherInitialSignal.toSignalVector), lifespan, config.teacherSignalIndex) - } + (energy: Energy, lifespan: Long) => TeacherCell(energy, arg.smellWith(config.teacherInitialSignal.toSignalVector), lifespan, config.teacherSignalIndex) def unapply(arg: EmptyCell)(implicit config: SchoolConfig): TeacherAccessible[TeacherCell] = - new TeacherAccessible[TeacherCell] { - override def withTeacher(energy: Energy, lifespan: Long): TeacherCell = TeacherCell(energy, arg.smellWith(config.teacherInitialSignal.toSignalVector), lifespan, config.teacherSignalIndex) - } + (energy: Energy, lifespan: Long) => TeacherCell(energy, arg.smellWith(config.teacherInitialSignal.toSignalVector), lifespan, config.teacherSignalIndex) def unapply(arg: BufferCell)(implicit config: SchoolConfig): TeacherAccessible[BufferCell] = - new TeacherAccessible[BufferCell] { - override def withTeacher(energy: Energy, lifespan: Long): BufferCell = BufferCell(TeacherCell(energy, arg.smellWith(config.teacherInitialSignal.toSignalVector), lifespan, config.teacherSignalIndex)) - } + (energy: Energy, lifespan: Long) => BufferCell(TeacherCell(energy, arg.smellWith(config.teacherInitialSignal.toSignalVector), lifespan, config.teacherSignalIndex)) def unapply(arg: GridPart)(implicit config: SchoolConfig): Option[TeacherAccessible[GridPart]] = arg match { case cell: StudentCell => Some(unapply(cell)) diff --git a/school/src/main/scala/pl/edu/agh/school/model/parallel/SchoolConflictResolver.scala b/school/src/main/scala/pl/edu/agh/school/model/parallel/SchoolConflictResolver.scala index 2391718e..92dc1231 100644 --- a/school/src/main/scala/pl/edu/agh/school/model/parallel/SchoolConflictResolver.scala +++ b/school/src/main/scala/pl/edu/agh/school/model/parallel/SchoolConflictResolver.scala @@ -14,17 +14,24 @@ object SchoolConflictResolver extends ConflictResolver[SchoolConfig] { (current, incoming) match { case (EmptyCell(currentSmell), incomingCell) => (incomingCell.withSmell(incomingCell.smell + currentSmell), SchoolMetrics.empty()) + case (currentCell: SmellingCell, EmptyCell(incomingSmell)) => (currentCell.withSmell(currentCell.smell + incomingSmell), SchoolMetrics.empty()) + case (StudentCell(currentSmell, currentLifespan, _), CleanerCell(energy, incomingSmell, incomingLifespan, pursuedSignalIndex)) => - (CleanerCell(energy + config.algaeEnergeticCapacity, incomingSmell + currentSmell, incomingLifespan, pursuedSignalIndex), SchoolMetrics(0, 0, 0, 0, 0, 1, 0, currentLifespan)) + (CleanerCell(energy, incomingSmell + currentSmell, incomingLifespan, pursuedSignalIndex), SchoolMetrics(0, 0, 0, 0)) + case (CleanerCell(energy, currentSmell, currentLifespan, pursuedSignalIndex), StudentCell(incomingSmell, incomingLifespan, _)) => - (CleanerCell(energy + config.algaeEnergeticCapacity, incomingSmell + currentSmell, currentLifespan, pursuedSignalIndex), SchoolMetrics(0, 0, 0, 0, 0, 1, 0, incomingLifespan)) + (CleanerCell(energy, incomingSmell + currentSmell, currentLifespan, pursuedSignalIndex), SchoolMetrics(0, 0, 0, 0)) + case (StudentCell(currentSmell, lifespan, signalIndex), StudentCell(incomingSmell, incomingLifespan, _)) => (StudentCell(currentSmell + incomingSmell, math.max(lifespan, incomingLifespan), signalIndex), SchoolMetrics.empty()) + case (CleanerCell(currentEnergy, currentSmell, lifespan, _), CleanerCell(incomingEnergy, incomingSmell, incomingLifespan, pursuedSignalIndex)) => // TODO: here are place where two smells can intersect (CleanerCell(currentEnergy + incomingEnergy, currentSmell + incomingSmell, math.max(lifespan, incomingLifespan), pursuedSignalIndex), SchoolMetrics.empty()) + case (Obstacle, _) => (Obstacle, SchoolMetrics.empty()) + case (x, y) => throw new UnsupportedOperationException(s"Unresolved conflict: $x with $y") } } diff --git a/school/src/main/scala/pl/edu/agh/school/simulation/SchoolMetrics.scala b/school/src/main/scala/pl/edu/agh/school/simulation/SchoolMetrics.scala index 508a1e07..a6bff3e4 100644 --- a/school/src/main/scala/pl/edu/agh/school/simulation/SchoolMetrics.scala +++ b/school/src/main/scala/pl/edu/agh/school/simulation/SchoolMetrics.scala @@ -5,43 +5,50 @@ import pl.edu.agh.xinuk.simulation.Metrics /** * Metrics are used to measure actual statistics for eg. current students count */ -final case class SchoolMetrics(foraminiferaCount: Long, - algaeCount: Long, - foraminiferaDeaths: Long, - foraminiferaTotalEnergy: Double, - foraminiferaReproductionsCount: Long, - consumedAlgaeCount: Long, - foraminiferaTotalLifespan: Long, - algaeTotalLifespan: Long) extends Metrics { +final case class SchoolMetrics(studentsCount: Long, + teachersCount: Long, + cleanersCount: Long, + studentsDeaths: Long, + // foraminiferaTotalEnergy: Double, + // foraminiferaReproductionsCount: Long, + // consumedAlgaeCount: Long, + // foraminiferaTotalLifespan: Long, + // algaeTotalLifespan: Long + ) extends Metrics { override def log: String = { - s"$foraminiferaCount;$algaeCount;$foraminiferaDeaths;$foraminiferaTotalEnergy;$foraminiferaReproductionsCount;$consumedAlgaeCount;$foraminiferaTotalLifespan;$algaeTotalLifespan" + s"$studentsCount;$teachersCount;$cleanersCount$studentsDeaths" } override def series: Vector[(String, Double)] = Vector( - "Foraminifera" -> foraminiferaCount, - "Algae" -> algaeCount + "Students" -> studentsCount, + "Teachers" -> teachersCount, + "Cleaners" -> cleanersCount ) override def +(other: Metrics): SchoolMetrics = { other match { case SchoolMetrics.EMPTY => this - case SchoolMetrics(otherForaminiferaCount, otherAlgaeCount, otherForaminiferaDeaths, otherForaminiferaTotalEnergy, - otherForaminiferaReproductionsCount, otherConsumedAlgaeCount, otherForaminiferaTotalLifespan, - otherAlgaeTotalLifespan) => - SchoolMetrics(foraminiferaCount + otherForaminiferaCount, algaeCount + otherAlgaeCount, - foraminiferaDeaths + otherForaminiferaDeaths, foraminiferaTotalEnergy + otherForaminiferaTotalEnergy, - foraminiferaReproductionsCount + otherForaminiferaReproductionsCount, - consumedAlgaeCount + otherConsumedAlgaeCount, foraminiferaTotalLifespan + otherForaminiferaTotalLifespan, - algaeTotalLifespan + otherAlgaeTotalLifespan) + case SchoolMetrics(otherStudentsCount, otherTeachersCount, otherCleanersCount, otherStudentDeaths, +// otherForaminiferaTotalEnergy, +// otherForaminiferaReproductionsCount, otherConsumedAlgaeCount, otherForaminiferaTotalLifespan, +// otherAlgaeTotalLifespan + ) => + SchoolMetrics(studentsCount + otherStudentsCount, teachersCount + otherTeachersCount, + cleanersCount + otherCleanersCount, studentsDeaths + otherStudentDeaths, +// foraminiferaTotalEnergy + otherForaminiferaTotalEnergy, +// foraminiferaReproductionsCount + otherForaminiferaReproductionsCount, +// consumedAlgaeCount + otherConsumedAlgaeCount, foraminiferaTotalLifespan + otherForaminiferaTotalLifespan, +// algaeTotalLifespan + otherAlgaeTotalLifespan + ) case null => this - case _ => throw new UnsupportedOperationException(s"Cannot add: non-ForminMetrics to ForminMetrics") + case _ => throw new UnsupportedOperationException(s"Cannot add: non-SchoolMetrics to SchoolMetrics") } } } object SchoolMetrics { - private val EMPTY = SchoolMetrics(0, 0, 0, 0, 0, 0, 0, 0) + private val EMPTY = SchoolMetrics(0, 0, 0, 0) def empty(): SchoolMetrics = EMPTY -} \ No newline at end of file +} From 5542bd1c02b4bbf9f075775decd32153c38c88ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Wr=C3=B3bel?= Date: Thu, 9 May 2019 00:40:11 +0200 Subject: [PATCH 08/10] Add fix to not propagating signal from dirt; Metrics enhancement --- school/src/main/resources/reference.conf | 16 ++-- .../scala/pl/edu/agh/school/SchoolMain.scala | 25 ++++-- .../algorithm/SchoolMovesController.scala | 88 +++---------------- .../scala/pl/edu/agh/school/model/Dirt.scala | 4 + .../pl/edu/agh/school/model/Student.scala | 12 +-- .../agh/school/simulation/SchoolMetrics.scala | 23 ++--- 6 files changed, 46 insertions(+), 122 deletions(-) diff --git a/school/src/main/resources/reference.conf b/school/src/main/resources/reference.conf index 138a7d9f..475f14af 100644 --- a/school/src/main/resources/reference.conf +++ b/school/src/main/resources/reference.conf @@ -22,19 +22,13 @@ xinuk { school { config { -// foraminiferaStartEnergy = 0.5 -// foraminiferaReproductionCost = 0.5 -// foraminiferaReproductionThreshold = 1 -// foraminiferaLifeActivityCost = 0.1 -// algaeReproductionFrequency = 2 -// algaeEnergeticCapacity = 0.6 signalSpeedRatio = 2 - signalSuppressionFactor = 0.75 - signalAttenuationFactor = 1.2 + signalSuppressionFactor = 0.5 + signalAttenuationFactor = 0.5 gridSize = 100 spawnChance = 0.1 - cleanerSpawnChance = 0.5 + cleanerSpawnChance = 0.2 cleanerInitialSignal: [-1, 0] cleanerInitialSignalIndex: 0 @@ -42,11 +36,11 @@ school { dirtInitialSignal: [1, 0] dirtSignalIndex: 0 - studentSpawnChance: 0.05 + studentSpawnChance: 0.1 studentInitialSignal: [0, 1] studentSignalIndex: 1 - teacherSpawnChance: 0.2 + teacherSpawnChance: 0.5 teacherInitialSignal: [0, -1] teacherSignalIndex: 1 diff --git a/school/src/main/scala/pl/edu/agh/school/SchoolMain.scala b/school/src/main/scala/pl/edu/agh/school/SchoolMain.scala index 2d34a5e0..6b50f868 100644 --- a/school/src/main/scala/pl/edu/agh/school/SchoolMain.scala +++ b/school/src/main/scala/pl/edu/agh/school/SchoolMain.scala @@ -13,14 +13,10 @@ import pl.edu.agh.xinuk.model.{DefaultSmellPropagation, SmellingCell} object SchoolMain extends LazyLogging { private val configPrefix = "school" private val metricHeaders = Vector( // TODO metric headers (it's just for information, but nonetheless...) - "foraminiferaCount", - "algaeCount", - "foraminiferaDeaths", - "foraminiferaTotalEnergy", - "foraminiferaReproductionsCount", - "consumedAlgaeCount", - "foraminiferaTotalLifespan", - "algaeTotalLifespan" + "studentsCount", + "teachersCount", + "cleanersCount", + "dirtCount" ) private def cellToColor(cell: SmellingCell): Color = { @@ -30,10 +26,21 @@ object SchoolMain extends LazyLogging { case StudentCell(_, _, _) => new Color(16, 234, 23) // green case TeacherCell(_, _, _, _) => new Color(255, 10, 10) // red case WallCell(_) => new Color(135, 135, 135) // gray - case _ => Color.WHITE + case _ => cellToColor2(cell) } } + private def cellToColor2(cell: SmellingCell): Color = { + val smellValue = cell.smell + .map(signalVectors => + signalVectors.map(signalVector => + signalVector.value(0).value).max.toFloat).max + val brightness = Math.pow(smellValue, 0.1).toFloat + val hue = 1f + val saturation = 0.69f + Color.getHSBColor(hue, saturation, brightness) + } + def main(args: Array[String]): Unit = { import pl.edu.agh.xinuk.config.ValueReaders._ new Simulation[SchoolConfig](configPrefix, metricHeaders, SchoolConflictResolver, diff --git a/school/src/main/scala/pl/edu/agh/school/algorithm/SchoolMovesController.scala b/school/src/main/scala/pl/edu/agh/school/algorithm/SchoolMovesController.scala index 225fb08b..5e4ececf 100644 --- a/school/src/main/scala/pl/edu/agh/school/algorithm/SchoolMovesController.scala +++ b/school/src/main/scala/pl/edu/agh/school/algorithm/SchoolMovesController.scala @@ -19,25 +19,6 @@ final class SchoolMovesController(bufferZone: TreeSet[(Int, Int)])(implicit conf private val random = new Random(System.nanoTime()) - def initialGrid3: (Grid, Metrics) = { - grid = Grid.empty(bufferZone) - - var gridSize = config.gridSize - - // left - top - grid.cells(gridSize / 2)(gridSize / 4) = TeacherAccessible.unapply(EmptyCell.Instance).withTeacher(Energy(0), 0) - // left - bottom - grid.cells(gridSize / 4)(gridSize / 4) = CleanerAccessible.unapply(EmptyCell.Instance).withCleaner(Energy(0), 0) - - // right - top - grid.cells(gridSize / 4)(gridSize * 3 / 4) = StudentAccessible.unapply(EmptyCell.Instance).withStudent(0) - // right - bottom - grid.cells(gridSize / 2)(gridSize * 3 / 4) = DirtAccessible.unapply(EmptyCell.Instance).withDirt(Energy(0), 0) - - val metrics = SchoolMetrics(1, 1, 1, 0) - (grid, metrics) - } - override def initialGrid: (Grid, SchoolMetrics) = { grid = Grid.empty(bufferZone) var cleanersCount = 0L @@ -152,13 +133,9 @@ final class SchoolMovesController(bufferZone: TreeSet[(Int, Int)])(implicit conf val newGrid = Grid.empty(bufferZone) var studentsCount = 0L - var algaeCount = 0L - var foraminiferaDeaths = 0L - var foraminiferaReproductionsCount = 0L - var consumedAlgaeCount = 0L - var foraminiferaTotalLifespan = 0L - var algaeTotalLifespan = 0L - var foraminiferaTotalEnergy = 0.0 + var teachersCount = 0L + var cleanersCount = 0L + var dirtCount = 0L def isEmptyIn(grid: Grid)(i: Int, j: Int): Boolean = { grid.cells(i)(j) match { @@ -167,21 +144,6 @@ final class SchoolMovesController(bufferZone: TreeSet[(Int, Int)])(implicit conf } } - // def reproduce(x: Int, y: Int)(creator: PartialFunction[GridPart, GridPart]): Unit = { - //// val emptyCells = - //// Grid.neighbourCellCoordinates(x, y).flatMap { - //// case (i, j) => - //// grid.cells(i)(j).opt - //// .filter(_ => creator.isDefinedAt(newGrid.cells(i)(j))) //use the same availability criteria on new grid - //// .collect(creator) - //// .map((i, j, _)) - //// } - //// if (emptyCells.nonEmpty) { - //// val (newAlgaeX, newAlgaeY, newCell) = emptyCells(random.nextInt(emptyCells.size)) - //// newGrid.cells(newAlgaeX)(newAlgaeY) = newCell - //// } - //// } - def makeMove(x: Int, y: Int): Unit = { // order in this pattern match is important (cleaner and teacher must be last matched) this.grid.cells(x)(y) match { @@ -194,43 +156,17 @@ final class SchoolMovesController(bufferZone: TreeSet[(Int, Int)])(implicit conf case cell: DirtCell => if (isEmptyIn(newGrid)(x, y)) { - newGrid.cells(x)(y) = cell + spawnDirtIfPossible(cell, x, y) } case cell: StudentCell => moveStudent(cell, x, y) case cell: CleanerCell => - // if (iteration % config.algaeReproductionFrequency == 0) { - // reproduce(x, y) { case AlgaeAccessible(accessible) => accessible.withAlgae(0) } - // } moveCleaner(cell, x, y) - // if (isEmptyIn(newGrid)(x, y)) { - // newGrid.cells(x)(y) = cell.copy(lifespan = cell.lifespan + 1) - // } case cell: TeacherCell => - // if (cell.energy < config.foraminiferaLifeActivityCost) { - // killForaminifera(cell, x, y) - // } else if (cell.energy > config.foraminiferaReproductionThreshold) { - // reproduceForaminifera(cell, x, y) - // } else { moveTeacher(cell, x, y) - // } } } - // def killForaminifera(cell: CleanerCell, x: Int, y: Int): Unit = { - // foraminiferaDeaths += 1 - // foraminiferaTotalLifespan += cell.lifespan - // val vacated = EmptyCell(cell.smell) - // newGrid.cells(x)(y) = vacated - // grid.cells(x)(y) = vacated - // } - - // def reproduceForaminifera(cell: CleanerCell, x: Int, y: Int): Unit = { - // reproduce(x, y) { case DirtAccessible(accessible) => accessible.withDirt(config.foraminiferaStartEnergy, 0) } - // newGrid.cells(x)(y) = cell.copy(energy = cell.energy - config.foraminiferaReproductionCost, lifespan = cell.lifespan + 1) - // foraminiferaReproductionsCount += 1 - // } - def moveCleaner(cell: CleanerCell, x: Int, y: Int): Unit = { val destinations = calculatePossibleDestinations(cell, x, y, grid) val destination = selectDestinationCell(destinations, newGrid) @@ -301,14 +237,14 @@ final class SchoolMovesController(bufferZone: TreeSet[(Int, Int)])(implicit conf y <- 0 until config.gridSize } { this.grid.cells(x)(y) match { - case DirtCell(energy, _, _, _) => - foraminiferaTotalEnergy += energy.value - studentsCount += 1 - case BufferCell(DirtCell(energy, _, _, _)) => - foraminiferaTotalEnergy += energy.value - studentsCount += 1 + case DirtCell(_, _, _, _) | BufferCell(DirtCell(_, _, _, _)) => + dirtCount += 1 case StudentCell(_, _, _) | BufferCell(StudentCell(_, _, _)) => - algaeCount += 1 + studentsCount += 1 + case CleanerCell(_, _, _,_) | BufferCell(CleanerCell(_, _, _,_)) => + cleanersCount += 1 + case TeacherCell(_, _, _,_) | BufferCell(TeacherCell(_, _, _,_)) => + teachersCount += 1 case _ => } } @@ -318,7 +254,7 @@ final class SchoolMovesController(bufferZone: TreeSet[(Int, Int)])(implicit conf y <- 0 until config.gridSize } makeMove(x, y) - val metrics = SchoolMetrics(studentsCount, algaeCount, 0, foraminiferaDeaths) + val metrics = SchoolMetrics(studentsCount, teachersCount, cleanersCount, dirtCount) (newGrid, metrics) } } diff --git a/school/src/main/scala/pl/edu/agh/school/model/Dirt.scala b/school/src/main/scala/pl/edu/agh/school/model/Dirt.scala index ce4eb3f4..e5e944a3 100644 --- a/school/src/main/scala/pl/edu/agh/school/model/Dirt.scala +++ b/school/src/main/scala/pl/edu/agh/school/model/Dirt.scala @@ -26,10 +26,14 @@ object DirtAccessible { def unapply(arg: BufferCell)(implicit config: SchoolConfig): DirtAccessible[BufferCell] = (energy: Energy, lifespan: Long) => BufferCell(DirtCell(energy, arg.smellWith(config.dirtInitialSignal.toSignalVector), lifespan, config.dirtSignalIndex)) + def unapply(arg: DirtCell)(implicit config: SchoolConfig): DirtAccessible[DirtCell] = + (energy: Energy, lifespan: Long) => DirtCell(energy, arg.smellWith(config.dirtInitialSignal.toSignalVector), lifespan, config.dirtSignalIndex) + def unapply(arg: GridPart)(implicit config: SchoolConfig): Option[DirtAccessible[GridPart]] = arg match { case cell: StudentCell => Some(unapply(cell)) case cell: EmptyCell => Some(unapply(cell)) case cell: BufferCell => Some(unapply(cell)) + case cell: DirtCell => Some(unapply(cell)) case _ => None } } diff --git a/school/src/main/scala/pl/edu/agh/school/model/Student.scala b/school/src/main/scala/pl/edu/agh/school/model/Student.scala index 8ef49031..2082b503 100644 --- a/school/src/main/scala/pl/edu/agh/school/model/Student.scala +++ b/school/src/main/scala/pl/edu/agh/school/model/Student.scala @@ -18,19 +18,13 @@ trait StudentAccessible[+T <: GridPart] { object StudentAccessible { def unapply(arg: EmptyCell)(implicit config: SchoolConfig): StudentAccessible[StudentCell] = - new StudentAccessible[StudentCell] { - override def withStudent(lifespan: Long): StudentCell = StudentCell(arg.smellWith(config.studentInitialSignal.toSignalVector), lifespan, config.studentSignalIndex) - } + (lifespan: Long) => StudentCell(arg.smellWith(config.studentInitialSignal.toSignalVector), lifespan, config.studentSignalIndex) def unapply(arg: BufferCell)(implicit config: SchoolConfig): StudentAccessible[BufferCell] = - new StudentAccessible[BufferCell] { - override def withStudent(lifespan: Long): BufferCell = BufferCell(StudentCell(arg.smellWith(config.studentInitialSignal.toSignalVector), lifespan, config.studentSignalIndex)) - } + (lifespan: Long) => BufferCell(StudentCell(arg.smellWith(config.studentInitialSignal.toSignalVector), lifespan, config.studentSignalIndex)) def unapply(arg: DirtCell)(implicit config: SchoolConfig): StudentAccessible[StudentCell] = - new StudentAccessible[StudentCell] { - override def withStudent(lifespan: Long): StudentCell = StudentCell(arg.smellWith(config.studentInitialSignal.toSignalVector), lifespan, config.studentSignalIndex) - } + (lifespan: Long) => StudentCell(arg.smellWith(config.studentInitialSignal.toSignalVector), lifespan, config.studentSignalIndex) def unapply(arg: GridPart)(implicit config: SchoolConfig): Option[StudentAccessible[GridPart]] = arg match { case cell: EmptyCell => Some(unapply(cell)) diff --git a/school/src/main/scala/pl/edu/agh/school/simulation/SchoolMetrics.scala b/school/src/main/scala/pl/edu/agh/school/simulation/SchoolMetrics.scala index a6bff3e4..14e24c0a 100644 --- a/school/src/main/scala/pl/edu/agh/school/simulation/SchoolMetrics.scala +++ b/school/src/main/scala/pl/edu/agh/school/simulation/SchoolMetrics.scala @@ -8,38 +8,27 @@ import pl.edu.agh.xinuk.simulation.Metrics final case class SchoolMetrics(studentsCount: Long, teachersCount: Long, cleanersCount: Long, - studentsDeaths: Long, - // foraminiferaTotalEnergy: Double, - // foraminiferaReproductionsCount: Long, - // consumedAlgaeCount: Long, - // foraminiferaTotalLifespan: Long, - // algaeTotalLifespan: Long + dirtCount: Long ) extends Metrics { override def log: String = { - s"$studentsCount;$teachersCount;$cleanersCount$studentsDeaths" + s"$studentsCount;$teachersCount;$cleanersCount;$dirtCount" } override def series: Vector[(String, Double)] = Vector( "Students" -> studentsCount, "Teachers" -> teachersCount, - "Cleaners" -> cleanersCount + "Cleaners" -> cleanersCount, + "Dirt" -> dirtCount ) override def +(other: Metrics): SchoolMetrics = { other match { case SchoolMetrics.EMPTY => this - case SchoolMetrics(otherStudentsCount, otherTeachersCount, otherCleanersCount, otherStudentDeaths, -// otherForaminiferaTotalEnergy, -// otherForaminiferaReproductionsCount, otherConsumedAlgaeCount, otherForaminiferaTotalLifespan, -// otherAlgaeTotalLifespan + case SchoolMetrics(otherStudentsCount, otherTeachersCount, otherCleanersCount, otherDirtCount ) => SchoolMetrics(studentsCount + otherStudentsCount, teachersCount + otherTeachersCount, - cleanersCount + otherCleanersCount, studentsDeaths + otherStudentDeaths, -// foraminiferaTotalEnergy + otherForaminiferaTotalEnergy, -// foraminiferaReproductionsCount + otherForaminiferaReproductionsCount, -// consumedAlgaeCount + otherConsumedAlgaeCount, foraminiferaTotalLifespan + otherForaminiferaTotalLifespan, -// algaeTotalLifespan + otherAlgaeTotalLifespan + cleanersCount + otherCleanersCount, dirtCount + otherDirtCount ) case null => this case _ => throw new UnsupportedOperationException(s"Cannot add: non-SchoolMetrics to SchoolMetrics") From e16e324ed88c3848d94f5a7374579d1adaaaf274 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Wr=C3=B3bel?= Date: Sun, 12 May 2019 18:57:43 +0200 Subject: [PATCH 09/10] Changes after code review --- .../school/algorithm/SchoolMovesController.scala | 16 +++++----------- .../agh/school/simulation/SchoolMetrics.scala | 3 +-- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/school/src/main/scala/pl/edu/agh/school/algorithm/SchoolMovesController.scala b/school/src/main/scala/pl/edu/agh/school/algorithm/SchoolMovesController.scala index 5e4ececf..2df84eb1 100644 --- a/school/src/main/scala/pl/edu/agh/school/algorithm/SchoolMovesController.scala +++ b/school/src/main/scala/pl/edu/agh/school/algorithm/SchoolMovesController.scala @@ -32,21 +32,16 @@ final class SchoolMovesController(bufferZone: TreeSet[(Int, Int)])(implicit conf if (random.nextDouble() < config.spawnChance) { val chance = random.nextDouble() Math.abs(random.nextInt()) % 3 match { - case 0 => - if (chance < config.cleanerSpawnChance) { + case 0 if chance < config.cleanerSpawnChance => cleanersCount += 1 grid.cells(x)(y) = CleanerAccessible.unapply(EmptyCell.Instance).withCleaner(Energy(0), 0) - } - case 1 => - if (chance < config.teacherSpawnChance) { + case 1 if chance < config.teacherSpawnChance => teachersCount += 1 grid.cells(x)(y) = TeacherAccessible.unapply(EmptyCell.Instance).withTeacher(Energy(0), 0) - } - case 2 => - if (chance < config.studentSpawnChance) { + case 2 if chance < config.studentSpawnChance => studentsCount += 1 grid.cells(x)(y) = StudentAccessible.unapply(EmptyCell.Instance).withStudent(0) - } + case _ => } } } @@ -58,14 +53,13 @@ final class SchoolMovesController(bufferZone: TreeSet[(Int, Int)])(implicit conf def calculatePossibleDestinations(cell: CleanerCell, x: Int, y: Int, grid: Grid): Iterator[(Int, Int, GridPart)] = { val neighbourCellCoordinates = Grid.neighbourCellCoordinates(x, y) - val tuples = Grid.SubcellCoordinates + Grid.SubcellCoordinates .map { case (i, j) => cell.smell(i)(j) } .zipWithIndex .map { case (signalVector, index) => (signalVector(cell.signalIndex), index) } .sorted(implicitly[Ordering[(Signal, Int)]].reverse) - tuples .iterator .map { case (_, idx) => val (i, j) = neighbourCellCoordinates(idx) diff --git a/school/src/main/scala/pl/edu/agh/school/simulation/SchoolMetrics.scala b/school/src/main/scala/pl/edu/agh/school/simulation/SchoolMetrics.scala index 14e24c0a..71388741 100644 --- a/school/src/main/scala/pl/edu/agh/school/simulation/SchoolMetrics.scala +++ b/school/src/main/scala/pl/edu/agh/school/simulation/SchoolMetrics.scala @@ -25,8 +25,7 @@ final case class SchoolMetrics(studentsCount: Long, override def +(other: Metrics): SchoolMetrics = { other match { case SchoolMetrics.EMPTY => this - case SchoolMetrics(otherStudentsCount, otherTeachersCount, otherCleanersCount, otherDirtCount - ) => + case SchoolMetrics(otherStudentsCount, otherTeachersCount, otherCleanersCount, otherDirtCount) => SchoolMetrics(studentsCount + otherStudentsCount, teachersCount + otherTeachersCount, cleanersCount + otherCleanersCount, dirtCount + otherDirtCount ) From 011d37b021a3dc67c7711f9efb519c746934aabb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Zieli=C5=84ski?= Date: Mon, 20 May 2019 01:29:42 +0200 Subject: [PATCH 10/10] Move signals number to config, resolve cell conflicts --- school/src/main/resources/reference.conf | 8 +++--- .../scala/pl/edu/agh/school/SchoolMain.scala | 3 ++- .../edu/agh/school/config/SchoolConfig.scala | 1 + .../parallel/SchoolConflictResolver.scala | 27 ++++++++++++++++++- .../scala/pl/edu/agh/xinuk/Simulation.scala | 5 ++++ .../pl/edu/agh/xinuk/config/XinukConfig.scala | 1 + .../scala/pl/edu/agh/xinuk/model/Grid.scala | 3 ++- 7 files changed, 42 insertions(+), 6 deletions(-) diff --git a/school/src/main/resources/reference.conf b/school/src/main/resources/reference.conf index 475f14af..224ad1f3 100644 --- a/school/src/main/resources/reference.conf +++ b/school/src/main/resources/reference.conf @@ -25,7 +25,9 @@ school { signalSpeedRatio = 2 signalSuppressionFactor = 0.5 signalAttenuationFactor = 0.5 - gridSize = 100 + signalsNumber = 2 + + gridSize = 50 spawnChance = 0.1 cleanerSpawnChance = 0.2 @@ -40,13 +42,13 @@ school { studentInitialSignal: [0, 1] studentSignalIndex: 1 - teacherSpawnChance: 0.5 + teacherSpawnChance: 0.3 teacherInitialSignal: [0, -1] teacherSignalIndex: 1 guiType = basic guiCellSize = 4 - workersRoot = 1 + workersRoot = 2 iterationsNumber = 10000 isSupervisor = true shardingMod = 144 diff --git a/school/src/main/scala/pl/edu/agh/school/SchoolMain.scala b/school/src/main/scala/pl/edu/agh/school/SchoolMain.scala index 6b50f868..6e55234e 100644 --- a/school/src/main/scala/pl/edu/agh/school/SchoolMain.scala +++ b/school/src/main/scala/pl/edu/agh/school/SchoolMain.scala @@ -26,7 +26,8 @@ object SchoolMain extends LazyLogging { case StudentCell(_, _, _) => new Color(16, 234, 23) // green case TeacherCell(_, _, _, _) => new Color(255, 10, 10) // red case WallCell(_) => new Color(135, 135, 135) // gray - case _ => cellToColor2(cell) + case _ => Color.WHITE + //case _ => cellToColor2(cell) } } diff --git a/school/src/main/scala/pl/edu/agh/school/config/SchoolConfig.scala b/school/src/main/scala/pl/edu/agh/school/config/SchoolConfig.scala index 6b109f70..ae873b6c 100644 --- a/school/src/main/scala/pl/edu/agh/school/config/SchoolConfig.scala +++ b/school/src/main/scala/pl/edu/agh/school/config/SchoolConfig.scala @@ -29,6 +29,7 @@ final case class SchoolConfig( signalSpeedRatio: Int, signalSuppressionFactor: Double, signalAttenuationFactor: Double, + signalsNumber: Int, gridSize: Int, spawnChance: Double, diff --git a/school/src/main/scala/pl/edu/agh/school/model/parallel/SchoolConflictResolver.scala b/school/src/main/scala/pl/edu/agh/school/model/parallel/SchoolConflictResolver.scala index 92dc1231..e82e0c17 100644 --- a/school/src/main/scala/pl/edu/agh/school/model/parallel/SchoolConflictResolver.scala +++ b/school/src/main/scala/pl/edu/agh/school/model/parallel/SchoolConflictResolver.scala @@ -27,9 +27,34 @@ object SchoolConflictResolver extends ConflictResolver[SchoolConfig] { case (StudentCell(currentSmell, lifespan, signalIndex), StudentCell(incomingSmell, incomingLifespan, _)) => (StudentCell(currentSmell + incomingSmell, math.max(lifespan, incomingLifespan), signalIndex), SchoolMetrics.empty()) - case (CleanerCell(currentEnergy, currentSmell, lifespan, _), CleanerCell(incomingEnergy, incomingSmell, incomingLifespan, pursuedSignalIndex)) => // TODO: here are place where two smells can intersect + case (CleanerCell(currentEnergy, currentSmell, lifespan, _), CleanerCell(incomingEnergy, incomingSmell, incomingLifespan, pursuedSignalIndex)) => (CleanerCell(currentEnergy + incomingEnergy, currentSmell + incomingSmell, math.max(lifespan, incomingLifespan), pursuedSignalIndex), SchoolMetrics.empty()) + case (TeacherCell(currentEnergy, currentSmell, lifespan, _), DirtCell(incomingEnergy, incomingSmell, incomingLifespan, pursuedSignalIndex)) => + (TeacherCell(currentEnergy - incomingEnergy, currentSmell + incomingSmell, math.max(lifespan, incomingLifespan), pursuedSignalIndex), SchoolMetrics.empty()) + + case (DirtCell(currentEnergy, currentSmell, lifespan, _), TeacherCell(incomingEnergy, incomingSmell, incomingLifespan, pursuedSignalIndex)) => + (TeacherCell(incomingEnergy - currentEnergy, currentSmell + incomingSmell, math.max(lifespan, incomingLifespan), pursuedSignalIndex), SchoolMetrics.empty()) + + case (StudentCell(currentSmell, lifespan, signalIndex), TeacherCell(incomingEnergy, incomingSmell, incomingLifespan, pursuedSignalIndex)) => + (TeacherCell(incomingEnergy, currentSmell + incomingSmell, math.max(lifespan, incomingLifespan), signalIndex), SchoolMetrics.empty()) + + case (TeacherCell(incomingEnergy, incomingSmell, incomingLifespan, pursuedSignalIndex), StudentCell(currentSmell, lifespan, signalIndex)) => + (TeacherCell(incomingEnergy, currentSmell + incomingSmell, math.max(lifespan, incomingLifespan), signalIndex), SchoolMetrics.empty()) + + case (TeacherCell(currentEnergy, currentSmell, lifespan, _), CleanerCell(incomingEnergy, incomingSmell, incomingLifespan, pursuedSignalIndex)) => + (TeacherCell(currentEnergy - incomingEnergy, currentSmell + incomingSmell, math.max(lifespan, incomingLifespan), pursuedSignalIndex), SchoolMetrics.empty()) + + case (TeacherCell(currentEnergy, currentSmell, lifespan, _), TeacherCell(incomingEnergy, incomingSmell, incomingLifespan, pursuedSignalIndex)) => + (TeacherCell(currentEnergy + incomingEnergy, currentSmell + incomingSmell, math.max(lifespan, incomingLifespan), pursuedSignalIndex), SchoolMetrics.empty()) + + case (CleanerCell(incomingEnergy, incomingSmell, incomingLifespan, pursuedSignalIndex), TeacherCell(currentEnergy, currentSmell, lifespan, _)) => + (TeacherCell(currentEnergy - incomingEnergy, currentSmell + incomingSmell, math.max(lifespan, incomingLifespan), pursuedSignalIndex), SchoolMetrics.empty()) + + case (DirtCell(currentEnergy, currentSmell, lifespan, _), CleanerCell(incomingEnergy, incomingSmell, incomingLifespan, pursuedSignalIndex)) => + (CleanerCell(currentEnergy + incomingEnergy, currentSmell + incomingSmell, math.max(lifespan, incomingLifespan), pursuedSignalIndex), SchoolMetrics.empty()) + + case (Obstacle, _) => (Obstacle, SchoolMetrics.empty()) case (x, y) => throw new UnsupportedOperationException(s"Unresolved conflict: $x with $y") 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 ff18267d..aee99279 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 @@ -47,6 +47,7 @@ class Simulation[ConfigType <: XinukConfig : ValueReader]( import net.ceedubs.ficus.Ficus._ Try(forminConfig.as[ConfigType]("config")) match { case Success(parsedConfig) => + Simulation.config = parsedConfig parsedConfig case Failure(parsingError) => logger.error("Config parsing error.", parsingError) @@ -84,4 +85,8 @@ class Simulation[ConfigType <: XinukConfig : ValueReader]( } } +} + +object Simulation { + var config: XinukConfig = _ } \ 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..6df030d0 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 @@ -7,6 +7,7 @@ trait XinukConfig { def guiCellSize: Int def signalSuppressionFactor: Double def signalAttenuationFactor: Double + def signalsNumber: Int def workersRoot: Int def shardingMod: Int diff --git a/xinuk-core/src/main/scala/pl/edu/agh/xinuk/model/Grid.scala b/xinuk-core/src/main/scala/pl/edu/agh/xinuk/model/Grid.scala index aa945707..141aec62 100644 --- a/xinuk-core/src/main/scala/pl/edu/agh/xinuk/model/Grid.scala +++ b/xinuk-core/src/main/scala/pl/edu/agh/xinuk/model/Grid.scala @@ -1,5 +1,6 @@ package pl.edu.agh.xinuk.model +import pl.edu.agh.xinuk.Simulation import pl.edu.agh.xinuk.config.XinukConfig import pl.edu.agh.xinuk.model.Cell.SmellArray import pl.edu.agh.xinuk.model.Grid.CellArray @@ -75,7 +76,7 @@ object SignalVector { def toSignalVector: SignalVector = SignalVector(signalList.toArray) } - final val Signals = 2 + final val Signals = Simulation.config.signalsNumber final val Zero = SignalVector(Array.fill(Signals)(Signal.Zero)) }