⚠️ 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.

import Base.show

const sorteddeck = reshape([string(r == 'T' ? "10" : r) * s for r in "23456789TJQKA", s in "♣♦♥♠"], 52)

isvaliddeck(a) = (s = Vector{UInt8}(unique(a)); (a == s && all(x -> 0 < x <= 52, s)))

mutable struct Deck
    cards::Vector{UInt8}
    Deck(a) = if isvaliddeck(a) new(a) else throw("bad deck constructor $a") end
end

suitrank(crd) = divrem((crd - 1), 13) .+ 1
cardsuit(crd) = suitrank(crd)[1]
cardrank(crd) = suitrank(crd)[2]
shuffledeck!(d) = shuffle!(d.cards)

ascards(d::Deck) = [sorteddeck[i] for i in d.cards]
Base.show(io::IO, d::Deck) = show(io, join(ascards(d), " "))

byrank(d::Deck) = filter(!isempty, [filter(x -> x in n:13:52, d.cards) for n in 1:13])
printbyrank(d) = foreach(x -> println(map(y -> sorteddeck[y], x)), byrank(d))

function deal!(d::Deck, hlen)
    if hlen < length(d.cards)
        hand = Deck(d.cards[1:hlen])
        d.cards = d.cards[hlen+1:end]
    else
        hand = Deck(d.cards)
        empty!(d.cards)
    end
    return hand
end

function queryprompt(query, choices, choicetxt="")
    carr = map(x -> uppercase(strip(string(x))), collect(choices))
    while true
        print(query, " ", choicetxt == "" ? carr : choicetxt, ": ")
        choice = uppercase(strip(readline(stdin)))
        if choice in carr
            return choice
        end
        println()
    end
end

function gofish()
    ranks = ["2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"]
    displayplayer(d, l, cl) = (println("Player hand is"), printbyrank(d),
        println(".\nYour score is $(length(l)) books. Computer score is $(length(cl)) books."))
    discardfrom!(a, d) = (dis = Deck(a); d.cards = filter(x -> !(x in a), d.cards); dis)
    books!(d, l) = (b = [discardfrom!(a, d) for a in byrank(d) if length(a) == 4]; append!(l, b); b)

    function cardswithrank(rankstring, d)
        r = indexin([rankstring], ranks)[1]
        if r == nothing
            return []::Vector{Int}
        else
            arr = findall(x -> cardrank(x) == r, d.cards)
        end
        d.cards[arr]
    end

    function moverank!(rnk, srcdeck, dstdeck)
        arr = cardswithrank(rnk, srcdeck)
        srcdeck.cards = sort(filter(x -> !(x in arr), srcdeck.cards))
        dstdeck.cards = sort(unique(append!(dstdeck.cards, arr)))
    end

    function drawacard!(d, maindeck, display=true)
        if isempty(maindeck.cards)
            println("Draw pile is empty.")
            return
        end
        crd = popfirst!(maindeck.cards)
        d.cards = sort(push!(d.cards, crd))
        if display
            println("You drew a ", Deck([crd]))
        end
    end

    function playerasks!(d, plaid, claid, cdeck, maindeck)
        choice = ""
        while true
            if isempty(cdeck.cards)
                drawacard!(cdeck, maindeck, false)
            end
            if isempty(d.cards)
                drawacard!(d, maindeck)
            end
            if isempty(d.cards)
                return
            end
            displayplayer(d, plaid, claid)
            choice = queryprompt("Your turn. Rank you request? ", ranks)
            if isempty(cardswithrank(choice, d))
                println("You cannot ask for \"$choice\" cards since you have none.")
                continue
            end
            println("You want the \"$choice\" cards.")
            if (n = length(cardswithrank(choice, cdeck))) > 0
                moverank!(choice, cdeck, d)
                println("Computer gives you $n of the \"$choice\" cards.")
                choice = queryprompt("Ask computer for cards again? ", ["Y", "N"])
                if choice == "N"
                    break
                end
            else
                println("Sorry, computer does not have any of those cards.")
                break
            end
        end
    end

    function playerdiscards!(pd, plaid)
        choice = queryprompt("Discard cards? ", ["Y","N"])
        if choice == "Y"
            bks = books!(pd, plaid)
            println(length(bks) > 0 ? "Books: $(join(map(ascards, bks), ", ")))" : "No new books.")
        end
    end

    function computerasks!(cdeck, claid, pdeck, maindeck, memory)
        choice = ""
        while true
            if isempty(pdeck.cards)
                drawacard!(pdeck, maindeck)
            end
            if isempty(cdeck.cards)
                drawacard!(pdeck, maindeck, false)
            end
            if isempty(cdeck.cards)
                return
            end
            rankpick = 0
            while true
                r = rand(cdeck.cards)
                rankpick = cardrank(r)
                if haskey(memory, rankpick) && memory[rankpick] > 0
                    memory[rankpick] -= 1
                    continue
                else
                    memory[rankpick] = 3
                    break
                end
            end
            choice = ranks[rankpick]
            println("Computer asks you for \"$choice\" cards.")
            if (n = length(cardswithrank(choice, pdeck))) > 0
                moverank!(choice, pdeck, cdeck)
                println("Computer accepts $n \"$choice\" cards from you.")
            else
                println("Computer cannot get those cards from you.")
                return
            end
        end
    end

    function computerdiscards!(cdec, claid)
        bks = books!(cdec, claid)
        if length(bks) > 0
            println("Computer new books: ", join(map(ascards, bks), ", "))
        end
    end

    fishd = Deck(shuffle(collect(1:52)))
    println("------------GO FISH GAME------------\n\nDealing:")
    pdeck = deal!(fishd, 9)
    cdeck = deal!(fishd, 9)
    println("You are dealt $pdeck.")

    plaid = Vector{Deck}()
    claid = Vector{Deck}()
    memory= Dict{Int,Int}()

    while sum(length, [pdeck.cards, cdeck.cards, fishd.cards]) > 0
        playerasks!(pdeck, plaid, claid, cdeck, fishd)
        playerdiscards!(pdeck, plaid)
        drawacard!(pdeck, fishd)

        computerasks!(cdeck, claid, pdeck, fishd, memory)
        computerdiscards!(cdeck, claid)
        drawacard!(cdeck, fishd, false)
    end

    pscore = length(plaid)
    cscore = length(claid)
    println("Player has taken out $pscore books, and computer has taken out $cscore books.")
    println(pscore > cscore ? "Player wins." : pscore < cscore ? "Computer wins." : "Tied game.")
end

gofish()