Handle duplicate yellows + better rule testing

This commit is contained in:
2026-06-10 02:52:59 -06:00
parent 3af3fb59dc
commit 38760693c2

View File

@@ -1,3 +1,5 @@
const std = @import("std");
/// The length of Wordle words. /// The length of Wordle words.
pub const WordLen = 5; pub const WordLen = 5;
@@ -19,8 +21,11 @@ pub const Clues = [WordLen]Clue;
/// Guesses a word given a solution and returns the clues. /// Guesses a word given a solution and returns the clues.
pub fn checkGuess(guess: Word, solution: Word) Clues { pub fn checkGuess(guess: Word, solution: Word) Clues {
// init clues // init clues, defaulting to gray clues
var clues: Clues = undefined; var clues: Clues = @splat(Clue.Gray);
// track which letters have been used for yellow clues
var duplicates: [WordLen]bool = @splat(false);
// compare each letter pairwise // compare each letter pairwise
for (guess, 0..) |guessLetter, idx| { for (guess, 0..) |guessLetter, idx| {
@@ -30,13 +35,11 @@ pub fn checkGuess(guess: Word, solution: Word) Clues {
continue; continue;
} }
// default to a gray clue
clues[idx] = Clue.Gray;
// if a matching letter in the solution can be found, write yellow // if a matching letter in the solution can be found, write yellow
for (solution) |solutionLetter| { for (solution, &duplicates) |solutionLetter, *duplicate| {
if (guessLetter == solutionLetter) { if (guessLetter == solutionLetter and !duplicate.*) {
clues[idx] = Clue.Yellow; clues[idx] = Clue.Yellow;
duplicate.* = true;
break; break;
} }
} }
@@ -54,30 +57,26 @@ pub fn strToWord(str: WordLiteral) Word {
} }
/// Asserts that two word literals produce the expected clues. /// Asserts that two word literals produce the expected clues.
fn expectGuessClues(guess: WordLiteral, solution: WordLiteral, expectClues: WordLiteral) error{Mismatch}!void { fn expectGuessClues(guess: WordLiteral, solution: WordLiteral, expectClues: WordLiteral) !void {
// convert literals to the non-null word type // convert literals to the non-null word type
const guessWord = strToWord(guess); const guessWord = strToWord(guess);
const solutionWord = strToWord(solution); const solutionWord = strToWord(solution);
const expectCluesWord = strToWord(expectClues);
// check the guess and produce clues // check the guess and produce clues
const clues = checkGuess(guessWord, solutionWord); const clues = checkGuess(guessWord, solutionWord);
// confirm that the expected clues match the clues // convert the clues to a word
for (clues, expectCluesWord) |clue, expectedChar| { var cluesWord: Word = undefined;
// convert character to associated clue variant for (clues, &cluesWord) |src, *dst| {
const expected = switch (expectedChar) { dst.* = switch (src) {
' ' => Clue.Gray, Clue.Gray => ' ',
'Y' => Clue.Yellow, Clue.Yellow => 'Y',
'G' => Clue.Green, Clue.Green => 'G',
else => unreachable,
}; };
}
// if clues don't match, return an error // test the equality of the strings
if (expected != clue) { try std.testing.expectEqualStrings(&cluesWord, expectClues);
return error.Mismatch;
}
}
} }
test "full match" { test "full match" {
@@ -92,6 +91,10 @@ test "no match" {
try expectGuessClues("salet", "fuzzy", " "); try expectGuessClues("salet", "fuzzy", " ");
} }
test "duplicate letters" { test "anagram" {
try expectGuessClues("fuzzy", "zesty", " Y "); try expectGuessClues("beats", "abets", "YYYGG");
}
test "duplicate letters" {
try expectGuessClues("fuzzy", "zesty", " Y G");
} }