const REVEAL_COUNTDOWN_MS = 1500; /* 1.5 seconds to reveal a tile */
const MIN_CENTER_TILES = 7; /* auto-deal to maintain this many */
const MIN_WORD_LENGTH = 4;
+const IDLE_TIMEOUT_MS = 120000; /* 2 minutes of inactivity before warning */
+const IDLE_COUNTDOWN_MS = 10000; /* 10 seconds to respond to warning */
class Anagrams extends Game {
constructor(id) {
this._vote_timer = null;
this._revealing = false;
this._reveal_timer = null;
+ this._idle_timer = null;
+ this._idle_countdown_timer = null;
}
/*****************************************************
_finish_game() {
this.state.finished = true;
+ this._clear_idle_timers();
this.broadcast_event_object("game-over", {
scores: this._compute_all_scores()
});
}
+ /*****************************************************
+ * Idle timeout (bag empty only) *
+ *****************************************************/
+
+ _reset_idle_timer() {
+ if (!this.state.started || this.state.finished) return;
+ if (this.state.bag.length > 0) return;
+
+ this._clear_idle_timers();
+ this._idle_timer = setTimeout(() => {
+ this._idle_timer = null;
+ this._start_idle_countdown();
+ }, IDLE_TIMEOUT_MS);
+ }
+
+ _start_idle_countdown() {
+ this.broadcast_event_object("game-ending", {
+ countdown_ms: IDLE_COUNTDOWN_MS
+ });
+
+ this._idle_countdown_timer = setTimeout(() => {
+ this._idle_countdown_timer = null;
+ this._finish_game();
+ }, IDLE_COUNTDOWN_MS);
+ }
+
+ handle_still_looking(request, response) {
+ const session_id = request.session.id;
+ if (!this.state.player_words[session_id]) {
+ response.sendStatus(400);
+ return;
+ }
+
+ this._clear_idle_timers();
+ this.broadcast_event_object("game-continued", {});
+ this._reset_idle_timer();
+ response.json({ ok: true });
+ }
+
+ _clear_idle_timers() {
+ if (this._idle_timer) {
+ clearTimeout(this._idle_timer);
+ this._idle_timer = null;
+ }
+ if (this._idle_countdown_timer) {
+ clearTimeout(this._idle_countdown_timer);
+ this._idle_countdown_timer = null;
+ }
+ }
+
_all_player_words() {
const result = {};
for (const sid of Object.keys(this.state.player_words)) {
})}\n\n`);
}
+ /* Send game-ending countdown if one is active. */
+ if (this._idle_countdown_timer) {
+ response.write(`event: game-ending\ndata: ${JSON.stringify({
+ countdown_ms: IDLE_COUNTDOWN_MS
+ })}\n\n`);
+ }
+
/* Send game-over if finished. */
if (this.state.finished) {
response.write(`event: game-over\ndata: ${JSON.stringify({
Anagrams.router = express.Router();
const router = Anagrams.router;
+/* Reset idle timer on any player action. */
+router.post('*', (request, response, next) => {
+ if (request.game) request.game._reset_idle_timer();
+ next();
+});
+
router.post('/start', (request, response) => {
request.game.handle_start(request, response);
});
request.game.handle_done(request, response);
});
+router.post('/still-looking', (request, response) => {
+ request.game.handle_still_looking(request, response);
+});
+
Anagrams.meta = {
name: "Anagrams",
identifier: "anagrams",