= Solution
Use a distinct constructor for each mark, and reserve every bull before allocating any cow:
``
data Mark = Bull | Cow | Miss deriving (Eq,Show)
scoreWordle :: Eq a => [a] -> [a] -> [Mark]
scoreWordle secret guess = mark pairs pool
where
pairs = zip secret guess
pool = [s | (s,g) <- pairs, s /= g]
mark [] _ = []
mark ((s,g):ps) available
| s == g = Bull : mark ps available
| otherwise = case pluck g available of
Nothing -> Miss : mark ps available
Just rest -> Cow : mark ps rest
``
The <multiset> `pool` contains exactly the unused secret tokens after all bulls have been set aside. The recursive <invariant> is that `available` contains those tokens not yet allocated to cows. A successful `pluck` consumes one, and failure leaves the pool unchanged. This proves both multiplicity correctness and the priority of bulls over cows. Among duplicate non-bull guess tokens, the leftmost eligible ones receive cows. For secret `"alpha"` and guess `"aaabb"`, the result is \b[`[Bull,Cow,Miss,Miss,Miss]`]. This implements <duplicate-aware positional scoring>.
Back to article page