First implementation attempt inspired by Synesso/scala-chess

pull/1/merge
Thibault Duplessis 2012-02-21 20:45:24 +01:00
parent 0dc35f3cbc
commit 0fcb728a3f
12 changed files with 366 additions and 10 deletions

View File

@ -0,0 +1,23 @@
package lila
object Cli {
def main(args: Array[String]) {
args.headOption flatMap makeEnv map { env
args.tail.toList match {
case "fixtures" :: Nil fixtures(env)
case _ => sys error "Invalid command"
}
} err "Invalid environment"
}
def makeEnv(name: String) = name match {
case "test" Env.test some
case _ none
}
def fixtures(env: Env) {
println("Load %s fixtures" format env.name)
}
}

View File

@ -4,18 +4,35 @@ import repo._
import com.mongodb.casbah.MongoConnection
import com.mongodb.casbah.commons.conversions.scala._
class Env(configuration: Map[String, Any]) {
final class Env(configuration: Env.Settings, val name: String = "default") {
def gameRepo = new GameRepo(mongodb("game2"))
lazy val gameRepo = new GameRepo(mongodb("game2"))
private def mongoConnection = MongoConnection(
private lazy val mongodb = MongoConnection(
get[String]("mongo.host"),
get[Int]("mongo.port")
)
private def mongodb = mongoConnection(
get[String]("mongo.dbname")
)
)(get[String]("mongo.dbname"))
private def get[A](key: String) = configuration(key).asInstanceOf[A]
def ~(settings: Env.Settings) = new Env(
configuration ++ settings,
name
)
}
object Env {
type Settings = Map[String, Any]
private val defaults = Map(
"mongo.host" -> "127.0.0.1",
"mongo.port" -> 27017,
"mongo.dbname" -> "lichess"
)
def test = new Env(defaults ++ Map(
"mongo.dbname" -> "lichess_test"
), "test")
}

View File

@ -0,0 +1,11 @@
package lila
package format
import model.Game
trait Format {
def <<(source: String): Game
def >>(game: Game): String
}

View File

@ -0,0 +1,11 @@
package lila
package format
import model._
object Visual extends Format {
def <<(source: String): Game = Game("fromsource")
def >>(game: Game): String = ""
}

View File

@ -0,0 +1,130 @@
package lila
package model
//case class Board(pieces: Map[Pos, Piece], taken: List[Piece]) {
//def this() = this(Map(), Nil)
/**
* Place a piece on the board (at a position)
* @return a new board
*/
//def place(p: Piece) = {
//case class Placement() {
//def at(s: Symbol): Board = at(position(s))
//def at(destination: Pos): Board = new Board(pieces(destination) = p, taken)
//}
//new Placement
//}
/**
* Take (capture) the piece at the given position
* @return a new board
*/
//def take(p: Pos) = new Board((pieces - p), (pieces(p) :: taken))
/**
* Move the piece at the given position (to a new position)
* @return a new board
*/
//def move(orig: Pos) = {
//case class Movement() {
//def to(dest: Pos) = {
//if (pieces contains dest) throw new IllegalMoveException("Cannot move to occupied " + dest)
//else pieces.get(orig).map(piece => new Board((pieces - orig)(dest) = piece, taken))
//.getOrElse {throw new IllegalMoveException("No piece at " + orig + " to move")}
//}
//}
//new Movement
//}
/**
* Promote the piece at the given position to a new role
* @return a new board
*/
//def promote(p: Pos) = {
//case class Promotion() {
//def to(r: Role): Board = r match {
//case King => throw new IllegalPromotionException("Cannot promote to King")
//case _ => pieces.get(p).map(piece => piece.role match {
//case Pawn => new Board(pieces(p) = Piece(piece.colour, r), taken)
//case _ => throw new IllegalPromotionException("Can only promote pawns")
//}).getOrElse {throw new IllegalPromotionException("No piece at " + p + " to promote")}
//}
//}
//new Promotion
//}
/**
* Finds all positions which contain a threat to the given colour (at a given position).
* Note, will not identify threats to position from en passant (as this behaviour is not required).
* @return the positions of threat
*/
//def threatsTo(colour: Colour) = {
//case class Threat() {
//def at(s: Symbol): List[Pos] = at(position(s))
//def at(pos: Pos): List[Pos] = {
//def expand(direction: Seq[Option[Pos] => Option[Pos]]): Option[Pos] = {
//def next(from: Option[Pos]): Option[Pos] = {
//val nextPose = direction.foldLeft(from) {(acc, next) => next(acc)}
//if (nextPose.isEmpty) return None
//if (pieces.contains(nextPose.get)) Some(nextPose.get) else next(nextPose)
//}
//next(Some(pos))
//}
//def opposing(r: Role) = {
//case class OpposingRoleCheck() {
//def at(p: Pos) = pieces.get(p).map(_.equals(Piece(opposite of colour, r))).getOrElse(false)
//}
//new OpposingRoleCheck
//}
//val forward = if (White.equals(colour)) ^ _ else v _
//val pawns: List[Pos] = Set(forward(pos < 1), forward(pos > 1)).filter(_.isDefined).foldLeft(Nil: List[Pos]) {
//(acc, next) =>
//if (opposing(Pawn).at(next.get)) next.get :: acc else acc
//}
//val rankFileVectors: List[Pos] = (expand(Seq(< _)) :: expand(Seq(^ _)) :: expand(Seq(> _)) :: expand(Seq(v _)) :: Nil)
//.filter(_.isDefined).foldLeft(Nil: List[Pos]) {
//(acc, next) =>
//if (opposing(Rook).at(next.get) || opposing(Queen).at(next.get)) next.get :: acc else acc
//}
//val diagonalVectors: List[Pos] = (expand(Seq(< _, ^ _)) :: expand(Seq(> _, ^ _)) :: expand(Seq(> _, v _)) :: expand(Seq(< _, v _)) :: Nil)
//.filter(_.isDefined).foldLeft(Nil: List[Pos]) {
//(acc, next) =>
//if (opposing(Bishop).at(next.get) || opposing(Queen).at(next.get)) next.get :: acc else acc
//}
//val knights: List[Pos] = {
//val options = radialBasedPoss(pos, List(-2, -1, 1, 2), (rank, file) => Math.abs(rank) != Math.abs(file))
//options.toList.filter(opposing(Knight).at(_))
//}
//val kings: List[Pos] = {
//val options = radialBasedPoss(pos, -1 to 1, (rank, file) => (rank != 0 || file != 0))
//options.toList.filter(opposing(King).at(_))
//}
//pawns ::: rankFileVectors ::: diagonalVectors ::: knights ::: kings
//}
//}
//new Threat
//}
/**
* Layout the board for a new game.
* @return a new board
*/
//def reset = {
//val lineUp = Rook :: Knight :: Bishop :: Queen :: King :: Bishop :: Knight :: Rook :: Nil
//val pairs = for (rank <- 1 :: 2 :: 7 :: 8 :: Nil; file <- 1 to 8) yield (position(rank, file),
//rank match {
//case 1 => Piece(White, lineUp(file - 1))
//case 2 => Piece(White, Pawn)
//case 7 => Piece(Black, Pawn)
//case 8 => Piece(Black, lineUp(file - 1))
//})
//new Board(pairs.foldLeft(Map.empty[Pos, Piece]) {(acc, next) => acc(next._1) = next._2}, Nil)
//}
//}

View File

@ -6,5 +6,7 @@ import com.mongodb.casbah.Imports._
case class Game(
@Key("_id") id: String
@Key("_id") id: String,
players: List[Player] = Nil
)

View File

@ -0,0 +1,5 @@
package lila
package model
import com.novus.salat.annotations._
import com.mongodb.casbah.Imports._

View File

@ -0,0 +1,14 @@
package lila
package model
import com.novus.salat.annotations._
import com.mongodb.casbah.Imports._
case class Player(
id: String,
color: String,
ps: String
)

View File

@ -0,0 +1,73 @@
package lila
package model
import scala.math.{ abs, min, max }
object Pos {
val values: Set[Int] = (1 to 8) toSet
val all: Map[String, Pos] = {
for {
x <- 1 to 8
y <- 1 to 8
} yield (xToString(x) + y.toString, Pos(x, y))
} toMap
def apply(s: Symbol): Pos = apply(s.name)
def apply(k: String): Pos = apply(keyToPair(k))
def apply(xy: Pair[Int, Int]): Pos = Pos(xy._1, xy._2)
def ^(p: Option[Pos]): Option[Pos] = p.flatMap(_ ^ 1)
def >(p: Option[Pos]): Option[Pos] = p.flatMap(_ > 1)
def v(p: Option[Pos]): Option[Pos] = p.flatMap(_ v 1)
def <(p: Option[Pos]): Option[Pos] = p.flatMap(_ < 1)
def noop(p: Option[Pos]) = p
def vectorBasedPoss(from: Pos, directions: List[List[Option[Pos] Option[Pos]]]) = {
def expand(direction: Seq[Option[Pos] Option[Pos]]): List[Pos] = {
def next(acc: List[Pos]): List[Pos] = {
val seed: Option[Pos] = Some(acc.headOption.getOrElse(from))
val candidate = direction.foldLeft(seed) { (intermediate, step) step(intermediate) }
candidate match {
case Some(p) next(p :: acc)
case None acc
}
}
next(Nil).reverse
}
directions.foldLeft(Nil: List[List[Pos]]) { (acc, next) expand(next) :: acc }.filter(l !l.isEmpty)
}
def radialBasedPoss(from: Pos, offsets: Iterable[Int], filter: (Int, Int) Boolean) = {
(for (y offsets; x offsets; if (filter(y, x))) yield (from ^ y).flatMap(_ < x)).filter(_.isDefined).map(_.get)
}
private def keyToPair(k: String): Pair[Int, Int] =
(k.charAt(0).asDigit - 9, k.charAt(1).asDigit)
def xToString(x: Int) = (96 + x).toChar.toString
}
case class Pos private (x: Int, y: Int) {
if (!Pos.values(x) || !Pos.values(y))
throw new RuntimeException("Invalid position " + (x, y))
def ^(n: Int): Option[Pos] = if (Pos.values.contains(y + n)) Some(Pos(y + n, x)) else None
def >(n: Int): Option[Pos] = if (Pos.values.contains(x + n)) Some(Pos(y, x + n)) else None
def v(n: Int): Option[Pos] = this ^ (n * -1)
def <(n: Int): Option[Pos] = this > (n * -1)
def -?(other: Pos) = y == other.y
def |?(other: Pos) = x == other.x
def /?(other: Pos) = abs(x - other.x) == abs(y - other.y)
def *?(other: Pos) = this.-?(other) || this.|?(other) || this./?(other)
def ^^(n: Int): List[Pos] = expand(n, List(Some(this)), Pos.^).filter(_.isDefined).map(_.get).reverse
def >>(n: Int): List[Pos] = expand(n, List(Some(this)), Pos.>).filter(_.isDefined).map(_.get).reverse
def vv(n: Int): List[Pos] = expand(n, List(Some(this)), Pos.v).filter(_.isDefined).map(_.get).reverse
def <<(n: Int): List[Pos] = expand(n, List(Some(this)), Pos.<).filter(_.isDefined).map(_.get).reverse
def xToString = Pos xToString x
def yToString = y.toString
override def toString = xToString + yToString
private def expand(i: Int, accumulator: List[Option[Pos]], direct: Option[Pos] Option[Pos]): List[Option[Pos]] = {
if (i > 0 && accumulator.head.isDefined) expand(i - 1, direct(accumulator.head) :: accumulator, direct) else accumulator
}
}

View File

@ -9,4 +9,6 @@ import com.mongodb.casbah.MongoCollection
import com.mongodb.casbah.Imports._
class GameRepo(collection: MongoCollection) extends SalatDAO[Game, String](collection) {
def game(id: String) = findOneByID(id)
}

View File

@ -0,0 +1,67 @@
package lila
import org.specs2.mutable._
import org.specs2.specification._
import format.Visual
import model.Game
class FormatVisualTest extends Specification {
val f = Visual
"The visual formatter" should {
"import and export is non destructive" in {
forall(examples) { example =>
(f >> (f << example)) mustEqual example
}
}
}
val examples = Seq("""
rnbqkp r
pppppppp
P n
PPPP P
RNBQK R""", """
k
P
K
""", """
rnbqkbnr
pppppppp
RK NR
""", """
bqkb r
p ppp pp
pr
P p
QnB
PP N
P PPP
RN K R
""", """
r k nr
pp n ppp
p p
q
b P B
P N Q P
PP BPP
R K R
"""
)
}

View File

@ -34,7 +34,8 @@ object ApplicationBuild extends Build with Resolvers with Dependencies {
resolvers := Seq(
codahale, typesafe, iliaz, sonatype
),
shellPrompt := ShellPrompt.buildShellPrompt
shellPrompt := ShellPrompt.buildShellPrompt,
scalacOptions := Seq("-deprecation", "-unchecked")
)
val main = PlayProject("app", appVersion, Seq(), mainLang = SCALA) dependsOn lila