Files
2026-07-06 07:56:56 -04:00

148 lines
4.8 KiB
Python

import pytest
from wordle_solver.game import Clue, score_guess
from wordle_solver.solver import Solver, rank_guesses
G = Clue.GREEN
Y = Clue.YELLOW
B = Clue.GREY
ALL_GREEN = (G, G, G, G, G)
def make_solver(*extra):
"""Solver with a small, controlled answer list."""
answers = ["crane", "crave", "trace", "grace", "brace"]
return Solver(answers, list(extra))
class TestSolverApply:
def test_all_candidates_initially(self):
s = make_solver()
assert set(s.candidates) == {"crane", "crave", "trace", "grace", "brace"}
def test_apply_narrows_candidates(self):
s = make_solver()
# scoring "crane" against "crane" → all green → only "crane" remains
s.apply("crane", ALL_GREEN)
assert s.candidates == ["crane"]
def test_apply_chain(self):
s = make_solver()
# first clue: 'c' correct, 'r' present, rest absent/wrong
pattern1 = score_guess("crane", "crave")
s.apply("crane", pattern1)
# 'crave' must still be a candidate; 'grace' and 'brace' may or may not be
assert "crave" in s.candidates
assert "crane" not in s.candidates
def test_apply_empty_candidates_on_no_match(self):
s = make_solver()
# impossible pattern eliminates everything
s.apply("zzzzz", ALL_GREEN)
assert s.candidates == []
def test_reset_restores_all_candidates(self):
s = make_solver()
s.apply("crane", ALL_GREEN)
s.reset()
assert set(s.candidates) == {"crane", "crave", "trace", "grace", "brace"}
def test_reset_clears_history(self):
s = make_solver()
s.apply("crane", ALL_GREEN)
s.reset()
assert s.history == []
def test_is_solved_after_all_green(self):
s = make_solver()
s.apply("crane", ALL_GREEN)
assert s.is_solved()
def test_is_solved_false_initially(self):
assert not make_solver().is_solved()
class TestHardModeConstraints:
def test_green_positions_extracted(self):
s = make_solver()
s.apply("crane", (G, B, B, B, B)) # 'c' locked at position 0
green, _ = s.hard_mode_constraints()
assert green == {0: "c"}
def test_yellow_minimum_count(self):
s = make_solver()
s.apply("crane", (B, Y, B, B, B)) # 'r' must appear at least once
_, min_count = s.hard_mode_constraints()
assert min_count["r"] >= 1
def test_multiple_greens_accumulated(self):
s = make_solver()
s.apply("crane", (G, G, B, B, B))
s.apply("crave", (G, G, B, G, B))
green, _ = s.hard_mode_constraints()
assert green[0] == "c"
assert green[1] == "r"
assert green[3] == "v"
def test_duplicate_yellow_counts_correctly(self):
s = make_solver()
# Two 'a's in a guess, both yellow → need at least 2 'a's
s.apply("llama", (B, B, Y, B, Y))
_, min_count = s.hard_mode_constraints()
assert min_count["a"] >= 2
class TestHardModePool:
def test_no_history_returns_all_words(self):
s = make_solver("extra1", "extra2")
pool = s.hard_mode_pool()
assert set(pool) == set(s.all_words)
def test_green_constraint_filters_pool(self):
s = make_solver()
s.apply("crane", (G, B, B, B, B)) # first letter must be 'c'
pool = s.hard_mode_pool()
assert all(w[0] == "c" for w in pool)
def test_yellow_constraint_filters_pool(self):
s = make_solver()
s.apply("crane", (B, Y, B, B, B)) # 'r' must be present
pool = s.hard_mode_pool()
assert all("r" in w for w in pool)
def test_word_violating_green_excluded(self):
s = make_solver()
s.apply("crane", (G, B, B, B, B)) # 'c' at position 0
pool = s.hard_mode_pool()
assert "trace" not in pool # starts with 't'
class TestRankGuesses:
def test_returns_at_most_top_n(self):
answers = ["crane", "crave", "trace", "grace", "brace"]
pool = answers[:]
results = rank_guesses(pool, answers, top_n=3)
assert len(results) <= 3
def test_possible_answers_tagged(self):
answers = ["crane", "crave"]
pool = answers + ["zzzzz"]
results = rank_guesses(pool, answers, top_n=10)
by_word = {w: is_ans for w, _, is_ans in results}
assert by_word["crane"] is True
assert by_word["crave"] is True
assert by_word["zzzzz"] is False
def test_scores_descending(self):
answers = ["crane", "crave", "trace", "grace", "brace"]
results = rank_guesses(answers, answers, top_n=5)
scores = [s for _, s, _ in results]
assert scores == sorted(scores, reverse=True)
def test_single_candidate_returned(self):
answers = ["crane"]
results = rank_guesses(answers, answers, top_n=5)
assert len(results) == 1
assert results[0][0] == "crane"