package snake import java.nio.file.{Files, Paths} /** Loads shared fixtures from ../fixtures/.json and decodes them into the course's types. */ object Fixtures: private val dir = Paths.get(sys.props.getOrElse("user.dir", ".")).resolve("..").resolve("fixtures").normalize def load(name: String): ujson.Value = ujson.read(Files.readString(dir.resolve(s"$name.json"))) /** The cases array of a fixture. */ def cases(name: String): Seq[ujson.Value] = load(name)("cases").arr.toSeq /** Decode a nullable JSON value into an Option. */ def opt[A](v: ujson.Value)(f: ujson.Value => A): Option[A] = if v.isNull then None else Some(f(v)) def point(v: ujson.Value): Point = Point(v("x").num.toInt, v("y").num.toInt) def direction(v: ujson.Value): Direction = Direction.valueOf(v.str) def status(v: ujson.Value): Status = Status.valueOf(v.str) def action(v: ujson.Value): Action = v("tag").str match case "Tick" => Action.Tick case "Turn" => Action.Turn(direction(v("dir"))) case "TogglePause" => Action.TogglePause case "Restart" => Action.Restart case "Frame" => Action.Frame case other => throw new IllegalArgumentException(s"unknown action tag: $other") def gameState(v: ujson.Value): GameState = GameState( width = v("width").num.toInt, height = v("height").num.toInt, snake = v("snake").arr.toList.map(point), dir = direction(v("dir")), nextDir = direction(v("nextDir")), apple = opt(v("apple"))(point), seed = v("seed").num.toLong, score = v("score").num.toInt, status = status(v("status")), frame = v("frame").num.toInt ) def shape(v: ujson.Value): Shape = v("tag").str match case "Rect" => Shape.Rect(v("x").num.toInt, v("y").num.toInt, v("w").num.toInt, v("h").num.toInt, v("color").str) case "Text" => Shape.Text(v("x").num.toInt, v("y").num.toInt, v("text").str, v("color").str, v("size").num.toInt, v("align").str) case other => throw new IllegalArgumentException(s"unknown shape tag: $other") def list[A](v: ujson.Value)(f: ujson.Value => A): List[A] = v.arr.toList.map(f)