First take at applying event moves to the stack
parent
9cb335463d
commit
d1f4bbdc50
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue