package snake /** m09 app: the whole game wired together as values: streams of actions in, a stream of drawings out. */ object App: val TICK_MS: Long = 150 val FRAME_MS: Long = 25 // Everything that can happen to the game: a Tick every TICK_MS, and an action for each bound key. lazy val classicActions: Stream[Action] = Stream.interval(TICK_MS).map(_ => Action.Tick).merge(Stream.keyDowns.filterMap(Input.keyToAction)) // m10: a Frame every FRAME_MS instead of a Tick; the game itself decides (movesEvery) which frames take a step. lazy val speedActions: Stream[Action] = Stream.interval(FRAME_MS).map(_ => Action.Frame).merge(Stream.keyDowns.filterMap(Input.keyToAction)) // Actions folded into game states. startWith puts the initial state first so it is drawn right away. def states(seed: Long, actions: Stream[Action]): Stream[GameState] = actions.scan(Spawn.newGame(seed))(Game.update).startWith(Spawn.newGame(seed)) def render(state: GameState): IO[Unit] = IO.drawScene(View.view(state)) // The whole program as a value: get a seed, then turn the stream of states into a stream of drawings. // Nothing runs here; Runtime interprets it. lazy val main: IO[Stream[IO[Unit]]] = IO.randomSeed.map(seed => states(seed, speedActions).map(render))