Solution (source code)

= Solution

The score function takes a proposed secret first and a guess second:
``
mkGuesser :: Eq score
          => (guess -> guess -> score)
          -> ([guess] -> guess)
          -> [guess] -> [score] -> [guess]
mkGuesser score strategy dictionary = play dictionary
  where
    play candidates replies =
      let g = strategy candidates
      in g : case replies of
          [] -> []
          r:rs -> play [s | s <- candidates, score s g == r] rs
``
Assume that the initial dictionary contains the secret, that scores are truthful, and that `strategy` returns a candidate from its nonempty input. The candidate <invariant> is exactly the dictionary entries consistent with every score already received. Filtering by `score s g == r` establishes the same invariant after the next reply and retains the true secret.

Crucially, the next guess `g` is placed under the outer list constructor before `replies` is inspected. It depends on earlier replies only; the first guess depends on none. This guarded <lazy feedback stream> makes the mutually recursive `game` productive whenever the strategy and scoring operations terminate. Moving the `case replies` outside the constructor would create a circular demand before the first guess.