While Implementing a generalized version of tic-toc-toe I got a bit sidetracked. I wanted to try to use refined types for the points in the field and the result is a small library, pigeonhole! Maybe this was just an exercise in excessive type safety, but it was fun to do!
Up until the sonatype release.
Usage
import com.fmsbeekmans.pigeonhole._
import eu.timepit.refined.auto._
import eu.timepit.refined.W
val `threeByThree`: Grid[W.`3`.T, W.`3`.T] = Grid(3, 3)
val `fourByFour`: Grid[W.`4`.T, W.`4`.T] = Grid(4, 4)
The points in a grid are refined to the dimensions of the grid they are in.
scala> fourByFour.points
res0: cats.data.NonEmptyList[com.fmsbeekmans.pigeonhole.Point[Int(4),Int(4)]] = NonEmptyList(Point(0,0), Point(0,1), Point(0,2), Point(0,3), Point(1,0), Point(1,1), Point(1,2), Point(1,3), Point(2,0), Point(2,1), Point(2,2), Point(2,3), Point(3,0), Point(3,1), Point(3,2), Point(3,3))
They also understand steps
scala> val chessBoard = Grid[W.`8`.T, W.`8`.T](8, 8)
chessBoard: com.fmsbeekmans.pigeonhole.Grid[Int(8),Int(8)] = Grid(8,8)
scala> val knightsJump = Step(2, 1)
knightsJump: com.fmsbeekmans.pigeonhole.Step = Step(2,1)
scala> val knightStartPosition = chessBoard.refinePoint(1, 0).right.get
knightStartPosition: chessBoard.Pt = Point(1,0)
scala> knightStartPosition.add(knightsJump)
res1: Option[com.fmsbeekmans.pigeonhole.Point[Int(8),Int(8)]] = Some(Point(3,1))
Or lines in a direction
scala> val bishopAtF3 = chessBoard.refinePoint(5, 2).right.get
bishopAtF3: chessBoard.Pt = Point(5,2)
scala> val bishopCanMoveTo = (
| bishopAtF3.steps(Step(1, 1)) ++
| bishopAtF3.steps(Step(1, -1)) ++
| bishopAtF3.steps(Step(-1, -1)) ++
| bishopAtF3.steps(Step(-1, -1))
| ).toList
bishopCanMoveTo: List[com.fmsbeekmans.pigeonhole.Point[Int(8),Int(8)]] = List(Point(5,2), Point(6,3), Point(7,4), Point(5,2), Point(6,1), Point(7,0), Point(5,2), Point(4,1), Point(3,0), Point(5,2), Point(4,1), Point(3,0))