2 次代碼提交 0956d1f433 ... aa378d8542

作者 SHA1 備註 提交日期
  Oleg Panashchenko aa378d8542 dict-ru new word 3 周之前
  Oleg Panashchenko 42f7277d14 fix(solver): cap duplicated letters that are gray and non-gray in one row 3 周之前
共有 3 個文件被更改,包括 29 次插入 和 1 次删除
  1. 2 1
      data/word-bank-ru.json
  2. 3 0
      raw/15-bug-gray-yellow.md
  3. 24 0
      server/src/routes/solver.ts

+ 2 - 1
data/word-bank-ru.json

@@ -2866,7 +2866,8 @@
     "ястык",
     "яхонт",
     "грант",
-    "камео"
+    "камео",
+    "брыль"
   ],
   "targets": [
     {

+ 3 - 0
raw/15-bug-gray-yellow.md

@@ -0,0 +1,3 @@
+# Bug in server/src/routes/solver.ts
+
+When a guess has one letter twice, and result has one of the letters gray and another non-gray, the solver should exclude words, where that letter occures more than once. For example, if 'karma' guess results in 'xxxxy', the word 'award' should not be suggested, as guess results show only one occurence of the letter 'a',

+ 24 - 0
server/src/routes/solver.ts

@@ -64,6 +64,7 @@ export function createSolverRouter(wordBank: WordBank): Router {
     const greens = new Map<number, string>(); // position → letter
     const yellowForbidden = new Map<string, Set<number>>(); // letter → forbidden positions
     const yellowMinCount = new Map<string, number>(); // letter → min occurrences in target
+    const maxCounts = new Map<string, number>(); // letter → exact occurrences in target (gray + non-gray in same row)
     const grayCandidates = new Set<string>(); // letters seen as gray anywhere
     const allGreenYellow = new Set<string>(); // letters seen as g or y in ANY row
 
@@ -73,6 +74,7 @@ export function createSolverRouter(wordBank: WordBank): Router {
 
       // Per-row counts for green+yellow (min number of each letter in target)
       const rowCounts = new Map<string, number>();
+      const rowGray = new Set<string>();
 
       for (let i = 0; i < 5; i++) {
         const letter = g[i];
@@ -91,6 +93,7 @@ export function createSolverRouter(wordBank: WordBank): Router {
           yellowForbidden.get(letter)!.add(i);
         } else {
           grayCandidates.add(letter);
+          rowGray.add(letter);
         }
       }
 
@@ -98,6 +101,14 @@ export function createSolverRouter(wordBank: WordBank): Router {
       for (const [letter, count] of rowCounts) {
         yellowMinCount.set(letter, Math.max(yellowMinCount.get(letter) ?? 0, count));
       }
+
+      // A letter that is both gray and green/yellow in this row is capped
+      // at its green+yellow count (the gray marks the "extra" occurrence).
+      for (const [letter, count] of rowCounts) {
+        if (rowGray.has(letter)) {
+          maxCounts.set(letter, Math.min(maxCounts.get(letter) ?? count, count));
+        }
+      }
     }
 
     // Remove from gray set: any letter that also appears as green or yellow
@@ -156,6 +167,19 @@ export function createSolverRouter(wordBank: WordBank): Router {
       }
       if (!ok) continue;
 
+      // Exact (upper-bound) count check
+      for (const [letter, maxCount] of maxCounts) {
+        let count = 0;
+        for (let i = 0; i < 5; i++) {
+          if (word[i] === letter) count++;
+        }
+        if (count > maxCount) {
+          ok = false;
+          break;
+        }
+      }
+      if (!ok) continue;
+
       candidates.push(word);
     }