Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Keep track of what positions people tend to sit or disconnect in #5254

Merged
merged 1 commit into from
Jul 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 4 additions & 3 deletions app/controllers/User.scala
Original file line number Diff line number Diff line change
Expand Up @@ -275,10 +275,11 @@ object User extends LilaController {
Env.mod.logApi.userHistory(user.id).logTimeIfGt(s"$username logApi.userHistory", 2 seconds) zip
Env.plan.api.recentChargesOf(user).logTimeIfGt(s"$username plan.recentChargesOf", 2 seconds) zip
Env.report.api.byAndAbout(user, 20).logTimeIfGt(s"$username report.byAndAbout", 2 seconds) zip
Env.pref.api.getPref(user).logTimeIfGt(s"$username pref.getPref", 2 seconds) flatMap {
case history ~ charges ~ reports ~ pref =>
Env.pref.api.getPref(user).logTimeIfGt(s"$username pref.getPref", 2 seconds) zip
Env.playban.api.getSitAndDcCounter(user) flatMap {
case history ~ charges ~ reports ~ pref ~ sitAndDcCounter =>
Env.user.lightUserApi.preloadMany(reports.userIds).logTimeIfGt(s"$username lightUserApi.preloadMany", 2 seconds) inject
html.user.mod.parts(user, history, charges, reports, pref).some
html.user.mod.parts(user, history, charges, reports, pref, sitAndDcCounter).some
}
val actions = UserRepo.isErased(user) map { erased =>
html.user.mod.actions(user, emails, erased).some
Expand Down
10 changes: 9 additions & 1 deletion app/views/user/mod.scala
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,11 @@ object mod {
)
)

def parts(u: User, history: List[lila.mod.Modlog], charges: List[lila.plan.Charge], reports: lila.report.Report.ByAndAbout, pref: lila.pref.Pref)(implicit ctx: Context) = frag(
def parts(u: User, history: List[lila.mod.Modlog], charges: List[lila.plan.Charge], reports: lila.report.Report.ByAndAbout, pref: lila.pref.Pref, sitAndDcCounter: Int)(implicit ctx: Context) = frag(
roles(u),
prefs(u, pref),
plan(u, charges),
sitDcCounter(sitAndDcCounter),
modLog(u, history),
reportLog(u, reports)
)
Expand All @@ -151,6 +152,13 @@ object mod {
)
)

def sitDcCounter(sitAndDcCounter: Int)(implicit ctx: Context) = div(id := "mz_sitdccounter")(
strong(cls := "text inline")("Sit/disconnect counter: "),
span(cls := "text inline")(sitAndDcCounter.toString),
br,
span(cls := "text inline")("+1 for every sit/disconnect in 'winning' position, -1 for 'losing' position")
)

def plan(u: User, charges: List[lila.plan.Charge])(implicit ctx: Context) = charges.headOption.map { firstCharge =>
div(id := "mz_plan")(
strong(cls := "text", dataIcon := patronIconChar)(
Expand Down
29 changes: 20 additions & 9 deletions modules/playban/src/main/PlaybanApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,30 @@ final class PlaybanApi(
blameable(game) flatMap { _ ?? f }
}

private def roughWinEstimate(game: Game, color: Color) = {
game.chess.board.materialImbalance match {
case a if a >= 5 => 1
case a if a <= -5 => -1
case _ => 0
}
} * (if (color == Color.White) 1 else -1)

def abort(pov: Pov, isOnGame: Set[Color]): Funit = IfBlameable(pov.game) {
pov.player.userId.ifTrue(isOnGame(pov.opponent.color)) ?? { userId =>
save(Outcome.Abort, userId) >>- feedback.abort(pov)
save(Outcome.Abort, userId, 0) >>- feedback.abort(pov)
}
}

def noStart(pov: Pov): Funit = IfBlameable(pov.game) {
pov.player.userId ?? { userId =>
save(Outcome.NoPlay, userId) >>- feedback.noStart(pov)
save(Outcome.NoPlay, userId, 0) >>- feedback.noStart(pov)
}
}

def rageQuit(game: Game, quitterColor: Color): Funit =
sandbag(game, quitterColor) >> IfBlameable(game) {
game.player(quitterColor).userId ?? { userId =>
save(Outcome.RageQuit, userId) >>- feedback.rageQuit(Pov(game, quitterColor))
save(Outcome.RageQuit, userId, roughWinEstimate(game, quitterColor)) >>- feedback.rageQuit(Pov(game, quitterColor))
}
}

Expand All @@ -73,7 +81,7 @@ final class PlaybanApi(
seconds = nowSeconds - game.movedAt.getSeconds
limit <- unreasonableTime
if seconds >= limit
} yield save(Outcome.Sitting, userId) >>- feedback.sitting(Pov(game, flaggerColor))
} yield save(Outcome.Sitting, userId, roughWinEstimate(game, flaggerColor)) >>- feedback.sitting(Pov(game, flaggerColor))

// flagged after waiting a short time;
// but the previous move used a long time.
Expand All @@ -84,7 +92,7 @@ final class PlaybanApi(
lastMovetime <- movetimes.lastOption
limit <- unreasonableTime
if lastMovetime.toSeconds >= limit
} yield save(Outcome.SitMoving, userId) >>- feedback.sitting(Pov(game, flaggerColor))
} yield save(Outcome.SitMoving, userId, roughWinEstimate(game, flaggerColor)) >>- feedback.sitting(Pov(game, flaggerColor))

sandbag(game, flaggerColor) flatMap { isSandbag =>
IfBlameable(game) {
Expand All @@ -102,7 +110,7 @@ final class PlaybanApi(
w <- winner
loserId <- game.player(!w).userId
} yield {
if (Status.NoStart is status) save(Outcome.NoPlay, loserId) >>- feedback.noStart(Pov(game, !w))
if (Status.NoStart is status) save(Outcome.NoPlay, loserId, 0) >>- feedback.noStart(Pov(game, !w))
else goodOrSandbag(game, !w, isSandbag)
})
}
Expand All @@ -111,7 +119,7 @@ final class PlaybanApi(
private def goodOrSandbag(game: Game, color: Color, isSandbag: Boolean): Funit =
game.player(color).userId ?? { userId =>
if (isSandbag) feedback.sandbag(Pov(game, color))
save(if (isSandbag) Outcome.Sandbag else Outcome.Good, userId)
save(if (isSandbag) Outcome.Sandbag else Outcome.Good, userId, 0)
}

def currentBan(userId: User.ID): Fu[Option[TempBan]] = coll.find(
Expand Down Expand Up @@ -148,7 +156,10 @@ final class PlaybanApi(
}(scala.collection.breakOut)
}

private def save(outcome: Outcome, userId: User.ID): Funit = {
def getSitAndDcCounter(user: User): Fu[Int] =
coll.primitiveOne[Int]($doc("_id" -> user.id, "c" $exists true), "c").map(~_)

private def save(outcome: Outcome, userId: User.ID, sitAndDcCounterChange: Int): Funit = {
lila.mon.playban.outcome(outcome.key)()
coll.findAndUpdate(
selector = $id(userId),
Expand All @@ -157,7 +168,7 @@ final class PlaybanApi(
"$each" -> List(outcome),
"$slice" -> -30
)
)),
), "$inc" -> $doc("c" -> sitAndDcCounterChange)),
fetchNewObject = true,
upsert = true
).map(_.value) map2 UserRecordBSONHandler.read flatMap {
Expand Down
4 changes: 3 additions & 1 deletion modules/playban/src/main/model.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import play.api.libs.json._
case class UserRecord(
_id: String,
o: Option[List[Outcome]],
b: Option[List[TempBan]]
b: Option[List[TempBan]],
c: Option[Int]
) {

def userId = _id
def outcomes: List[Outcome] = ~o
def bans: List[TempBan] = ~b
def sitAndDcCounter: Int = ~c

def banInEffect = bans.lastOption.exists(_.inEffect)

Expand Down