/
Fens.scala
84 lines (74 loc) · 2.64 KB
/
Fens.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package lila.ws
import akka.actor.typed.ActorRef
import chess.Color
import chess.format.{ FEN, Uci }
import java.util.concurrent.ConcurrentHashMap
import lila.ws.ipc._
import lila.ws.{ Clock, Position }
/* Manages subscriptions to FEN updates */
object Fens {
case class Watched(position: Option[Position], clients: Set[ActorRef[ClientMsg]])
private val games = new ConcurrentHashMap[Game.Id, Watched](1024)
// client starts watching
def watch(gameIds: Iterable[Game.Id], client: Client): Unit =
gameIds foreach { gameId =>
games
.compute(
gameId,
{
case (_, null) => Watched(None, Set(client))
case (_, Watched(pos, clients)) => Watched(pos, clients + client)
}
)
.position foreach { p =>
client ! ClientIn.Fen(gameId, p)
}
}
// when a client disconnects
def unwatch(gameIds: Iterable[Game.Id], client: Client): Unit =
gameIds foreach { gameId =>
games.computeIfPresent(
gameId,
(_, watched) => {
val newClients = watched.clients - client
if (newClients.isEmpty) null
else watched.copy(clients = newClients)
}
)
}
// a game finishes
def finish(gameId: Game.Id, winner: Option[Color]) =
games.computeIfPresent(
gameId,
(_, watched) => {
watched.clients foreach { _ ! ClientIn.Finish(gameId, winner) }
null
}
)
// move coming from the server
def move(gameId: Game.Id, json: JsonString, moveBy: Option[Color]): Unit =
games.computeIfPresent(
gameId,
(_, watched) => {
val turnColor = moveBy.fold(Color.white)(c => !c)
(json.value match {
case MoveClockRegex(uciS, fenS, wcS, bcS) =>
for {
uci <- Uci(uciS)
wc <- wcS.toIntOption
bc <- bcS.toIntOption
} yield Position(uci, FEN(fenS), Some(Clock(wc, bc)), turnColor)
case MoveRegex(uciS, fenS) => Uci(uciS) map { Position(_, FEN(fenS), None, turnColor) }
case _ => None
}).fold(watched) { position =>
val msg = ClientIn.Fen(gameId, position)
watched.clients foreach { _ ! msg }
watched.copy(position = Some(position))
}
}
)
// ...,"uci":"h2g2","san":"Rg2","fen":"r2qb1k1/p2nbrpn/6Np/3pPp1P/1ppP1P2/2P1B3/PP2B1R1/R2Q1NK1",...,"clock":{"white":121.88,"black":120.94}
private val MoveRegex = """uci":"([^"]+)".+fen":"([^"]+)""".r.unanchored
private val MoveClockRegex = """uci":"([^"]+)".+fen":"([^"]+).+white":(\d+).+black":(\d+)""".r.unanchored
def size = games.size
}