iomonadize more stuff

pull/1/merge
Thibault Duplessis 2012-03-08 22:49:20 +01:00
parent 776c5d7951
commit c8d9efd246
6 changed files with 183 additions and 181 deletions

View File

@ -19,7 +19,7 @@ trait Dependencies {
val casbah = "com.mongodb.casbah" %% "casbah" % "2.1.5-1"
val salat = "com.novus" %% "salat-core" % "0.0.8-SNAPSHOT"
val slf4j = "org.slf4j" % "slf4j-nop" % "1.6.4"
val scalalib = "com.github.ornicar" %% "scalalib" % "1.18"
val scalalib = "com.github.ornicar" %% "scalalib" % "1.20"
val hasher = "com.roundeights" % "hasher" % "0.3" from "http://cloud.github.com/downloads/Nycto/Hasher/hasher_2.9.1-0.3.jar"
val config = "com.typesafe.config" % "config" % "0.3.0"

View File

@ -43,6 +43,7 @@ class GameRepo(collection: MongoCollection)
} yield game
def save(game: DbGame): IO[Unit] = io {
println("save " + game.lastMove)
update(DBObject("_id" -> game.id), _grater asDBObject encode(game), false, false)
}

View File

@ -3,31 +3,42 @@ package lila.system
import model._
import lila.chess._
import Pos.posAt
import scalaz.effects._
final class Server(repo: GameRepo, ai: Ai) {
def playMove(
fullId: String,
moveString: String,
promString: Option[String] = None): Valid[Map[Pos, List[Pos]]] = for {
promString: Option[String] = None): IO[Valid[Map[Pos, List[Pos]]]] =
repo playerGame fullId flatMap { game
doPlay(game, fullId, moveString, promString).fold(
e io(failure(e)),
a repo save a map { _ success(a.toChess.situation.destinations) }
)
}
def doPlay(
game: Valid[DbGame],
fullId: String,
moveString: String,
promString: Option[String]): Valid[DbGame] = for {
g1 game
g2 if (g1.playable) success(g1) else failure("Game is not playable" wrapNel)
moveParts decodeMoveString(moveString) toValid "Wrong move"
(origString, destString) = moveParts
orig posAt(origString) toValid "Wrong orig " + origString
dest posAt(destString) toValid "Wrong dest " + destString
promotion Role promotable promString toValid "Wrong promotion " + promString
gameAndPlayer repo player fullId toValid "No game found for " + fullId
(g1, _) = gameAndPlayer
g2 if (g1.playable) success(g1) else failure("Game is not playable" wrapNel)
chessGame = g2.toChess
newChessGameAndMove chessGame(orig, dest, promotion)
(newChessGame, move) = newChessGameAndMove
g3 = g2.update(newChessGame, move)
g4 if (g3.player.isAi) aiResponse(g3) else success(g3)
_ unsafe { repo save g4 }
} yield newChessGame.situation.destinations
} yield g4
private def aiResponse(dbGame: DbGame): Valid[DbGame] = for {
aiResult <- unsafe { ai(dbGame).unsafePerformIO }
aiResult unsafe { ai(dbGame).unsafePerformIO }
newChessGameAndMove aiResult
(newChessGame, move) = newChessGameAndMove
} yield dbGame.update(newChessGame, move)

View File

@ -31,7 +31,7 @@ case class DbGame(
case Black blackPlayer
}
def playerById(id: String): Option[DbPlayer] = players find (_.id == id)
def player(id: String): Option[DbPlayer] = players find (_.id == id)
def player: DbPlayer = player(if (0 == turns % 2) White else Black)

View File

