⚠️ Warning: This is a draft ⚠️

This means it might contain formatting issues, incorrect code, conceptual problems, or other severe issues.

If you want to help to improve and eventually enable this page, please fork RosettaGit's repository and open a merge request on GitHub.

{{trans|Ruby}} {{collection|Go Fish}}

constant show_non_human_hands = false

sequence players

enum NAME, HAND, BOOKS, KNOWN, SINCE, PLAYER=$

function new_player(string name)
    if name="" then
        name = prompt_string("What is your name?:")
--      puts(1,"What is your name?:pete\n")
--      name = "pete"
--      name = "computer"   -- plays itself
        if name!="" then
            name = lower(name)
            name[1] = upper(name[1])
        else
            name = "Computer"
        end if
    end if
    sequence player = repeat(0,PLAYER)
    player[NAME] = name
    player[HAND] = {}
    player[BOOKS] = ""
    player[KNOWN] = repeat(' ',13)
    player[SINCE] = repeat('2',13) -- (cards picked up since you last asked)
    -- aside: arguably, SINCE should be "999..99", but it would not make any
    --        difference, however 9 messes up debug info almost immediately,
    --        whereas 2s remain visually meaningful for 7 or so more rounds.
    return player
end function

constant ranks = "123456789EJQX",   -- ("1EX"->"ATK" in output)
         fish  = "A23456789TJQK",   -- (input version of above)
         suits = "CDHS",
         cards = {"Ace","Two","Three","Four","Five","Six","Seven",
                  "Eight","Nine","Ten","Jack","Queen","King"}

function map_card(integer card)
-- (input -> internal)
    integer k = find(card,"ATK")
    if k then card = "1EX"[k] end if
    return card
end function

function unmap_cards(string cards)
-- (internal -> display)
    return substitute_all(cards,"1EX","ATK")
end function

procedure show_cards(sequence cards)
    printf(1,"%s\n",{unmap_cards(join(cards))})
end procedure

sequence deck

function deal()
    string res = deck[$]
    deck = deck[1..-2]
    return res
end function

procedure check_empty_hand(integer player)
    if players[player][HAND]={}
    and deck!={} then
        sequence one = {deal()}
        string name = players[player][NAME]
        if show_non_human_hands
        or name!="Computer" then
            printf(1,"%s's hand is empty. Picks up the ",{name})
            show_cards(one)
        end if
        players[player][HAND] = one
    end if
end procedure

procedure check_for_books(integer player)
    sequence hand = sort(players[player][HAND])
    string books = players[player][BOOKS]
    integer i = length(hand)-3
    while i>=1 do
        if hand[i][1]=hand[i+3][1] then
            books &= hand[i][1]
            hand[i..i+3] = {}
            i -= 4
        else
            i -= 1
        end if
    end while
    players[player][HAND] = hand
    players[player][BOOKS] = books
end procedure

procedure take_cards(integer player, sequence cards, bool bDeal=true)
    players[player][HAND] &= cards
    check_for_books(player)
    if bDeal then
        check_empty_hand(player)
    end if
end procedure

function has_card(integer player, rank)
    sequence hand = players[player][HAND]
    for i=1 to length(hand) do
        if hand[i][1]=rank then return true end if
    end for
    return false
end function

function hand_over(integer player, rank)
    sequence cards = players[player][HAND]
    integer l = length(cards)
    for i=1 to l do
        if cards[i][1]=rank then
            for j=i+1 to l+1 do
                if j=l+1 or cards[j][1]!=rank then
                    cards = cards[i..j-1]
                    players[player][HAND][i..j-1] = {}
                    printf(1,"%s hands over ",{players[player][NAME]})
                    show_cards(cards)
                    check_empty_hand(player)
                    return cards
                end if
            end for
        end if
    end for
    return 9/0  -- should never trigger
end function

constant ESC=#1B

function wanted_card(integer player)
integer wanted,
        opponent = 3-player
