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.

via GIPHY

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))