@ -2,6 +2,8 @@ package lila.system
import lila.chess._
import model._
import scalaz.effects._
import scalaz.{ Success, Failure }
class ServerTest extends SystemTest {
@ -9,134 +11,179 @@ class ServerTest extends SystemTest {
val repo = env.gameRepo
val server = env.server
def insert(dbGame: DbGame = newDbGameWithRandomIds) = {
repo insert dbGame
dbGame
}
def move(game: DbGame, m: String = "d2 d4") = server.playMove(game fullIdOf White, m)
def insert(dbGame: DbGame = newDbGameWithRandomIds()): IO[DbGame] = for {
_ repo insert dbGame
} yield dbGame
def move(game: DbGame, m: String = "d2 d4"): IO[Valid[Map[Pos, List[Pos]]]] =
server.playMove(game fullIdOf White, m)
def updated(
game: DbGame = newDbGameWithRandomIds,
m: String = "d2 d4"): IO[Valid[DbGame]] = for {
inserted insert(game)
res move(inserted)
updated res.fold(
e io(Failure(e)),
_ repo game game.id
)
} yield updated
"the server" should {
"play a single move" in {
"wrong player" in {
val game = insert()
move(game, "d7 d5") must beFailure
}
"report success" in {
val game = insert()
move(game) must beSuccess
}
"be persisted" in {
"update turns" in {
val game = insert()
move(game)
repo game game.id must beSome.like {
case g g.turns must_== 1
}
}
"update board" in {
val game = insert()
move(game)
repo game game.id must beSome.like {
case g addNewLines(g.toChess.board.visual) must_== """
rnbqkbnr
pppppppp
//"play a single move" in {
//"wrong player" in {
//insert() flatMap { move(_, "d7 d5") } must beIO.like {
//case g g must beFailure
//}
//}
//"report success" in {
//insert() flatMap { move(_) } must beIO.like {
//case g g must beSuccess
//}
//}
//"be persisted" in {
//"update turns" in {
//updated() must beIO.like {
//case vg vg must beSuccess.like {
//case g g.turns must_== 1
//}
//}
//}
//"update board" in {
//updated() must beIO.like {
//case vg vg must beSuccess.like {
//case g addNewLines(g.toChess.board.visual) must_== """
//rnbqkbnr
//pppppppp
P
//P
PPP PPPP
RNBQKBNR
"""
}
}
}
}
//PPP PPPP
//RNBQKBNR
//"""
//}
//}
//}
//}
//}
"play the Peruvian Immortal" in {
val moves = List("e2 e4", "d7 d5", "e4 d5", "d8 d5", "b1 c3", "d5 a5", "d2 d4", "c7 c6", "g1 f3", "c8 g4", "c1 f4", "e7 e6", "h2 h3", "g4 f3", "d1 f3", "f8 b4", "f1 e2", "b8 d7", "a2 a3", "e8 c8", "a3 b4", "a5 a1", "e1 d2", "a1 h1", "f3 c6", "b7 c6", "e2 a6")
def play(game: DbGame) = for (m moves) yield move(game, m)
def play(game: IO[DbGame]): IO[Valid[DbGame]] =
moves.foldLeft(game map { g Success(g) }: IO[Valid[DbGame]]) {
case (ioGame, move) for {
vGame ioGame
newGame vGame.fold(
e io(Failure(e)),
game updated(game, move)
)
} yield newGame
}
//A List[B] (A B IO[Validation[E, A]]) IO[Validation[E, A]]
"report success" in {
val game = insert()
sequenceValid(play(game)) must beSuccess
}
"be persisted" in {
val game = insert()
play(game)
val found = repo game game.id
"update turns" in {
found must beSome.like {
case g g.turns must_== 27
}
play(insert()) must beIO.like {
case vg vg must beSuccess
}
"update board" in {
found must beSome.like {
case g addNewLines(g.toChess.board.visual) must_== """
kr nr
p n ppp
B p p
}
//"be persisted" in {
//val found = for {
//game insert()
//found repo game game.id
//} yield found
P P B
N P
PPK PP
q
"""
}
}
"event stacks" in {
val stack = found map (_ player White) map (_.eventStack)
"high version number" in {
stack must beSome.like { case s s.version must be_>(20) }
}
"rotated" in {
stack must beSome.like { case s s.events.size must_== 16 }
}
}
}
}
"play to threefold repetition" in {
val moves = List("b1 c3", "b8 c6", "c3 b1", "c6 b8", "b1 c3", "b8 c6", "c3 b1", "c6 b8", "b1 c3", "b8 c6")
//"update turns" in {
//found must beIO.like {
//case vg vg must beSuccess.like {
//case g g.turns must_== 27
//}
//}
//}
//"update board" in {
//found must beIO.like {
//case vg vg must beSuccess.like {
//case g addNewLines(g.toChess.board.visual) must_== """
//kr nr
//p n ppp
//B p p
def play(game: DbGame) = for (m moves) yield move(game, m)
//P P B
//N P
//PPK PP
//q
//"""
//}
//}
//}
//"event stacks" in {
//val stack = for {
//valid found
//} yield for {
//game valid
//} yield game player White eventStack
"report success" in {
val game = insert()
sequenceValid(play(game)) must beSuccess
}
"be persisted" in {
val game = insert()
play(game)
val found = repo game game.id
val events = found map (_ player White) map (_.eventStack.events)
"propose threefold" in {
events must beSome.like {
case es es map (_._2) must contain(ThreefoldEvent())
}
}
}
}
"play on playing game" in {
val dbGame = insert(randomizeIds(newDbGameWithBoard("""
PP kr
K
""")))
move(dbGame, "a1 b1") must beSuccess
}
"play on finished game" in {
"by checkmate" in {
val game = insert(randomizeIds(newDbGameWithBoard("""
PP
K r
""")))
move(game, "a1 b1") must beFailure
}
"by autodraw" in {
val game = insert(randomizeIds(newDbGameWithBoard("""
k
K B""")))
move(game, "a1 b1") must beFailure
}
//"high version number" in {
//stack must beIO.like {
//case vs vs must beSuccess.like {
//case s s.version must be_>(20)
//}
//}
//}
//"rotated" in {
//stack must beIO.like {
//case vs vs must beSuccess.like {
//case s s.events.size must_== EventStack.maxEvents
//}
//}
//}
//}
//}
}
//"play to threefold repetition" in {
//val moves = List("b1 c3", "b8 c6", "c3 b1", "c6 b8", "b1 c3", "b8 c6", "c3 b1", "c6 b8", "b1 c3", "b8 c6")
//def play(game: DbGame) = for (m moves) yield move(game, m)
//"report success" in {
//val game = insert()
//sequenceValid(play(game)) must beSuccess
//}
//"be persisted" in {
//val game = insert()
//play(game)
//val found = repo game game.id
//val events = found map (_ player White) map (_.eventStack.events)
//"propose threefold" in {
//events must beSome.like {
//case es es map (_._2) must contain(ThreefoldEvent())
//}
//}
//}
//}
//"play on playing game" in {
//val dbGame = insert(randomizeIds(newDbGameWithBoard("""
//PP kr
//K
//""")))
//move(dbGame, "a1 b1") must beSuccess
//}
//"play on finished game" in {
//"by checkmate" in {
//val game = insert(randomizeIds(newDbGameWithBoard("""
//PP
//K r
//""")))
//move(game, "a1 b1") must beFailure
//}
//"by autodraw" in {
//val game = insert(randomizeIds(newDbGameWithBoard("""
//k
//K B""")))
//move(game, "a1 b1") must beFailure
//}
//}
}
}

View File

@ -1,7 +1,7 @@
package lila.system
import org.specs2.mutable.Specification
import ornicar.scalalib.test.OrnicarValidationMatchers
import ornicar.scalalib.test._
import model._
import lila.chess._
@ -10,66 +10,9 @@ import format.Visual
trait SystemTest
extends Specification
with OrnicarValidationMatchers
with ScalazIOMatchers
with Fixtures {
import util.control.Exception.allCatch
import org.specs2.execute.{ Failure, Success, Result }
import org.specs2.matcher._
import scalaz.effects._
/** matcher for an IO */
def beIO[A](t: A) = new Matcher[IO[A]] {
def apply[S <: IO[A]](value: Expectable[S]) = {
val expected = t
(allCatch either { value.value.unsafePerformIO }).fold(
e result(false,
"IO fails with " + e,
"IO fails with " + e,
value),
a result(a == expected,
a + " is IO with value " + expected,
a + " is not IO with value " + expected,
value)
)
}
}
def beIO[A] = new Matcher[IO[A]] {
def apply[S <: IO[A]](value: Expectable[S]) = {
val performed = allCatch either { value.value.unsafePerformIO }
result(performed.isRight,
"IO perfoms successfully",
"IO fails",
value)
}
def like(f: PartialFunction[A, MatchResult[_]]) = this and partialMatcher(f)
private def partialMatcher(
f: PartialFunction[A, MatchResult[_]]) = new Matcher[IO[A]] {
def apply[S <: IO[A]](value: Expectable[S]) = {
(allCatch either { value.value.unsafePerformIO }).fold(
e result(false,
"IO fails with " + e,
"IO fails with " + e,
value),
a {
val res: Result = a match {
case t if f.isDefinedAt(t) f(t).toResult
case _ Failure("function undefined")
}
result(res.isSuccess,
a + " is IO[A] and " + res.message,
a + " is not IO[A] with value " + res.message,
value)
}
)
}
}
}
implicit def stringToBoard(str: String): Board = Visual << str
implicit def richDbGame(dbGame: DbGame) = new {