]> git.cworth.org Git - lmno.games/commitdiff
anagrams: Display words and letters being claimed by another player
authorCarl Worth <cworth@cworth.org>
Sun, 8 Mar 2026 16:03:10 +0000 (12:03 -0400)
committerCarl Worth <cworth@cworth.org>
Sun, 8 Mar 2026 16:03:10 +0000 (12:03 -0400)
This allows other players to be a part of the action, (and even try
to predict what the final word will be while a claim is happening).

anagrams/anagrams.css
anagrams/anagrams.jsx

index fb7f2dab7a54036e53ff1e9866c496edc20282a5..5ab18cc1ce92f117a931e80dcc5659913625cce7 100644 (file)
   color: #555;
 }
 
-/* Notification for claim activity */
-.claim-notification {
-  background: #fff3cd;
-  border: 1px solid #ffc107;
-  border-radius: 4px;
-  padding: 0.5em 1em;
-  margin-bottom: 1em;
-  font-size: 0.95em;
+
+.claim-items-display {
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  gap: 2px;
 }
index 13e3fb3713233941d6ff73e59ab55a9a099890aa..8fac388ba0954f13abe836c2e4198329133b398f 100644 (file)
@@ -115,6 +115,8 @@ class Game extends React.Component {
       claim_player: null,       /* name of the current claimer */
       claim_rack: [],           /* tiles I've claimed (in my order) */
       claimed_words: [],        /* word objects I've stolen */
+      other_claim_letters: [],  /* letters claimed by another player */
+      other_claim_words: [],    /* words stolen by another player */
       claim_error: null,
       claim_warning: false,
       claim_remaining_ms: 0,
@@ -256,6 +258,8 @@ class Game extends React.Component {
       claiming: is_me,
       claim_rack: is_me ? [] : this.state.claim_rack,
       claimed_words: is_me ? [] : this.state.claimed_words,
+      other_claim_letters: is_me ? [] : (data.claimed_letters || []),
+      other_claim_words: is_me ? [] : (data.claimed_words || []),
       claim_error: null,
       claim_warning: false,
       claim_remaining_ms: data.timeout_ms
@@ -285,6 +289,8 @@ class Game extends React.Component {
       claiming: false,
       claim_rack: [],
       claimed_words: [],
+      other_claim_letters: [],
+      other_claim_words: [],
       claim_error: null,
       claim_warning: false,
       claim_remaining_ms: 0
@@ -293,14 +299,15 @@ class Game extends React.Component {
   }
 
   receive_letter_claimed(data) {
-    /* Remove from center. */
     this.setState(prev => ({
-      center: prev.center.filter(t => t.id !== data.tile.id)
+      center: prev.center.filter(t => t.id !== data.tile.id),
+      other_claim_letters: prev.claim_active
+        ? prev.other_claim_letters
+        : [...prev.other_claim_letters, data.tile]
     }));
   }
 
   receive_letter_returned(data) {
-    /* Add back to center. */
     this.setState(prev => {
       const positions = { ...prev.tile_positions };
       if (!positions[data.tile.id]) {
@@ -308,17 +315,31 @@ class Game extends React.Component {
       }
       return {
         center: [...prev.center, data.tile],
-        tile_positions: positions
+        tile_positions: positions,
+        other_claim_letters: prev.other_claim_letters.filter(
+          t => t.id !== data.tile.id)
       };
     });
   }
 
   receive_word_stolen(data) {
     /* Player words update will come via player-words event. */
+    if (!this.state.claim_active) {
+      this.setState(prev => ({
+        other_claim_words: [...prev.other_claim_words,
+          { word: data.word, word_id: data.word_id }]
+      }));
+    }
   }
 
   receive_word_returned(data) {
     /* Player words update will come via player-words event. */
+    if (!this.state.claim_active) {
+      this.setState(prev => ({
+        other_claim_words: prev.other_claim_words.filter(
+          w => w.word_id !== data.word_id)
+      }));
+    }
   }
 
   receive_word_accepted(data) {
@@ -812,8 +833,34 @@ class Game extends React.Component {
       state.joined ? this.render_controls() : null,
 
       state.claim_player && !state.claim_active ? (
-        <div key="notif" className="claim-notification">
-          {state.claim_player} is forming a word...
+        <div key="notif" className="claim-area">
+          <h3>{state.claim_player} is forming a word:</h3>
+          {state.other_claim_words.length > 0
+           || state.other_claim_letters.length > 0 ? (
+            <div className="claim-items-display">
+              {state.other_claim_words
+                .slice().sort((a, b) => b.word.length - a.word.length)
+                .map((cw, i) => [
+                  i > 0 ? <span key={"sep" + cw.word_id}
+                                className="separator">+</span> : null,
+                  <span key={cw.word_id} className="claimed-word">
+                    {cw.word.split("").map((ch, j) => (
+                      <Tile key={j} letter={ch} />
+                    ))}
+                  </span>
+              ])}
+              {state.other_claim_letters
+                .slice().sort((a, b) => a.letter.localeCompare(b.letter))
+                .map((tile, i) => [
+                  (i > 0 || state.other_claim_words.length > 0)
+                    ? <span key={"sep-l" + tile.id}
+                            className="separator">+</span> : null,
+                  <span key={tile.id} className="claimed-word">
+                    <Tile letter={tile.letter} />
+                  </span>
+              ])}
+            </div>
+          ) : null}
         </div>
       ) : null,