package snake package web import scala.scalajs.js import scala.scalajs.js.Dynamic.global import scala.scalajs.js.typedarray.Uint32Array /** * Browser glue: turns a canvas into a Platform. Impure by nature; not part of the exercises' logic. * `js.Dynamic` lets us call browser APIs by name without type facades (and without extra libraries). */ final class BrowserPlatform(canvas: js.Dynamic) extends Platform: private val ctx = canvas.getContext("2d") private val window = global.window // Keys that scroll the page should only steer the game. private val prevent = Set("ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", " ") private def listen(kind: String, cb: String => Unit): Unit = val handler: js.Function1[js.Dynamic, Unit] = e => val key = e.key.asInstanceOf[String] if prevent(key) then e.preventDefault() cb(key) window.addEventListener(kind, handler) def fillRect(x: Int, y: Int, w: Int, h: Int, color: String): Unit = ctx.fillStyle = color ctx.fillRect(x, y, w, h) def fillText(x: Int, y: Int, text: String, color: String, size: Int, align: String): Unit = ctx.fillStyle = color ctx.font = s"${size}px monospace" ctx.textAlign = align ctx.fillText(text, x, y) def randomSeed(): Long = val buf = new Uint32Array(1) global.crypto.getRandomValues(buf) buf(0).toLong // t is the number of milliseconds since the first animation frame. def startClock(onTime: Long => Unit): Unit = def frame(start: Option[Double]): js.Function1[Double, Unit] = now => val t0 = start.getOrElse(now) onTime((now - t0).toLong) window.requestAnimationFrame(frame(Some(t0))) window.requestAnimationFrame(frame(None)) def onKeyDown(cb: String => Unit): Unit = listen("keydown", cb) def onKeyUp(cb: String => Unit): Unit = listen("keyup", cb)