Secure DbGame players type

pull/1/merge
Thibault Duplessis 2012-03-04 21:30:04 +01:00
parent 894adbf00e
commit 5517451ee2
9 changed files with 67 additions and 82 deletions

View File

@ -43,10 +43,7 @@ class Benchmark extends SimpleScalaBenchmark {
val repo = env.gameRepo
val server = env.server
def move(game: DbGame, m: String = "d2 d4") = for {
player game playerByColor "white"
fullId game fullIdOf player
} yield server.playMove(fullId, m)
def move(game: DbGame, m: String = "d2 d4") = server.playMove(game fullIdOf White, m)
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")

View File

@ -24,8 +24,7 @@ extends SalatDAO[RawDbGame, String](collection) {
def player(gameId: String, color: Color): Option[(DbGame, DbPlayer)] = for {
game game(gameId take gameIdSize)
player game playerByColor color
} yield (game, player)
} yield (game, game player color)
def save(game: DbGame) =
update(DBObject("_id" -> game.id), _grater asDBObject encode(game), false, false)

View File

@ -7,7 +7,8 @@ import Role.forsyth
case class DbGame(
id: String,
players: List[DbPlayer],
whitePlayer: DbPlayer,
blackPlayer: DbPlayer,
pgn: String,
status: Int,
turns: Int,
@ -17,18 +18,27 @@ case class DbGame(
castles: String = "KQkq",
isRated: Boolean = false) {
def playerById(id: String): Option[DbPlayer] = playersById get id
val players = List(whitePlayer, blackPlayer)
def playerByColor(color: Color): Option[DbPlayer] = playersByColor get color
val playersByColor: Map[Color, DbPlayer] = Map(
White -> whitePlayer,
Black -> blackPlayer
)
def player: DbPlayer = playerByColor(if (0 == turns % 2) White else Black) get
def player(color: Color): DbPlayer = color match {
case White whitePlayer
case Black blackPlayer
}
lazy val playersByColor: Map[Color, DbPlayer] = players map { p (p.color, p) } toMap
lazy val playersById: Map[String, DbPlayer] = players map { p (p.id, p) } toMap
def playerById(id: String): Option[DbPlayer] = players find (_.id == id)
def player: DbPlayer = player(if (0 == turns % 2) White else Black)
def fullIdOf(player: DbPlayer): Option[String] =
(players contains player) option id + player.id
def fullIdOf(color: Color): String = id + player(color).id
def toChess: Game = {
def posPiece(posCode: Char, roleCode: Char, color: Color): Option[(Pos, Piece)] = for {
@ -80,14 +90,15 @@ case class DbGame(
})
val (history, situation) = (game.board.history, game.situation)
val events = (Event fromMove move) ::: (Event fromSituation game.situation)
def updatePlayer(player: DbPlayer) = player.copy(
ps = player encodePieces allPieces,
evts = player.newEvts(events :+ Event.possibleMoves(game.situation, player.color)))
copy(
pgn = game.pgnMoves,
players = for {
player players
} yield player.copy(
ps = player encodePieces allPieces,
evts = player.newEvts(events :+ Event.possibleMoves(game.situation, player.color))
),
whitePlayer = updatePlayer(whitePlayer),
blackPlayer = updatePlayer(blackPlayer),
turns = game.turns,
positionHashes = history.positionHashes mkString,
castles = List(
@ -106,6 +117,11 @@ case class DbGame(
}
def playable = status < DbGame.ABORTED
def mapPlayers(f: DbPlayer => DbPlayer) = copy(
whitePlayer = f(whitePlayer),
blackPlayer = f(blackPlayer)
)
}
object DbGame {

View File

@ -27,6 +27,3 @@ case class DbPlayer(
def isAi = aiLevel.isDefined
}
object DbPlayer {
}

View File

@ -21,16 +21,14 @@ case class RawDbGame(
isRated: Boolean = false) {
def decode: Option[DbGame] = for {
player1 players.lift(0) flatMap (_.decode)
player2 players.lift(1) flatMap (_.decode)
validPlayers = List(player1, player2)
colors = validPlayers map (_.color)
//if (colors.pp contains White) && (colors contains Black)
whitePlayer players find (_.color == "white") flatMap (_.decode)
blackPlayer players find (_.color == "black") flatMap (_.decode)
validClock = clock flatMap (_.decode)
if validClock.isDefined == clock.isDefined
} yield DbGame(
id = id,
players = validPlayers,
whitePlayer = whitePlayer,
blackPlayer = blackPlayer,
pgn = pgn,
status = status,
turns = turns,

View File

@ -15,14 +15,10 @@ class ChessToModelTest extends SystemTest {
"identity" in {
val dbg2 = dbGame.update(game, anyMove)
"white pieces" in {
dbg2 playerByColor White map (_.ps) map sortPs must_== {
dbGame playerByColor White map (_.ps) map sortPs
}
sortPs((dbg2 player White).ps) must_== sortPs((dbGame player White).ps)
}
"black pieces" in {
dbg2 playerByColor Black map (_.ps) map sortPs must_== {
dbGame playerByColor Black map (_.ps) map sortPs
}
sortPs((dbg2 player Black).ps) must_== sortPs((dbGame player Black).ps)
}
}
}
@ -51,33 +47,25 @@ R QK q
"identity" in {
val dbg2 = dbGame.update(game, anyMove)
"white pieces" in {
dbg2 playerByColor White map (_.ps) map sortPs must_== {
dbGame playerByColor White map (_.ps) map sortPs
}
sortPs((dbg2 player White).ps) must_== sortPs((dbGame player White).ps)
}
"black pieces" in {
dbg2 playerByColor Black map (_.ps) map sortPs must_== {
dbGame playerByColor Black map (_.ps) map sortPs
}
sortPs((dbg2 player Black).ps) must_== sortPs((dbGame player Black).ps)
}
}
"new pieces positions" in {
val dbg2 = newDbGame.update(game, anyMove)
"white pieces" in {
dbg2 playerByColor White map (_.ps) map sortPs must_== {
dbGame playerByColor White map (_.ps) map sortPs
}
sortPs((dbg2 player White).ps) must_== sortPs((dbGame player White).ps)
}
"black pieces" in {
dbg2 playerByColor Black map (_.ps) map sortPs must_== {
dbGame playerByColor Black map (_.ps) map sortPs
}
sortPs((dbg2 player Black).ps) must_== sortPs((dbGame player Black).ps)
}
}
}
"update events" in {
def playerEvents(dbg: Valid[DbGame], color: Color) =
dbg.toOption flatMap (_ playerByColor color) map (_.eventStack.events)
dbg.toOption map (g (g player color).eventStack.events)
"simple move" in {
val dbg = newDbGame.withoutEvents.afterMove(D2, D4)
"white events" in {

View File

@ -15,7 +15,8 @@ trait Fixtures {
lazy val newDbGame = DbGame(
id = "arstdhne",
players = List(white, black),
whitePlayer = white,
blackPlayer = black,
pgn = "",
status = 10,
turns = 0,
@ -28,9 +29,8 @@ trait Fixtures {
def newDbGameWithRandomIds() = randomizeIds(newDbGame)
def randomizeIds(game: DbGame) = game.copy(
id = randomString(gameIdSize),
players = game.players map (_.copy(id = randomString(playerIdSize)))
)
id = randomString(gameIdSize)
) mapPlayers (p p.copy(id = randomString(playerIdSize)))
lazy val white = newDbPlayer(White, "ip ar jp bn kp cb lp dq mp ek np fb op gn pp hr")
lazy val black = newDbPlayer(Black, "Wp 4r Xp 5n Yp 6b Zp 7q 0p 8k 1p 9b 2p !n 3p ?r")
@ -47,9 +47,8 @@ trait Fixtures {
lazy val dbGame1 = DbGame(
id = "huhuhaha",
players = List(
newDbPlayer(White, "ip ar sp16 sN14 kp ub8 Bp6 dq Kp0 ek np LB12 wp22 Fn2 pp hR"),
newDbPlayer(Black, "Wp 4r Xp Qn1 Yp LB13 Rp9 hq17 0p 8k 1p 9b 2p sN3 3p ?r")),
whitePlayer = newDbPlayer(White, "ip ar sp16 sN14 kp ub8 Bp6 dq Kp0 ek np LB12 wp22 Fn2 pp hR"),
blackPlayer = newDbPlayer(Black, "Wp 4r Xp Qn1 Yp LB13 Rp9 hq17 0p 8k 1p 9b 2p sN3 3p ?r"),
pgn = "e4 Nc6 Nf3 Nf6 e5 Ne4 d3 Nc5 Be3 d6 d4 Ne4 Bd3 Bf5 Nc3 Nxc3 bxc3 Qd7 Bxf5 Qxf5 Nh4 Qe4 g3 Qxh1+",
status = 31,
turns = 24,
@ -59,9 +58,8 @@ trait Fixtures {
lazy val dbGame2 = DbGame(
id = "-176b4to",
players = List(
newDbPlayer(White, "zP32 Yr44 jp JN10 Jp20 cb Kp18 KQ2 KP0 Gk30 np ZB22 op QN4 pp dr50"),
newDbPlayer(Black, "WP Ar19 BP13 QN11 YP ZB35 KP21 KQ3 KP1 Ik37 1p zB29 Ep5 JN9 3p 5r25")),
whitePlayer = newDbPlayer(White, "zP32 Yr44 jp JN10 Jp20 cb Kp18 KQ2 KP0 Gk30 np ZB22 op QN4 pp dr50"),
blackPlayer = newDbPlayer(Black, "WP Ar19 BP13 QN11 YP ZB35 KP21 KQ3 KP1 Ik37 1p zB29 Ep5 JN9 3p 5r25"),
pgn = "e4 e5 Qh5 Qf6 Nf3 g6 Qxe5+ Qxe5 Nxe5 Nf6 Nc3 Nc6 Nxc6 bxc6 e5 Nd5 Nxd5 cxd5 d4 Rb8 c3 d6 Be2 dxe5 dxe5 Rg8 Bf3 d4 cxd4 Bb4+ Ke2 g5 a3 g4 Bc6+ Bd7 Bxd7+ Kxd7 axb4 Rxb4 Kd3 Rb3+ Kc4 Rb6 Rxa7 Rc6+ Kb5 Rb8+ Ka5 Rc4 Rd1 Kc6 d5+ Kc5 Rxc7#",
status = 30,
turns = 55,
@ -78,9 +76,8 @@ trait Fixtures {
// { "_id" : "7xfxoj4v", "clock" : null, "createdAt" : ISODate("2012-01-28T01:55:33Z"), "creatorColor" : Black, "initialFen" : "rkbbnnqr/pppppppp/8/8/8/8/PPPPPPPP/RKBBNNQR w KQkq - 0 1", "lastMove" : "a3 a8", "pgn" : "d4 d5 f3 Bf5 Ne3 Nd6 Bd2 c6 g4 Bb6 gxf5 Nd7 Qg5 f6 Qg4 h5 Qh4 Rh6 N1g2 Rg6 Qf2 Rg5 c3 e6 Kc1 exf5 Kb1 f4 Nxf4 Nf5 h4 Nxe3 hxg5 Nxd1 Qf1 Nxc3+ Bxc3 a5 Qf2 Nc5 Kc1 Ra6 Rh2 fxg5 dxc5 gxf4 cxb6 Rxb6 Rxh5 g6 Qxb6 Qe6 Qd8+ Qc8 Qxc8+ Kxc8 Rh2 a4 Kc2 b5 Rh7 c5 Bg7 a3 b3 c4 Bf6 cxb3+ axb3 b4 Be5 g5 Bd6 Kd8 Bxb4 d4 Rxa3 d3+ exd3 g4 Ra8#", "players" : [ { "aiLevel" : 1, "color" : White, "id" : "jqsx", "isAi" : true, "isWinner" : true, "ps" : "zb6 dB 6Q12 uN4 DN18 4r76 kk24 3r42 rp68 rP64 sP22 PP0 tp78 vp2 LP8 MP30" }, { "color" : Black, "id" : "7n7r", "ps" : "LB3 PB9 6Q51 IN11 sN5 PR41 7k55 MR17 qP37 zP59 rP7 tP1 DP23 Dp13 Ep49 NP15" } ], "status" : 30, "turns" : 81, "updatedAt" : ISODate("2012-01-28T02:01:28Z"), "userIds" : [ ], "variant" : 2, "winnerUserId" : "" }
lazy val dbGame3 = DbGame(
id = "7xfxoj4v",
players = List(
newDbPlayer(White, "zb6 dB 6Q12 uN4 DN18 4r76 kk24 3r42 rp68 rP64 sP22 PP0 tp78 vp2 LP8 MP30"),
newDbPlayer(Black, "LB3 PB9 6Q51 IN11 sN5 PR41 7k55 MR17 qP37 zP59 rP7 tP1 DP23 Dp13 Ep49 NP15")),
whitePlayer = newDbPlayer(White, "zb6 dB 6Q12 uN4 DN18 4r76 kk24 3r42 rp68 rP64 sP22 PP0 tp78 vp2 LP8 MP30"),
blackPlayer = newDbPlayer(Black, "LB3 PB9 6Q51 IN11 sN5 PR41 7k55 MR17 qP37 zP59 rP7 tP1 DP23 Dp13 Ep49 NP15"),
pgn = "d4 d5 f3 Bf5 Ne3 Nd6 Bd2 c6 g4 Bb6 gxf5 Nd7 Qg5 f6 Qg4 h5 Qh4 Rh6 N1g2 Rg6 Qf2 Rg5 c3 e6 Kc1 exf5 Kb1 f4 Nxf4 Nf5 h4 Nxe3 hxg5 Nxd1 Qf1 Nxc3+ Bxc3 a5 Qf2 Nc5 Kc1 Ra6 Rh2 fxg5 dxc5 gxf4 cxb6 Rxb6 Rxh5 g6 Qxb6 Qe6 Qd8+ Qc8 Qxc8+ Kxc8 Rh2 a4 Kc2 b5 Rh7 c5 Bg7 a3 b3 c4 Bf6 cxb3+ axb3 b4 Be5 g5 Bd6 Kd8 Bxb4 d4 Rxa3 d3+ exd3 g4 Ra8#",
status = 30,
turns = 81,
@ -90,9 +87,8 @@ trait Fixtures {
lazy val dbGame4 = DbGame(
id = "huhuhiha",
players = List(
newDbPlayer(White, "ip ar sp sN kp ub Bp dq Kp ek np LB wp Fn pp hR"),
newDbPlayer(Black, "Wp 4r Xp Qn Yp LB Rp hq 0p 8k 1p 9b 2p sN 3p ?r")),
whitePlayer = newDbPlayer(White, "ip ar sp sN kp ub Bp dq Kp ek np LB wp Fn pp hR"),
blackPlayer = newDbPlayer(Black, "Wp 4r Xp Qn Yp LB Rp hq 0p 8k 1p 9b 2p sN 3p ?r"),
pgn = "e4 Nc6 Nf3 Nf6 e5 Ne4 d3 Nc5 Be3 d6 d4 Ne4 Bd3 Bf5 Nc3 Nxc3 bxc3 Qd7 Bxf5 Qxf5 Nh4 Qe4 g3 Qxh1+",
status = 31,
turns = 24,
@ -103,9 +99,8 @@ trait Fixtures {
// from online prod DB
val dbGame5 = DbGame(
id = "luik7l0a",
players = List(
newDbPlayer(White, "qp4 aR rp0 bn sp46 TB2 uP26 dQ uP14 kk40 MP22 QB8 EP6 gn 3P10 lr44", "109p|110m1vb|111p|112mplw|113p|114mvub|115p|116ml7w|117C?|118p|119m?3b|120p|121m7lw|122Msystem Time out|123e|124p"),
newDbPlayer(Black, "Op5 7R47 Xp QN21 Ip19 Qb17 uP1 dQ39 KP3 3k15 EP9 ab13 Up33 TN11 Mp7 ur15", "109p|110m1vb|111p|112mplw|113p|114mvub|115p|116ml7w|117C?|118p|119m?3b|120p|121m7lw|122Msystem Time out|123e|124p")
whitePlayer = newDbPlayer(White, "qp4 aR rp0 bn sp46 TB2 uP26 dQ uP14 kk40 MP22 QB8 EP6 gn 3P10 lr44", "109p|110m1vb|111p|112mplw|113p|114mvub|115p|116ml7w|117C?|118p|119m?3b|120p|121m7lw|122Msystem Time out|123e|124p"),
blackPlayer = newDbPlayer(Black, "Op5 7R47 Xp QN21 Ip19 Qb17 uP1 dQ39 KP3 3k15 EP9 ab13 Up33 TN11 Mp7 ur15", "109p|110m1vb|111p|112mplw|113p|114mvub|115p|116ml7w|117C?|118p|119m?3b|120p|121m7lw|122Msystem Time out|123e|124p"
),
pgn = "b3 d5 Bb2 e6 a3 a6 g3 h6 Bg2 f5 h3 Nf6 Bf1 Be7 e3 O-O Bg2 Bd7 h4 c5 h5 Nc6 f3 d4 g4 dxe3 dxe3 fxg4 fxg4 e5 g5 hxg5 h6 g6 h7+ Kh8 Bxc6 Bxc6 Bxe5 Qxd1+ Kxd1 Rf7 Bxf6+ Bxf6 Rh2 Bxa1 c3 Rd8+ Kc2 Rf3 Rd2 Rxe3 Rxd8+ Kxh7 Rd2",
status = 35,
@ -140,5 +135,5 @@ trait Fixtures {
promotion = promotion,
enpassant = enpassant)
val anyMove = newMove(White.pawn, D2, D4)
val anyMove = newMove(White.pawn, D2, D4)
}

View File

@ -13,20 +13,17 @@ class ServerTest extends SystemTest {
repo insert dbGame
dbGame
}
def move(game: DbGame, m: String = "d2 d4") = for {
player game playerByColor White
fullId game fullIdOf player
} yield server.playMove(fullId, m)
def move(game: DbGame, m: String = "d2 d4") = server.playMove(game fullIdOf White, m)
"the server" should {
"play a single move" in {
"wrong player" in {
val game = insert()
move(game, "d7 d5") must beSome.like { case r r must beFailure }
move(game, "d7 d5") must beFailure
}
"report success" in {
val game = insert()
move(game) must beSome.like { case r r must beSuccess }
move(game) must beSuccess
}
"be persisted" in {
"update turns" in {
@ -58,7 +55,7 @@ RNBQKBNR
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).get
def play(game: DbGame) = for (m moves) yield move(game, m)
"report success" in {
val game = insert()
@ -88,7 +85,7 @@ B p p
}
}
"event stacks" in {
val stack = found flatMap (_ playerByColor White) map (_.eventStack)
val stack = found map (_ player White) map (_.eventStack)
"high version number" in {
stack must beSome.like { case s s.version must be_>(20) }
}
@ -101,7 +98,7 @@ B p p
"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).get
def play(game: DbGame) = for (m moves) yield move(game, m)
"report success" in {
val game = insert()
@ -111,7 +108,7 @@ B p p
val game = insert()
play(game)
val found = repo game game.id
val events = found flatMap (_ playerByColor White) map (_.eventStack.events)
val events = found map (_ player White) map (_.eventStack.events)
"propose threefold" in {
events must beSome.like {
case es es map (_._2) must contain(ThreefoldEvent())
@ -124,7 +121,7 @@ B p p
PP kr
K
""")))
move(dbGame, "a1 b1") must beSome.like { case r r must beSuccess }
move(dbGame, "a1 b1") must beSuccess
}
"play on finished game" in {
"by checkmate" in {
@ -132,13 +129,13 @@ K
PP
K r
""")))
move(game, "a1 b1") must beSome.like { case r r must beFailure }
move(game, "a1 b1") must beFailure
}
"by autodraw" in {
val game = insert(randomizeIds(newDbGameWithBoard("""
k
K B""")))
move(game, "a1 b1") must beSome.like { case r r must beFailure }
move(game, "a1 b1") must beFailure
}
}
}

View File

@ -16,9 +16,7 @@ trait SystemTest
implicit def richDbGame(dbGame: DbGame) = new {
def withoutEvents: DbGame = dbGame.copy(
players = dbGame.players map (_.copy(evts = ""))
)
def withoutEvents: DbGame = dbGame mapPlayers (_.copy(evts = ""))
def afterMove(orig: Pos, dest: Pos): Valid[DbGame] =
dbGame.toChess.apply(orig, dest) map {