First take at applying event moves to the stack

pull/1/merge
Thibault Duplessis 2012-03-04 00:49:18 +01:00
parent 9cb335463d
commit d1f4bbdc50
6 changed files with 92 additions and 26 deletions

View File

@ -2,7 +2,7 @@ package lila.chess
import ornicar.scalalib.test.OrnicarValidationMatchers
import org.specs2.mutable.Specification
import org.specs2.matcher.{ Matcher }
import org.specs2.matcher.Matcher
import format.Visual

View File

@ -21,7 +21,7 @@ final class Server(repo: GameRepo) {
newChessGameAndMove chessGame(orig, dest, promotion)
(newChessGame, move) = newChessGameAndMove
g1 = game update newChessGame
eventStacks = game.eventStacks mapValues (_ withMove move)
eventStacks = game.eventStacks mapValues (_ withMove move optimize)
g2 = g1 withEventStacks eventStacks
result unsafe { repo save g2 }
} yield newChessGame.situation.destinations

View File

@ -49,6 +49,8 @@ case class MoveEvent(orig: Pos, dest: Pos, color: Color) extends Event {
}
object MoveEvent extends EventDecoder {
def apply(move: Move): MoveEvent = MoveEvent(move.orig, move.dest, move.piece.color)
def decode(str: String) = str.toList match {
case List(o, d, c) for {
orig decodePos get o

View File

@ -4,7 +4,7 @@ package model
import lila.chess._
import Piotr._
case class EventStack(events: IndexedSeq[(Int, Event)]) {
case class EventStack(events: Seq[(Int, Event)]) {
def encode: String = (events map {
case (version, event) event.encode map (version.toString + _)
@ -15,15 +15,31 @@ case class EventStack(events: IndexedSeq[(Int, Event)]) {
def optimize: EventStack = {
var previous: Boolean = false
EventStack(
(events.toList.reverse take EventStack.maxEvents map {
(events.reverse take EventStack.maxEvents map {
case (v, PossibleMovesEvent(_)) if previous (v, PossibleMovesEvent(Map.empty))
case (v, e @ PossibleMovesEvent(_)) previous = true; (v, e)
case x x
}).reverse.toIndexedSeq
case (v, e @ PossibleMovesEvent(_)) previous = true; (v, e)
case x x
}).reverse
)
}
def withMove(move: Move): EventStack = this
def version: Int = events.lastOption map (_._1) getOrElse 0
def withMove(move: Move): EventStack = withEvents(MoveEvent(move) :: {
move match {
case _ Nil
}
})
def withEvents(newEvents: List[Event]): EventStack = {
def versionEvents(v: Int, events: List[Event]): List[(Int, Event)] = events match {
case Nil Nil
case event :: rest (v + 1, event) :: versionEvents(v + 1, rest)
}
copy(events = events ++ versionEvents(version, newEvents))
}
}
object EventStack {
@ -39,9 +55,11 @@ object EventStack {
decoder EventDecoder.all get code(0)
event decoder decode data
} yield (version, event)
}).toIndexedSeq.flatten
}).toSeq.flatten
)
def apply(events: Event*): EventStack =
new EventStack(events.zipWithIndex map (_.swap) toIndexedSeq)
def apply(): EventStack = new EventStack(Seq.empty)
def build(events: Event*): EventStack =
new EventStack(events.zipWithIndex map (_.swap) toSeq)
}

View File

@ -3,12 +3,13 @@ package lila.system
import model._
import lila.chess._
import Pos._
import org.specs2.matcher.{ Expectable, Matcher }
class EventStackTest extends SystemTest {
"an event stack" should {
"encode and decode all events without loss" in {
val stack = EventStack(
val stack = EventStack.build(
StartEvent(),
MoveEvent(orig = G4, dest = C3, color = Black),
PossibleMovesEvent(Map(A7 -> List(A8, B8))),
@ -33,13 +34,13 @@ class EventStackTest extends SystemTest {
EventStack decode stack.encode must_== stack
}
"decode and re-encode production data events" in {
dbGame5.players.forall { player =>
dbGame5.players.forall { player
(EventStack decode player.evts).encode must_== player.evts
}
}
"optimize events" in {
"empty duplicated possible move events" in {
EventStack(
EventStack.build(
StartEvent(),
MoveEvent(orig = G4, dest = C3, color = Black),
PossibleMovesEvent(Map(A7 -> List(A8, B8))),
@ -49,27 +50,51 @@ class EventStackTest extends SystemTest {
PossibleMovesEvent(Map(A5 -> List(A8, B8))),
MoretimeEvent(White, 15),
EndEvent()
).optimize must_== EventStack(
StartEvent(),
MoveEvent(orig = G4, dest = C3, color = Black),
PossibleMovesEvent(Map()),
MoveEvent(orig = E5, dest = F6, color = White),
PossibleMovesEvent(Map()),
MoveEvent(orig = G4, dest = C3, color = Black),
PossibleMovesEvent(Map(A5 -> List(A8, B8))),
MoretimeEvent(White, 15),
EndEvent()
)
).optimize must_== EventStack.build(
StartEvent(),
MoveEvent(orig = G4, dest = C3, color = Black),
PossibleMovesEvent(Map()),
MoveEvent(orig = E5, dest = F6, color = White),
PossibleMovesEvent(Map()),
MoveEvent(orig = G4, dest = C3, color = Black),
PossibleMovesEvent(Map(A5 -> List(A8, B8))),
MoretimeEvent(White, 15),
EndEvent()
)
}
"keep only the %d more recent events" format EventStack.maxEvents in {
val nb = EventStack.maxEvents
val someEvent = CheckEvent(pos = D6)
val endEvent = EndEvent()
val events = List.fill(nb + 40)(someEvent) :+ endEvent
val stack = EventStack(events: _*)
val stack = EventStack.build(events: _*)
val expected = (List.fill(nb - 1)(someEvent) :+ endEvent)
stack.optimize.events.toList map (_._2) must_== expected
}
}
"apply move events" in {
"start with no events" in {
EventStack().events must beEmpty
}
"add a move event" in {
val stack = EventStack() withMove newMove(
piece = White.pawn, orig = D2, dest = D4
)
stack.events must_== Seq(
1 -> MoveEvent(D2, D4, White)
)
}
"add two move events" in {
val stack = EventStack() withMove newMove(
piece = White.pawn, orig = D2, dest = D4
) withMove newMove(
piece = Black.pawn, orig = D7, dest = D5
)
stack.events must_== Seq(
1 -> MoveEvent(D2, D4, White),
2 -> MoveEvent(D7, D5, Black)
)
}
}
}
}

View File

@ -2,6 +2,7 @@ package lila.system
import scala.util.Random
import lila.chess._
import model._
import DbGame._
@ -113,4 +114,24 @@ trait Fixtures {
)),
lastMove = Some("d8 d2")
)
def newMove(
piece: Piece,
orig: Pos,
dest: Pos,
before: Board = Board(),
after: Board = Board(),
capture: Option[Pos] = None,
castles: Boolean = false,
promotion: Option[PromotableRole] = None,
enpassant: Boolean = false) = Move(
piece = piece,
orig = orig,
dest = dest,
before = before,
after = after,
capture = capture,
castles = castles,
promotion = promotion,
enpassant = enpassant)
}