progressive takeback delay - closes #2024

pull/2028/head
Thibault Duplessis 2016-06-21 11:52:42 +02:00
parent ebb271543d
commit f98136240c
2 changed files with 54 additions and 16 deletions

View File

@ -1,18 +1,17 @@
package lila.round
import scala.concurrent.duration._
import akka.actor._
import akka.pattern.{ ask, pipe }
import org.joda.time.DateTime
import scala.concurrent.duration._
import actorApi._, round._
import chess.Color
import lila.game.{ Game, Pov, PovRef, PlayerRef, Event, Progress }
import lila.game.{ Game, Pov, Event }
import lila.hub.actorApi.DeployPost
import lila.hub.actorApi.map._
import lila.hub.actorApi.round.FishnetPlay
import lila.hub.SequentialActor
import lila.i18n.I18nKey.{ Select => SelectI18nKey }
import makeTimeout.large
private[round] final class Round(
@ -51,6 +50,8 @@ private[round] final class Round(
}
}
var takebackSituation = Round.TakebackSituation()
def process = {
case ReceiveTimeout => fuccess {
@ -157,11 +158,23 @@ private[round] final class Round(
}
}
case RematchYes(playerRef) => handle(playerRef)(rematcher.yes)
case RematchNo(playerRef) => handle(playerRef)(rematcher.no)
case RematchYes(playerRef) => handle(playerRef)(rematcher.yes)
case RematchNo(playerRef) => handle(playerRef)(rematcher.no)
case TakebackYes(playerRef) => handle(playerRef)(takebacker.yes)
case TakebackNo(playerRef) => handle(playerRef)(takebacker.no)
case TakebackYes(playerRef) => handle(playerRef) { pov =>
takebacker.yes(takebackSituation)(pov) map {
case (events, situation) =>
takebackSituation = situation
events
}
}
case TakebackNo(playerRef) => handle(playerRef) { pov =>
takebacker.no(takebackSituation)(pov) map {
case (events, situation) =>
takebackSituation = situation
events
}
}
case Moretime(playerRef) => handle(playerRef) { pov =>
pov.game.clock.ifTrue(pov.game moretimeable !pov.color) ?? { clock =>
@ -249,3 +262,20 @@ private[round] final class Round(
case e: Exception => logger.warn(s"$name: ${e.getMessage}")
}
}
object Round {
case class TakebackSituation(
nbDeclined: Int = 0,
lastDeclined: Option[DateTime] = none) {
def decline = TakebackSituation(nbDeclined + 1, DateTime.now.some)
def delaySeconds = (math.pow(nbDeclined min 10, 2) * 10).toInt
def offerable = lastDeclined.fold(true) { _ isBefore DateTime.now.minusSeconds(delaySeconds) }
def reset = TakebackSituation()
}
}

View File

@ -1,5 +1,7 @@
package lila.round
import org.joda.time.DateTime
import lila.game.{ GameRepo, Game, UciMemo, Pov, Rewind, Event, Progress }
import lila.pref.{ Pref, PrefApi }
@ -8,31 +10,37 @@ private[round] final class Takebacker(
uciMemo: UciMemo,
prefApi: PrefApi) {
def yes(pov: Pov)(implicit proxy: GameProxy): Fu[Events] = IfAllowed(pov.game) {
def yes(situation: Round.TakebackSituation)(pov: Pov)(implicit proxy: GameProxy): Fu[(Events, Round.TakebackSituation)] = IfAllowed(pov.game) {
pov match {
case Pov(game, _) if pov.opponent.isProposingTakeback =>
case Pov(game, _) if pov.opponent.isProposingTakeback => {
if (pov.opponent.proposeTakebackAt == pov.game.turns) single(game)
else double(game)
case Pov(game, _) if pov.opponent.isAi => double(game)
case Pov(game, color) if (game playerCanProposeTakeback color) =>
} map (_ -> situation.reset)
case Pov(game, _) if pov.opponent.isAi => double(game) map (_ -> situation)
case Pov(game, color) if (game playerCanProposeTakeback color) && situation.offerable => {
messenger.system(game, _.takebackPropositionSent)
val progress = Progress(game) map { g =>
g.updatePlayer(color, _ proposeTakeback g.turns)
}
proxy.save(progress) inject List(Event.TakebackOffers(color.white, color.black))
} map (_ -> situation)
case _ => fufail(ClientError("[takebacker] invalid yes " + pov))
}
}
def no(pov: Pov)(implicit proxy: GameProxy): Fu[Events] = pov match {
def no(situation: Round.TakebackSituation)(pov: Pov)(implicit proxy: GameProxy): Fu[(Events, Round.TakebackSituation)] = pov match {
case Pov(game, color) if pov.player.isProposingTakeback => proxy.save {
messenger.system(game, _.takebackPropositionCanceled)
Progress(game) map { g => g.updatePlayer(color, _.removeTakebackProposition) }
} inject List(Event.TakebackOffers(false, false))
} inject {
List(Event.TakebackOffers(false, false)) -> situation
}
case Pov(game, color) if pov.opponent.isProposingTakeback => proxy.save {
messenger.system(game, _.takebackPropositionDeclined)
Progress(game) map { g => g.updatePlayer(!color, _.removeTakebackProposition) }
} inject List(Event.TakebackOffers(false, false))
} inject {
List(Event.TakebackOffers(false, false)) -> situation.decline
}
case _ => fufail(ClientError("[takebacker] invalid no " + pov))
}
@ -46,7 +54,7 @@ private[round] final class Takebacker(
}
}
private def IfAllowed(game: Game)(f: => Fu[Events]): Fu[Events] =
private def IfAllowed[A](game: Game)(f: => Fu[A]): Fu[A] =
if (!game.playable) fufail(ClientError("[takebacker] game is over " + game.id))
else isAllowedByPrefs(game) flatMap {
_.fold(f, fufail(ClientError("[takebacker] disallowed by preferences " + game.id)))