]> git.cworth.org Git - lmno-server/commitdiff
Simplify tile dealing from the bag
authorCarl Worth <cworth@cworth.org>
Sun, 8 Mar 2026 12:23:09 +0000 (08:23 -0400)
committerCarl Worth <cworth@cworth.org>
Sun, 8 Mar 2026 12:23:09 +0000 (08:23 -0400)
No longer require multiple people to request a letter. Instead, any person
can draw a new letter from the bag as long as no letter reveal is currently
in progress.

anagrams.js

index 2f841aea144f716e1da9f13257926cc3f265ad5c..cdf9e3df6cccad3f2b83b44702420b368a0de28b 100644 (file)
@@ -28,8 +28,6 @@ class Anagrams extends Game {
       claimed_words: [],     /* { owner_session, word_id, word_obj } */
       /* Voting state. */
       vote_pending: null,
-      /* Letter request state. */
-      letter_requests: new Set(),
       /* Game state. */
       finished: false,
       done_players: new Set()
@@ -37,7 +35,7 @@ class Anagrams extends Game {
     this._claim_timer = null;
     this._claim_warning_timer = null;
     this._vote_timer = null;
-    this._letter_request_timer = null;
+    this._revealing = false;
     this._reveal_timer = null;
   }
 
@@ -70,15 +68,30 @@ class Anagrams extends Game {
    *****************************************************/
 
   /* Deal a letter from bag to center with a countdown reveal. */
-  deal_letter() {
-    if (this.state.bag.length === 0) return false;
+  handle_deal_letter(request, response) {
+    const session_id = request.session.id;
+    if (!this.state.player_words[session_id]) {
+      response.sendStatus(400);
+      return;
+    }
+
+    if (this.state.bag.length === 0 || this._revealing) {
+      response.json({ ok: false });
+      return;
+    }
 
     const letter = this.state.bag.pop();
     const id = this.state.next_letter_id++;
     const tile = { id, letter };
 
     this.state.center.push(tile);
-    this.state.letter_requests = new Set();
+    this._revealing = true;
+
+    if (this._reveal_timer) clearTimeout(this._reveal_timer);
+    this._reveal_timer = setTimeout(() => {
+      this._revealing = false;
+      this._reveal_timer = null;
+    }, REVEAL_COUNTDOWN_MS);
 
     this.broadcast_event_object("letter-reveal", {
       tile,
@@ -86,63 +99,11 @@ class Anagrams extends Game {
       countdown_ms: REVEAL_COUNTDOWN_MS
     });
 
-    /* Also broadcast updated bag count. */
     this.broadcast_event_object("bag-count", {
       remaining: this.state.bag.length
     });
 
-    return true;
-  }
-
-  /* Handle a player requesting a new letter. */
-  handle_request_letter(request, response) {
-    const session_id = request.session.id;
-    if (!this.state.player_words[session_id]) {
-      response.sendStatus(400);
-      return;
-    }
-
-    if (this.state.bag.length === 0) {
-      response.json({ remaining: 0 });
-      return;
-    }
-
-    /* Don't allow requests during a reveal countdown. */
-    if (this._reveal_timer) {
-      response.json({ queued: false });
-      return;
-    }
-
-    this.state.letter_requests.add(session_id);
-
-    const joined = this.joined_player_count();
-    const votes = this.state.letter_requests.size;
-
-    this.broadcast_event_object("letter-request", {
-      votes,
-      needed: joined
-    });
-
-    /* Scale timer: all players voting = immediate.
-     * One player = full 10-second wait.
-     * Linear interpolation between. */
-    if (this._letter_request_timer) {
-      clearTimeout(this._letter_request_timer);
-      this._letter_request_timer = null;
-    }
-
-    if (votes >= joined) {
-      this.deal_letter();
-    } else {
-      const ratio = votes / joined;
-      const delay_ms = Math.round(10000 * (1 - ratio));
-      this._letter_request_timer = setTimeout(() => {
-        this._letter_request_timer = null;
-        this.deal_letter();
-      }, delay_ms);
-    }
-
-    response.json({ queued: true, remaining: this.state.bag.length });
+    response.json({ ok: true });
   }
 
   /*****************************************************
@@ -825,8 +786,8 @@ router.post('/cancel-claim', (request, response) => {
   request.game.handle_cancel_claim(request, response);
 });
 
-router.post('/request-letter', (request, response) => {
-  request.game.handle_request_letter(request, response);
+router.post('/deal-letter', (request, response) => {
+  request.game.handle_deal_letter(request, response);
 });
 
 router.post('/vote', (request, response) => {