Solution (source code)

= 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>.