package snake package life import LifeModel.* /** m13 life app: the same wiring as Snake and Pong, with a third kind of input, the mouse. */ object LifeApp: val GEN_MS: Long = 150 def lifeKey(key: String): Option[LifeAction] = key match case " " => Some(LifeAction.TogglePause) case "n" | "N" => Some(LifeAction.Step) case "c" | "C" => Some(LifeAction.Clear) case "r" | "R" => Some(LifeAction.Randomize) case _ => None // The pixel is checked before dividing: integer division rounds towards zero, so -1 / 16 would be cell 0. def lifeClick(x: Int, y: Int): Option[LifeAction] = if x >= 0 && y >= 0 && x / LCELL < LW && y / LCELL < LH then Some(LifeAction.Toggle(Point(x / LCELL, y / LCELL))) else None lazy val lifeActions: Stream[LifeAction] = Stream .interval(GEN_MS) .map(_ => LifeAction.Frame) .merge(Stream.keyDowns.filterMap(lifeKey)) .merge(Stream.clicks.filterMap(lifeClick(_, _))) def lifeStates(seed: Long, actions: Stream[LifeAction]): Stream[LifeState] = actions.scan(initialLife(seed))(LifeRules.lifeUpdate).startWith(initialLife(seed)) lazy val lifeMain: IO[Stream[IO[Unit]]] = IO.randomSeed.map(seed => lifeStates(seed, lifeActions).map(state => IO.drawScene(LifeView.lifeView(state))).clampTime(App.MAX_GAP_MS) )