string name = players[player][NAME],
       books = players[player][BOOKS],
       known = players[player][KNOWN],
       since = players[player][SINCE]
sequence hand = players[player][HAND]

    if show_non_human_hands
    or name!="Computer" then
        printf(1,"%s's hand: ",{name})
        show_cards(hand)
--      printf(1,"  books: %s\n",{unmap_cards(books)})
        printf(1,"  opponent is known to have: %s\n",{unmap_cards(known)})
        -- aside: this is not very clear: should probably space out any
        --  entries for known!=' ', and do better than ..789:;<=>?@..
        printf(1,"         and not have since: %s\n",{since})
    end if

    if name="Computer" then
        -- strategy: if known then take, 
        -- otherwise pick largest since
        -- (or try something else here)
        integer hs = 0, hw -- highest; hs==since[hw]
        for i=1 to length(hand) do
            integer wdx = find(hand[i][1],ranks)
            wanted = known[wdx]
            if wanted!=' ' then exit end if
            integer sw = since[wdx]
            if sw>hs then {hs,hw} = {sw,wdx} end if
        end for
        if wanted=' ' then wanted = ranks[hw] end if
    else
        printf(1,"\n")
        while 1 do
            printf(1,"What rank to ask for?:")
            while 1 do
                wanted = wait_key()
                if wanted=ESC then
                    abort(0)
                elsif wanted<#FF then -- ignore ctrl/shift etc
                    wanted = upper(wanted)
                    printf(1,"%c",wanted)
                    exit
                end if
            end while
            if not find(wanted,fish) then
                printf(1," not a valid rank -- try again.\n")
            else
                wanted = map_card(wanted)
                if has_card(player,wanted) then exit end if
                printf(1," you don't have any of those -- try again\n")
            end if
        end while
        printf(1,"\n")
    end if
    return wanted
end function

function query(integer player)
    integer wanted = wanted_card(player),
            wdx = find(wanted,ranks),
            opponent = 3-player
    string name = players[player][NAME],
           card = substitute(cards[wdx],"x","xe") -- (Sixs -> Sixes)

    -- we now (or soon will) know opponent no longer has that card
    players[player][SINCE][wdx] = '0'
    players[player][KNOWN][wdx] = ' '

    -- opponent now knows we have one or more of that card:
    players[opponent][KNOWN][wdx] = wanted
    players[opponent][SINCE][wdx] = '0'

    printf(1,"%s: Do you have any %ss?\n",{name,card})
    if not has_card(opponent,wanted) then
        printf(1,"%s: Go fish!\n",{players[opponent][NAME]})
        players[player][SINCE] = sq_add(players[player][SINCE],1)
        if deck!={} then
            sequence one = {deal()}
            if show_non_human_hands
            or name!="Computer" then
                printf(1,"%s picks up ",{name})
                show_cards(one)
            end if
            take_cards(player,one)
        end if
        return false
    end if
    sequence cards = hand_over(opponent,wanted)
    take_cards(player,cards)
    return true -- another go
end function

function gameover()
    return length(players[1][BOOKS])+length(players[2][BOOKS])=13
end function

procedure GoFishGame()
    deck = {}
    for rank=1 to length(ranks) do
        for suit=1 to length(suits) do
            string card = ""&ranks[rank]&suits[suit]
            deck = append(deck,card)
        end for
    end for
    deck = shuffle(deck)
    players = {new_player("Computer"),
               new_player("")}
    for i=1 to 9 do
        for p=1 to 2 do
            take_cards(p,{deal()},false)
        end for
    end for
    integer player = rand(2)
    while not gameover() do
        if not query(player) then
            player = 3-player
            puts(1,"--------------------------------------\n")
        end if
    end while
    puts(1,"Game over\n")
    for p=1 to 2 do
        integer l = length(players[p][BOOKS])
        string name = players[p][NAME],
                s = iff(l=1?"":"s")
        printf(1,"%s has %d book%s\n",{name,l,s})
    end for
end procedure
GoFishGame()