Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/main/scala/floodsim/model/grid/GridElement.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,10 @@ final case class CellGridElement(element: Cell) extends GridElement:
def asSymbol: String = element.cellType.symbol

final case class WeatherGridElement(element: Weather) extends GridElement:
def asSymbol: String = element.symbol
def asSymbol: String = element match
case Weather.ClearSky => "o"
case Weather.LightClouds => "."
case Weather.HeavyClouds => ":"
case Weather.LightRain => "`"
case Weather.HeavyRain => "~"
case Weather.IntenseSun => "*"
31 changes: 17 additions & 14 deletions src/main/scala/floodsim/model/weather/Weather.scala
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
package floodsim.model.weather

enum Weather(val waterDelta: Double, val symbol: String):
case ClearSky extends Weather(-0.1, "o")
case LightClouds extends Weather(0.0, ".")
case HeavyClouds extends Weather(0.2, ":")
case LightRain extends Weather(0.5, "`")
case HeavyRain extends Weather(1.0, "~")
case IntenseSun extends Weather(-0.5, "*")
import scala.util.Random

enum Weather(val waterDelta: Double):
case IntenseSun extends Weather(-0.5)
case ClearSky extends Weather(-0.1)
case LightClouds extends Weather(0.0)
case HeavyClouds extends Weather(0.2)
case LightRain extends Weather(0.5)
case HeavyRain extends Weather(1.0)

object Weather:
def random: Weather =
val values = Weather.values
values(scala.util.Random.nextInt(values.length))
values(Random.nextInt(values.length))

def forIntensity(intensity: Double): Weather =
intensity match
case i if i < -0.3 => IntenseSun
case i if i < 0.0 => ClearSky
case i if i < 0.3 => LightClouds
case i if i < 0.6 => HeavyClouds
case i if i < 0.8 => LightRain
case _ => HeavyRain
case x if x <= -0.1 => IntenseSun
case x if x <= 0.0 => ClearSky
case x if x <= 0.2 => LightClouds
case x if x <= 0.5 => HeavyClouds
case x if x <= 1.0 => LightRain
case _ => HeavyRain

108 changes: 44 additions & 64 deletions src/test/scala/floodsim/model/weather/WeatherTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,91 +3,71 @@ package floodsim.model.weather
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

