Task

Write a player of the Bulls and Cows game, rather than a scorer. The player should give intermediate answers that respect the scores to previous attempts.

One method is to generate a list of all possible numbers that could be the answer, then to prune the list by keeping only those numbers that would give an equivalent score to how your last guess was scored. Your next guess can be any number from the pruned list.

Either you guess correctly or run out of numbers to guess, which indicates a problem with the scoring.



Related tasks

   Bulls and cows
   Guess the number
   Guess the number/With Feedback (Player)


with javascript_semantics
constant line = " +---------+-----------------------------+-------+------+\n",
         digits = "123456789"
 
function mask(integer ch)
    return power(2,ch-'1')
end function
 
function score(string guess, goal)
    integer bits = 0, bulls = 0, cows = 0
    for i=1 to length(guess) do
        if guess[i]=goal[i] then
            bulls += 1
        else
            bits += mask(goal[i])
        end if
    end for
    for i=1 to length(guess) do
        cows += (and_bits(bits,mask(guess[i]))!=0)
    end for
    return {bulls, cows}
end function
 
sequence list = {}
 
procedure pick(integer n, got, marker, string buf)
    if got>=n then
        list = append(list,buf)
    else
        integer bits = 1
        for i=0 to length(digits)-1 do
            if not and_bits(marker,bits) then
                buf[got+1] = i+'1'
                pick(n, got+1, or_bits(marker,bits), deep_copy(buf))
            end if
            bits *= 2
        end for
    end if
end procedure
 
procedure filter_list(string guess, integer bulls, cows)
    integer idx = 0
    sequence bc = {bulls,cows}
    for i=1 to length(list) do
        if score(guess,list[i])=bc then
            idx += 1
            list[idx] = list[i]
        end if
    end for
    list = list[1..idx]
end procedure
 
procedure game(string tgt)
    integer n = length(tgt), attempt = 1
    pick(n,0,0,repeat('\0',n))
    while true do
        string guess = list[rand(length(list))]
        integer {bulls, cows} = score(guess,tgt)
        printf(1," | Guess %-2d| %9s    %-14s |   %d   |   %d  |\n",
                    {attempt, guess, sprintf("(from %d)",length(list)), bulls, cows})
        if bulls=n then exit end if
        filter_list(guess, bulls, cows)
        attempt += 1
    end while
    puts(1,line)
end procedure
 
constant N = 4
string secret = shuffle(digits)[1..N]
printf(1,"%s | Secret  | %9s                   | BULLS | COWS |\n%s", {line, secret, line})
game(secret)
