diff --git a/build.sbt b/build.sbt index cfdb483e2..c0c11847d 100644 --- a/build.sbt +++ b/build.sbt @@ -38,7 +38,7 @@ lazy val scalachess = Project("scalachess", file(".")).settings( ) ThisBuild / organization := "org.lichess" -ThisBuild / version := "15.3.5" +ThisBuild / version := "15.3.6" ThisBuild / scalaVersion := "3.3.0" ThisBuild / licenses += "MIT" -> url("https://opensource.org/licenses/MIT") diff --git a/src/main/scala/ByColor.scala b/src/main/scala/ByColor.scala index d7dc9c3f3..947affa6a 100644 --- a/src/main/scala/ByColor.scala +++ b/src/main/scala/ByColor.scala @@ -1,8 +1,9 @@ package chess -import cats.{ Applicative, Eq } +import cats.{ Applicative, Eq, Functor, Monoid } import cats.syntax.all.* import scala.annotation.targetName +import alleycats.Zero case class ByColor[A](white: A, black: A): @@ -78,9 +79,24 @@ case class ByColor[A](white: A, black: A): object ByColor: def apply[A](a: A): ByColor[A] = ByColor(a, a) def apply[A](f: Color => A): ByColor[A] = ByColor(white = f(White), black = f(Black)) + def apply[F[_], A](f: Color => F[A]): Applicative[F] ?=> F[ByColor[A]] = + (f(White), f(Black)).mapN(ByColor(_, _)) given [A: Eq]: Eq[ByColor[A]] with def eqv(x: ByColor[A], y: ByColor[A]) = x.white === y.white && x.black === y.black + given [A: Zero]: Zero[ByColor[A]] with + def zero = ByColor(Zero[A].zero) + + given [A: Monoid]: Monoid[ByColor[A]] with + def empty = ByColor(Monoid[A].empty) + def combine(x: ByColor[A], y: ByColor[A]) = + ByColor(Monoid[A].combine(x.white, y.white), Monoid[A].combine(x.black, y.black)) + + given Functor[ByColor] with + def map[A, B](fa: ByColor[A])(f: A => B): ByColor[B] = fa.map(f) + extension [A](bc: ByColor[IterableOnce[A]]) def flatten: List[A] = bc.all.flatten + + extension [A](p: (A, A)) def asByColor: ByColor[A] = ByColor(p._1, p._2) diff --git a/src/main/scala/Hash.scala b/src/main/scala/Hash.scala index 3cf073794..e73019282 100644 --- a/src/main/scala/Hash.scala +++ b/src/main/scala/Hash.scala @@ -104,7 +104,7 @@ object Hash extends OpaqueInt[Hash]: data.pockets .mapWithColor: (color, pocket) => val colorshift = color.fold(79, -1) - pocket.map((role, size) => crazyPocketMask(role, colorshift, size)) + pocket.flatMap((role, size) => crazyPocketMask(role, colorshift, size)) .flatten .computeHash(hcrazypromotions) diff --git a/src/main/scala/Role.scala b/src/main/scala/Role.scala index 37d7fb578..0f96ee3c9 100644 --- a/src/main/scala/Role.scala +++ b/src/main/scala/Role.scala @@ -9,6 +9,8 @@ sealed trait Role: lazy val forsythUpper: Char = forsyth.toUpper lazy val pgn: Char = forsythUpper lazy val name = toString.toLowerCase + inline def forsythBy(color: Color): Char = + if color.white then forsythUpper else forsyth sealed trait PromotableRole extends Role diff --git a/src/main/scala/variant/Crazyhouse.scala b/src/main/scala/variant/Crazyhouse.scala index 196a3e7eb..71240ee91 100644 --- a/src/main/scala/variant/Crazyhouse.scala +++ b/src/main/scala/variant/Crazyhouse.scala @@ -175,7 +175,7 @@ case object Crazyhouse def store(piece: Piece): Pockets = pockets.update(!piece.color, _.store(piece.role)) - def forsyth = pockets.white.forsythUpper + pockets.black.forsyth + def forsyth = pockets.reduce(_.forsythUpper + _.forsyth) case class Pocket(pawn: Int, knight: Int, bishop: Int, rook: Int, queen: Int): @@ -228,9 +228,12 @@ case object Crazyhouse case Queen => f(queen).map(x => copy(queen = x)) case King => None - def map[B](f: (Role, Int) => Option[B]): List[B] = + def flatMap[B](f: (Role, Int) => IterableOnce[B]): List[B] = List(f(Pawn, pawn), f(Knight, knight), f(Bishop, bishop), f(Rook, rook), f(Queen, queen)).flatten + def map[B](f: (Role, Int) => B): List[B] = + List(f(Pawn, pawn), f(Knight, knight), f(Bishop, bishop), f(Rook, rook), f(Queen, queen)) + object Pocket: val empty = Pocket(0, 0, 0, 0, 0) def apply(roles: List[Role]): Pocket =