class WeatherTest extends AnyFlatSpec with Matchers {
class WeatherTest extends AnyFlatSpec with Matchers:

"Weather values" should "have correct waterDelta values" in {
"Weather enum" should "have correct waterDelta values" in {
Weather.IntenseSun.waterDelta shouldBe -0.5
Weather.ClearSky.waterDelta shouldBe -0.1
Weather.LightClouds.waterDelta shouldBe 0.0
Weather.HeavyClouds.waterDelta shouldBe 0.2
Weather.LightRain.waterDelta shouldBe 0.5
Weather.HeavyRain.waterDelta shouldBe 1.0
Weather.IntenseSun.waterDelta shouldBe -0.5
}

"Weather values" should "be singletons" in {
val firstClearSky = Weather.ClearSky
val secondClearSky = Weather.ClearSky
assert(firstClearSky eq secondClearSky)

Weather.values.foreach { weather =>
val firstInstance = weather
val secondInstance = weather
assert(firstInstance eq secondInstance)
}
"Weather.random" should "return a valid Weather instance" in {
for _ <- 1 to 100 do
val result = Weather.random
Weather.values should contain(result)
}

"Weather enum" should "be sealed to allow exhaustive pattern matching" in {
val values: List[Weather] = List(
Weather.ClearSky,
Weather.LightClouds,
Weather.HeavyClouds,
Weather.LightRain,
Weather.HeavyRain,
Weather.IntenseSun
)

values.foreach {
case Weather.ClearSky =>
case Weather.LightClouds =>
case Weather.HeavyClouds =>
case Weather.LightRain =>
case Weather.HeavyRain =>
case Weather.IntenseSun =>
}
"Weather.forIntensity" should "return IntenseSun for intensities <= -0.1" in {
Weather.forIntensity(-100.0) shouldBe Weather.IntenseSun
Weather.forIntensity(-1.0) shouldBe Weather.IntenseSun
Weather.forIntensity(-0.1) shouldBe Weather.IntenseSun
}

"Weather values" should "have appropriate water delta values for their type" in {
List(Weather.HeavyClouds, Weather.LightRain, Weather.HeavyRain).foreach { weather =>
weather.waterDelta should be > 0.0
}
it should "return ClearSky for intensities between -0.1 and 0.0" in {
Weather.forIntensity(-0.09) shouldBe Weather.ClearSky
Weather.forIntensity(-0.05) shouldBe Weather.ClearSky
Weather.forIntensity(0.0) shouldBe Weather.ClearSky
}

List(Weather.ClearSky, Weather.IntenseSun).foreach { weather =>
weather.waterDelta should be < 0.0
}
it should "return LightClouds for intensities between 0.0 and 0.2" in {
Weather.forIntensity(0.01) shouldBe Weather.LightClouds
Weather.forIntensity(0.1) shouldBe Weather.LightClouds
Weather.forIntensity(0.2) shouldBe Weather.LightClouds
}

Weather.LightClouds.waterDelta shouldBe 0.0
it should "return HeavyClouds for intensities between 0.2 and 0.5" in {
Weather.forIntensity(0.21) shouldBe Weather.HeavyClouds
Weather.forIntensity(0.3) shouldBe Weather.HeavyClouds
Weather.forIntensity(0.5) shouldBe Weather.HeavyClouds
}

"All weather values" should "be accessible through Weather.values" in {
val expectedValues = Set(
Weather.ClearSky,
Weather.LightClouds,
Weather.HeavyClouds,
Weather.LightRain,
Weather.HeavyRain,
Weather.IntenseSun
)
it should "return LightRain for intensities between 0.5 and 1.0" in {
Weather.forIntensity(0.51) shouldBe Weather.LightRain
Weather.forIntensity(0.75) shouldBe Weather.LightRain
Weather.forIntensity(1.0) shouldBe Weather.LightRain
}

Weather.values.toSet shouldBe expectedValues
Weather.values should have size 6
it should "return HeavyRain for intensities > 1.0" in {
Weather.forIntensity(1.01) shouldBe Weather.HeavyRain
Weather.forIntensity(2.0) shouldBe Weather.HeavyRain
Weather.forIntensity(Double.MaxValue) shouldBe Weather.HeavyRain
}

"Weather.random" should "return a valid Weather value" in {
val randomWeather = Weather.random
Weather.values should contain(randomWeather)
it should "handle edge cases correctly" in {
Weather.forIntensity(Double.NegativeInfinity) shouldBe Weather.IntenseSun
Weather.forIntensity(Double.MinValue) shouldBe Weather.IntenseSun
Weather.forIntensity(Double.PositiveInfinity) shouldBe Weather.HeavyRain
Weather.forIntensity(Double.NaN) shouldBe Weather.HeavyRain
}

"Weather.forIntensity" should "return correct Weather for given intensity" in {
Weather.forIntensity(-0.4) shouldBe Weather.IntenseSun
Weather.forIntensity(-0.3) shouldBe Weather.ClearSky
Weather.forIntensity(-0.1) shouldBe Weather.ClearSky
Weather.forIntensity(0.0) shouldBe Weather.LightClouds
it should "match exact boundary values correctly" in {
Weather.forIntensity(-0.1) shouldBe Weather.IntenseSun
Weather.forIntensity(0.0) shouldBe Weather.ClearSky
Weather.forIntensity(0.2) shouldBe Weather.LightClouds
Weather.forIntensity(0.3) shouldBe Weather.HeavyClouds
Weather.forIntensity(0.5) shouldBe Weather.HeavyClouds
Weather.forIntensity(0.6) shouldBe Weather.LightRain
Weather.forIntensity(0.7) shouldBe Weather.LightRain
Weather.forIntensity(0.8) shouldBe Weather.HeavyRain
Weather.forIntensity(1.0) shouldBe Weather.HeavyRain
Weather.forIntensity(1.0) shouldBe Weather.LightRain
}
}
Loading