85 lines
2.2 KiB
Scala
85 lines
2.2 KiB
Scala
package dev.ctsk.aoc
|
|
|
|
import scala.reflect.ClassTag
|
|
|
|
object Point:
|
|
def of(x: Long, y: Long): Point = Point(x.toInt, y.toInt)
|
|
|
|
case class Point(x: Int, y: Int):
|
|
def +(o: Point): Point = Point(x + o.x, y + o.y)
|
|
def -(o: Point): Point = Point(x - o.x, y - o.y)
|
|
def *(n: Int): Point = Point(n * x, n * y)
|
|
def reduce: Point = { val g = gcd(x.abs, y.abs); Point(x / g, y / g) }
|
|
|
|
enum Direction:
|
|
case Up, Right, Down, Left
|
|
def turnRight: Direction = Direction.fromOrdinal((ordinal + 1) % 4)
|
|
def turnLeft: Direction = Direction.fromOrdinal((ordinal - 1 + 4) % 4)
|
|
|
|
def toPoint: Point =
|
|
this match
|
|
case Up => Point(-1, 0)
|
|
case Right => Point(0, 1)
|
|
case Down => Point(1, 0)
|
|
case Left => Point(0, -1)
|
|
|
|
def apply(p: Point): Point = p + toPoint
|
|
|
|
def applyN(p: Point, n: Int): Point = p + toPoint * n
|
|
|
|
def flip: Direction =
|
|
this match
|
|
case Up => Down
|
|
case Right => Left
|
|
case Down => Up
|
|
case Left => Right
|
|
|
|
case class Pose(pos: Point, dir: Direction):
|
|
def turnRight: Pose = Pose(pos, dir.turnRight)
|
|
def step: Pose = Pose(dir(pos), dir)
|
|
def step(n: Int): Pose = Pose(dir.applyN(pos, n), dir)
|
|
|
|
class Grid[A](val data: Array[Array[A]]):
|
|
def height: Int = data.length
|
|
|
|
def width: Int = data(0).length
|
|
|
|
def apply(p: Point): Option[A] =
|
|
if p.x < 0 | p.y < 0 | p.x >= height | p.y >= width
|
|
then None
|
|
else Some(data(p.x)(p.y))
|
|
|
|
def update(p: Point, a: A): Unit = data(p.x)(p.y) = a
|
|
|
|
def find(f: A => Boolean): IndexedSeq[Point] =
|
|
for
|
|
i <- data.indices
|
|
j <- data(i).indices
|
|
if f(data(i)(j))
|
|
yield Point(i, j)
|
|
|
|
def findFirst(f: A => Boolean): Option[Point] =
|
|
data.zipWithIndex.flatMap { (row, x) =>
|
|
row.zipWithIndex.collect { case (a, y) if f(a) => Point(x, y) }
|
|
}.headOption
|
|
|
|
def count(f: A => Boolean): Int = data.map(_.count(f)).sum
|
|
|
|
def contains(p: Point): Boolean =
|
|
p.x >= 0 && p.y >= 0 && p.x < height && p.y < width
|
|
|
|
def points: Seq[Point] =
|
|
for
|
|
x <- data.indices
|
|
y <- data(x).indices
|
|
yield Point(x, y)
|
|
|
|
def fill(value: A): Unit =
|
|
for
|
|
row <- 0 until height
|
|
col <- 0 until width
|
|
do data(row)(col) = value
|
|
|
|
def sameSizeWith[B: ClassTag](value: B) =
|
|
Grid(Array.fill(height, width)(value))
|