Go Fish: Difference between revisions

From Rosetta Code
Content added Content deleted
(→‎{{header|Haskell}}: Removed redundancy in AI. Added AI description.)
(61 intermediate revisions by 36 users not shown)
Line 3: Line 3:


* Each player is dealt nine cards to start with.
* Each player is dealt nine cards to start with.
* On their turn, a player asks their opponent for a given rank (like threes or kings). A player must already have at least one card of a given rank to ask for more.
* On their turn, a player asks their opponent for a given rank (such as threes or kings). A player must already have at least one card of a given rank to ask for more.
** If the opponent has any cards of the named rank, they must hand over all such cards, and the requester can ask again.
** If the opponent has any cards of the named rank, they must hand over all such cards, and the requester can ask again.
** If the opponent has no cards of the named rank, the requester draws a card and ends their turn.
** If the opponent has no cards of the named rank, the requester draws a card and ends their turn.
* A ''book'' is a collection of every card of a given rank. Whenever a player completes a book, they may remove it from their hand.
* A ''book'' is a collection of every card of a given rank. Whenever a player completes a book, they may remove it from their hand.
* If at any time a player's hand is empty, they may immediately draw a new card, so long as any new cards remain in the deck.
* If at any time a player's hand is empty, they may immediately draw a new card, so long as any new cards remain in the deck.
* The game ends when every book is complete. The player with more books wins.
* The game ends when every book is complete. The player with the most books wins.


The game's AI need not be terribly smart, but it should use at least some strategy. That is, it shouldn't choose legal moves entirely at random.
The game's AI need not be terribly smart, but it should use at least some strategy. That is, it shouldn't choose legal moves entirely at random.
Line 14: Line 14:
You may want to use code from [[Playing Cards]].
You may want to use code from [[Playing Cards]].


Related tasks:
=={{header|Haskell}}==
* [[Playing cards]]
* [[Card shuffles]]
* [[Deal cards_for_FreeCell]]
* [[War Card_Game]]
* [[Poker hand_analyser]]


If possible, the AI will randomly select a rank known to be in the human's hand (a card in the AI's hand that the human has asked for before and the AI hasn't asked for before). If there are no known ranks, a rank is randomly selected from the AI's hand.


=={{header|Aime}}==
<lang haskell>
See [[Go Fish/Aime]]
import Char
import Data.Function
import IO
import Data.Map (Map, (!))
import qualified Data.Map as M
import Data.Set (Set)
import qualified Data.Set as S
import Data.List
import Maybe
import Monad
import Random


=={{header|AutoHotkey}}==
data Player = Player String (GameState -> IO Rank)
See [[Go Fish/AutoHotkey]]


=={{header|C}}==
type PlayerState = (Hand, Set Rank, Set Rank)
See [[Go Fish/C]]


=={{header|C++}}==
data GameState = GS Deck PlayerState PlayerState
See [[Go Fish/C++]]


=={{header|D}}==
type Card = (Suit, Rank)
See [[Go Fish/D]]


=={{header|Erlang}}==
type Deck = [Card]
See [[Go Fish/Erlang]]


=={{header|FreeBASIC}}==
type Hand = Map Rank (Set Suit)
<syntaxhighlight lang="freebasic">
' Go Fish ~ ¡Pesca!


Const cartas = "A234567890JQK"
data Suit = Diamond | Club | Heart | Spade
deriving (Bounded, Enum, Eq, Ord, Show)


Declare Sub Reparto_Cartas
data Rank = Two | Three | Four | Five | Six | Seven | Eight | Nine | Ten |
Declare Sub Pescar_Carta_Jug
Jack | Queen | King | Ace
Declare Sub Pescar_Carta_CPU
deriving (Bounded, Enum, Eq, Ord, Show)
Declare Sub Comprobar_Libro_Jug
Declare Sub Comprobar_Libro_CPU
Declare Sub Comprobar_Fin_Partida
Declare Sub Intro


Dim Shared As Integer play(13), compu(13), deck(13), guess(13), poss(13), asked(13)
main =
Dim Shared As String nombre, Snombre, CartaPedida
hSetBuffering stdout NoBuffering >>
Dim Shared puntos(2) As Byte = {0,0}
putStrLn "GO FISH\n\nDealing Cards" >>
Dim Shared As Integer remca = 4*Len(cartas)
initialGameState >>=
Dim Shared As Integer i, k, j, cn
play (cycle [player, computer])
For i = 1 To 13
deck(i) = 4
Next i
For i = 1 To 9
Reparto_Cartas
deck(k) -= 1
compu(k) += 1
Reparto_Cartas
deck(k) -= 1
play(k) += 1
Next i
Dim As Integer v, po


Sub Reparto_Cartas
play ((Player name next):ps) state =
remca -= 1
putStrLn ('\n' : name ++ "'S TURN") >>
Dim As Integer sc = remca * Rnd + 1
executeCall next state >>= \nextState ->
(if isDone nextState
For k = 1 To 13
sc -= deck(k)
then let (winnerName, ws, ls) = scoreGame name (head ps) nextState
If sc <= 0 Then Return
in putStrLn (winnerName ++ " WINS " ++ show ws ++ " TO " ++ show ls)
Next k
else play ps $ swapPlayers nextState
End Sub
)


Sub Pescar_Carta_Jug
scoreGame name opponent state@(GS _ (_,books,_) (_,oppBooks,_)) =
Reparto_Cartas
if score > oppScore
Print " " &Mid(cartas,k,1) &"."
then (name, score, oppScore)
deck(k) -= 1
else ((\(Player opp _) -> opp) $ opponent, oppScore, score)
play(k) += 1
where (score, oppScore) = (\(GS _ (_,books,_) (_,oppBooks,_)) ->
End Sub
(S.size books, S.size oppBooks)) state


Sub Pescar_Carta_CPU
player = Player "PLAYER" playerBrain
Reparto_Cartas
playerBrain (GS _ (hand,_,_) _) =
putStr "Your cards: " >>
Print "a carta."
deck(k) -= 1
putStrLn (intercalate " " . map showCard $ handToCards hand) >>
compu(k) += 1
untilSuccess (liftM readRank rankPrompt) (putStrLn "Bad rank")
End Sub
where rankPrompt = putStr "Ask opponent for what rank? " >> getLine


Sub Comprobar_Libro_Jug
computer = Player "COMPUTER" computerBrain
For i = 1 To 13
computerBrain (GS deck (hand,_,history) (_, oppBooks, oppHistory)) =
If play(i) <> 4 Then
liftM selectRank newStdGen >>= \rank ->
Else
putStrLn ("Do you have any " ++ show rank ++ "s?") >>
Color 11: Print Snombre &" completa el libro de " &Mid(cartas,i,1) &"'s.": Color 7
return rank
play(i) = 0
where knowns = S.difference (S.intersection guesses oppHistory) history
guesses = M.keysSet hand
puntos(0) += 1
End If
ranks = S.toList $ if S.null knowns then guesses else knowns
Next i
selectRank = (ranks !!) . fst . randomR (0, length ranks - 1)
End Sub


Sub Comprobar_Libro_CPU
executeCall askRank state@(GS deck (hand, b, hi) o) =
if M.null hand
For i = 1 To 13
If compu(i) <> 4 Then
then if null deck
then return $! state
Else
Color 11: Print Snombre &" completa el libro de " &Mid(cartas,i,1) &"'s.": Color 7
else putStrLn "Empty hand, forced draw" >>
let (newHand, newDeck) = draw hand deck
compu(i) = 0
in normalizeBooks $ GS newDeck (newHand, b, hi) o
puntos(1) += 1
End If
else getValidRank askRank state >>= \rank ->
Next i
exchangeCards state rank >>= \(newState, done) ->
End Sub
normalizeBooks newState >>=
(if done then return else executeCall askRank)


Sub Comprobar_Fin_Partida
exchangeCards (GS deck (hand, b, hist) (opponentHand, ob, ohi)) rank =
Dim As Integer np = 0, nc = 0
putStrLn m >> return (GS nd (nh, b, nhi) (noh, ob, ohi), d)
For i = 1 To 13
where (m, nh, noh, nd, d) = worker $ M.lookup rank opponentHand
nhi = S.insert rank hist
np += play(i)
nc += compu(i)
worker Nothing = ("Go fish", newHand, opponentHand, newDeck, True)
Next i
where (newHand, newDeck) = draw hand deck
worker (Just suits) = (message, newHand, newOppHand, deck, False)
If remca = 0 Or np = 0 Or nc = 0 Then
Color 15: Print
where message = show (S.size suits) ++ " " ++ show rank ++ "(s)"
Print "*** ­FIN de la partida! ***"
newHand = M.adjust (S.union suits) rank hand
Print
newOppHand = M.delete rank opponentHand
If puntos(0) < puntos(1) Then
Print "La CPU ha ganado."
Elseif puntos(0) > puntos(1) Then
Print nombre &" ha ganado."
Else
Print "­Es un empate!"
End If
Sleep: End
End If
End Sub


Sub Intro
getValidRank askRank state@(GS _ (hand,_,_) _) = untilSuccess
Color 15
(liftM (check hand) $ askRank state) $ putStrLn "Rank not in hand"
where check m v = M.lookup v m >>= Just . const v
Print " __ _ _ "
Print " __ _ ___ / _(_)___| |__ "
Print " / ` |/ _ \ | |_| / __| '_ \ "
Print "| (_) | (_) | | _| \__ \ | | | "
Print " \__, |\___/ |_| |_|___/_| |_| "
Print " |___/ "
Print " "
Color 14: Locate 10, 2: Input "Como te llamas: ", nombre
End Sub


'--- Programa Principal ---
normalizeBooks (GS d (hand, books, hi) o) =
Cls
mapM_ printRank (M.keys newBookRanks) >>
Randomize Timer
(return $! GS d (newHand, newBooks, hi) o)
Intro
where (newBookRanks, newHand) = M.partition ((==4) . S.size) hand
Do
newBooks = S.union books $ M.keysSet newBookRanks
Dim As boolean MuestraMano = false
printRank r = putStrLn ("Rank " ++ show r ++ " was booked")
While MuestraMano = false
Color 15: Print Chr(10) &"Puntos >> " &nombre &": "; puntos(0); " CPU: "; puntos(1)
Color 13: Print Chr(10) &space(10) &remca &" cartas restantes"
Color 14: Print Chr(10) &"Tu mano: ";
For i = 1 To 13
If Not play(i) Then
For j = 1 To play(i)
Print Mid(cartas,i,1); " ";
Next j
End If
Next i
Print
Dim As boolean PideCarta = false
While PideCarta = false
Comprobar_Fin_Partida
Snombre = nombre
Color 7: Print
Input "¨Que carta pides... "; CartaPedida
Print
If CartaPedida <> "" Then cn = Instr(cartas, Ucase(CartaPedida)): PideCarta = true
If cn = 0 Then
Print "Lo siento, no es una opción valida.": PideCarta = false
Elseif play(cn) = 0 Then Color 12: Print "­No tienes esa carta!": Color 7: PideCarta = false
End If
Wend
guess(cn) = 1
If compu(cn) = 0 Then
Print Snombre &", ";
Color 15: Print "­ve a pescar!"
Color 7: Print Snombre &" pesca un";: Pescar_Carta_Jug
Comprobar_Libro_Jug
MuestraMano = true
Else
v = compu(cn)
compu(cn) = 0
play(cn) += v
Print Snombre &" consigue " &v &" carta(s) mas."
Comprobar_Libro_Jug
MuestraMano = false
End If
Wend


Snombre = "CPU"
swapPlayers (GS deck player1 player2) = GS deck player2 player1
For i = 1 To 13
asked(i) = 0
Next i
Dim As boolean Turno_CPU_2 = false
While Turno_CPU_2 = false
Comprobar_Fin_Partida
po = 0
For i = 1 To 13
If (compu(i) > 0) And (guess(i) > 0) Then poss(i) = 1: po += 1
Next i
If po = 0 Then
Do
k = (Rnd*12)+1
Loop While compu(k) = 0 Or asked(k)
Else
Do
k = (Rnd*12)+1
Loop While poss(k) = 0
guess(k) = 0
asked(k) = 1
End If


Print: Print Snombre &" quiere tus " &Mid(cartas,k,1) &"'s."
isDone (GS deck (hand,_,_) (oppHand,_,_)) =
and [M.null hand, M.null oppHand, null deck]
asked(k) = 1
If play(k) = 0 Then
Print Snombre &", ";
Color 15: Print "­ve a pescar!"
Color 7:Print Snombre &" pesca un";: Pescar_Carta_CPU
Comprobar_Libro_CPU
Turno_CPU_2 = true
Else
v = play(k)
play(k) = 0
compu(k) += v
Print Snombre &" consigue " &v &" carta(s) mas."
Comprobar_Libro_CPU
Turno_CPU_2 = false
End If
Wend
Loop
End
</syntaxhighlight>


=={{header|FutureBasic}}==
initialGameState = liftM worker newStdGen
Translated from FreeBasic
where worker gen =
May 5, 2024 Rich Love
(GS deck (hand1, S.empty, S.empty) (hand2, S.empty, S.empty))
Fixed intermittent hang
where (startDeck, _) = shuffle initialDeck gen
Also, now using && and || to be compatible with later versions of FutureBasic.
(hand1, deckMinusPlayerHand) = drawN 9 M.empty startDeck
( replaces AND and OR )
(hand2, deck) = drawN 9 M.empty deckMinusPlayerHand
<syntaxhighlight lang="futurebasic">


/*
untilSuccess action onFailure = worker
where worker = action >>= \result -> case result of
Nothing -> onFailure >> worker
Just value -> return value


Go Fish
readRank [x] | isDigit x && x > '1' = Just $ toEnum (fromEnum x - 50)
-- Rich Love --
readRank x@[_,_] | x == "10" = Just Ten
readRank [x] = case toLower x of
'j' -> Just Jack
'q' -> Just Queen
'k' -> Just King
'a' -> Just Ace
_ -> Nothing
readRank _ = Nothing


FutureBasic app For Macintosh
showCard (suit, rank) = r ++ [head $ show suit]
Get the latest FutureBasic here
where r = if rank > Ten
http://www.brilorsoftware.com/fb/pages/home.html
then [head $ show rank]
else show (fromEnum rank + 2)


*/
initialDeck = liftM2 (,) [Diamond .. Spade] [Two .. Ace]


shuffle deck gen = worker gen (length deck) [] deck
where worker g l xs [] = (xs, g)
worker g l xs ys = worker newGen (l-1) (card : xs) (delete card ys)
where (index, newGen) = randomR (0,l-1) g
card = ys !! index


_window = 1
draw hand ((s,r):deck) = (M.insertWith S.union r (S.singleton s) hand, deck)
begin enum 1
_scrollView
_textView
end enum


override _forLoopsAlwaysExecuteAtLeastOnce = _true
drawN n hand deck = iterate (uncurry draw) (hand, deck) !! (n-1)


begin globals
handToCards = concatMap (\(r,ss) -> map (flip (,) r) $ S.toList ss) . M.assocs
</lang>


str255 cards
=={{header|OCaml}}==
cards = "A234567890JQK"
short play(13), Computer(13), deck(13), guess(13), poss(13), asked(13)
str255 YourName, Someone
//bool gNeedToClearScreen
short Points(2) : Points(0) = 0 : Points(1) = 0
short i, k, j, CardNumber
short RemainingCards


end globals
<lang ocaml>type pip = Two | Three | Four | Five | Six | Seven | Eight | Nine | Ten |
Jack | Queen | King | Ace
let pips = [Two; Three; Four; Five; Six; Seven; Eight; Nine; Ten;
Jack; Queen; King; Ace]
type suit = Diamonds | Spades | Hearts | Clubs
let suits = [Diamonds; Spades; Hearts; Clubs]
type card = pip * suit


let string_of_pip = function
| Two -> "Two"
| Three -> "Three"
| Four -> "Four"
| Five -> "Five"
| Six -> "Six"
| Seven -> "Seven"
| Eight -> "Eight"
| Nine -> "Nine"
| Ten -> "Ten"
| Jack -> "Jack"
| Queen -> "Queen"
| King -> "King"
| Ace -> "Ace"
let string_of_suit = function
| Diamonds -> "Diamonds"
| Spades -> "Spades"
| Hearts -> "Hearts"
| Clubs -> "Clubs"


local fn CheckForFaceCard(TheCard as short) as str255
let string_of_card (pip, suit) =
str255 WantsCard
(Printf.sprintf "(%s-%s)" (string_of_pip pip) (string_of_suit suit))
WantsCard = str$(TheCard)
if TheCard = 0 then WantsCard = "10"
if TheCard = 11 then WantsCard = "jack"
if TheCard = 12 then WantsCard = "Queen"
if TheCard = 13 then WantsCard = "King"
if TheCard = 1 then WantsCard = "Ace"
end fn = WantsCard


let pip_of_card (pip, _) = (pip)


void local fn PrintViewScrollToBottom( printView as ViewRef )
let deck = List.concat (List.map (fun pip -> List.map (fun suit -> (pip, suit)) suits) pips)
BeginCCode
NSScrollView *scrollView = [printView enclosingScrollView];
NSClipView *clipView = [scrollView contentView];
[clipView scrollToPoint:NSMakePoint(0,printView.frame.size.height-scrollView.contentSize.height + 20)];
[scrollView reflectScrolledClipView:clipView];
EndC
end fn




void local fn DealCards
type rank_state =
| Unknown (* Don't know if the opponent has any cards in that rank. *)
RemainingCards -= 1
| No_cards (* Opponent has no cards there; I took them away, or I asked yet. *)
short sc
| Has_cards (* Opponent has cards there; they tried to get them off me and haven't booked them yet. *)
sc = rnd(RemainingCards) + 1 // 5/2/24 Rich added + 1
| Booked (* Someone has booked the rank. *)
For k = 1 To 13
sc -= deck(k)
If sc <= 0 Then exit fn
Next k
End fn


void local fn youGoFishing
let state_score = function
| Booked -> 0
fn DealCards
| No_cards -> 1
| Unknown -> 2
str255 WantsCard
| Has_cards -> 3
WantsCard = fn CheckForFaceCard(k)
if WantsCard = "0" then WantsCard = "10"
Print " " + WantsCard + "."
deck(k) -= 1
play(k) += 1
End fn


let string_of_state = function
| Booked -> "Booked"
| No_cards -> "No_cards"
| Unknown -> "Unknown"
| Has_cards -> "Has_cards"


void local fn cpuGoFishing
let replace ((rank,_) as state) opp =
let rec aux acc = function
fn DealCards
| (_rank,_)::tl when _rank = rank -> List.rev_append acc (state::tl)
Print " a card from the deck."
| hd::tl -> aux (hd::acc) tl
| [] -> assert(false)
if k > 13 then k = 13
deck(k) -= 1
in
Computer(k) += 1
aux [] opp ;;
End fn




void local fn CheckForCompletedBook
class virtual abstract_player =
object (s)
For i = 1 To 13
val mutable virtual cards : card list
If play(i) <> 4
val mutable virtual books : pip list
Else
method virtual ask_rank : unit -> pip
text ,,fn colorcyan
method virtual give_rank : pip -> card list
method virtual notify_booked : pip -> unit
str255 WantsCard
method virtual request_failed : pip -> unit
WantsCard = Mid$(cards,i,1)
if WantsCard = "j" || WantsCard = "J" then WantsCard = "Jack"
if WantsCard = "q" || WantsCard = "Q" then WantsCard = "Queen"
if WantsCard = "k" || WantsCard = "K" then WantsCard = "King"
if WantsCard = "a" || WantsCard = "A" then WantsCard = "Ace"
if WantsCard = "0" then WantsCard = "10"
Print YourName + " completed the book of " + WantsCard + "'s."
text ,,fn colorWhite
play(i) = 0
Points(0) += 1
fn PrintViewScrollToBottom( fn WindowPrintView(1))
End If
Next i
End fn


local fn CheckCPUForCompletedBook
method private cards_given rank =
let matched, rest = List.partition (fun (pip,_) -> pip = rank) cards in
For i = 1 To 13
if List.length matched = 4 then begin
cards <- rest;
books <- rank :: books;
If Computer(i) <> 4
Else
s#notify_booked rank;
(Some rank)
text ,,fn colorCyan
end
str255 WantsCard
else (None)
WantsCard = Mid$(cards,i,1)
if WantsCard = "j" || WantsCard = "J" then WantsCard = "Jack"
if WantsCard = "q" || WantsCard = "Q" then WantsCard = "Queen"
if WantsCard = "k" || WantsCard = "K" then WantsCard = "King"
if WantsCard = "a" || WantsCard = "A" then WantsCard = "Ace"
if WantsCard = "0" then WantsCard = "10"
Print "CPU completed the book of " + WantsCard + "'s."
text ,,fn colorWhite
Computer(i) = 0
Points(1) += 1
fn PrintViewScrollToBottom( fn WindowPrintView(1))
End If
Next i
End fn


method give_card (card : card) =
let rank = pip_of_card card in
cards <- card :: cards;
s#cards_given rank


local fn InitCards
method give_cards (_cards : card list) =
let rank =
cards = "A234567890JQK"
match _cards with
RemainingCards = 4 * len$(cards) // the length of cards is 13. There are 4 suits of cards. so RemainingCards is 52
| [] -> invalid_arg "empty list"
| hd::tl ->
i = 0:k = 0:j = 0:CardNumber = 0
List.fold_left
For i = 0 to 1
(fun rank1 (rank2,_) ->
Points(i) = 0
if rank1 <> rank2
next i
then invalid_arg "!= ranks"
else (rank1)
For i = 1 TO 13 // Reset each element to 0
) (pip_of_card hd) tl
in
play(i) = 0
Computer(i) = 0
cards <- _cards @ cards;
deck(i) = 0
s#cards_given rank
guess(i) = 0
poss(i) = 0
asked(i) = 0
NEXT i
For i = 1 To 13
deck(i) = 4
Next i
For i = 1 To 9
fn DealCards
deck(k) -= 1
Computer(k) += 1
fn DealCards
deck(k) -= 1
play(k) += 1
Next i
fn CheckForCompletedBook // Rich added 5/1/24
fn CheckCPUForCompletedBook // Rich added 5/5/24
end fn


method give_rank rank =
let give, _cards = List.partition (fun (pip, _) -> pip = rank) cards in
cards <- _cards;
(give)


local fn QuitOrPlayAlert(GameResult as CFStringRef)
method books_length =
(List.length books)
alert -2,,GameResult,@"Game Over",@"Quit;Play Again"
AlertButtonSetKeyEquivalent( 2, 2, @"\e" )
short result
result = alert 2
if ( result != NSAlertSecondButtonReturn ) then end
end fn


local fn QuitOrResumeAlert(GameResult as CFStringRef)
method empty_hand =
cards = []
alert -3,,GameResult,@"Quit the game?",@"Quit;Resume game"
AlertButtonSetKeyEquivalent( 3, 2, @"\e" )
short result
result = alert 3
if ( result != NSAlertSecondButtonReturn ) then end
end fn


local fn CheckForEndGame as boolean
method private dump_cards() =
print_endline(String.concat ", " (List.map string_of_card cards));
bool PlayAgain = _False
short np = 0, nc = 0
For i = 1 To 13
np += play(i)
nc += Computer(i)
Next i
If RemainingCards = 0 || np = 0 || nc = 0
text ,,fn colorRed
Print "*** Game Over! ***"
Print
If Points(0) < Points(1)
Print "The CPU has won."
print:print
fn QuitOrPlayAlert(@"the CPU won!")
PlayAgain = _True
Else if Points(0) > Points(1)
Print YourName + " has won."
print:print
fn QuitOrPlayAlert(@"You Won!")
PlayAgain = _True
Else
Print "­It's a tie!"
fn QuitOrPlayAlert(@"It's a tie!.")
PlayAgain = _True
End If
fn PrintViewScrollToBottom( fn WindowPrintView(1) )
End If
PlayAgain = _True
End If


End fn = PlayAgain
end




void local fn Intro
text ,,fn colorGreen
Print " __ _ _ "
Print " __ _ ___ / _(_)___| |__ "
Print " / ` |/ _ \ | |_| / __| //_ \ "
Print "| (_) | (_) | | _| \__ \ | | | "
Print " \__, |\___/ |_| |_|___/_| |_| "
Print " |___/ "
Print ""
text ,,fn colorCyan
print %(301,90),"( x to exit the game )"
text ,,fn colorWhite
print " Go Fish Rules:"
print
print " You are playing against the CPU."
print " You are dealt nine cards to start with."
print " The remaining cards are placed face down in the center of the table"
print " to form the draw pile (the fish pond)."
print " On your turn, you ask the CPU For a card."
print " You must already have at least one card of a given rank to ask For more."
print " (A rank is one || more of any card.)"
print " If the CPU has any cards of the named rank, it must hand over all such cards,"
print " and you can then ask again."
print " If the CPU has no cards of the named rank, a card will be drawn from the pile,"
print " and placed in your hand, which then ends your turn."
print " A book is a collection of four cards in a given rank."
print " Whenever you complete a book, it will be removed from your hand."
print " If at any time, your hand is empty, a new card will be drawn from the pile."
print " The game ends when every book is complete,"
print " || there are no more cards left in the pile."
print " The player with the most books wins."
CFStringRef UserInput
"InputYourName"
UserInput = input % (300, 70), @"What's your name?: "
if ( UserInput == NULL ) then "InputYourName" // Rich added this 5/1/24
fn CFStringGetPascalString (UserInput, @YourName, 256, _kCFStringEncodingMacRoman)
cls
if YourName = "X" || YourName = "x" || YourName = chr$(127) then fn QuitOrResumeAlert(@"EXIT")
End fn


class human_player =
object (s) inherit abstract_player


local fn WhatCardInputHeight as short
val mutable cards = []
val mutable books = []
CGRect mainScreenFrame = fn ScreenMainFrame
float InputHeight = int(mainScreenFrame.size.height - 120)
end fn = InputHeight


method ask_rank() =
let ranks =
List.fold_left (fun acc card ->
let rank = pip_of_card card in
if List.mem rank acc
then (acc)
else (rank::acc)
)
[] cards
in
s#dump_cards();
Printf.printf "Ranks: %s\n%!" (String.concat ", " (List.map string_of_pip ranks));
let n = List.length ranks in
Printf.printf "choose from 1 to %d\n%!" n;
let get_int() =
try int_of_string(read_line())
with Failure "int_of_string" -> raise Exit
in
let rec aux() =
let d = get_int() in
if d <= 0 || d > n then aux() else (pred d)
in
let d = aux() in
(List.nth ranks d)


local fn BuildWindow
method notify_booked rank =
Printf.printf "Rank [%s] is now booked\n%!" (string_of_pip rank);
// ---> Get the size of the Main Screen. <---
CGRect mainScreenFrame = fn ScreenMainFrame
float msh = mainScreenFrame.size.height
CGRect r = fn CGRectMake( 0, 0, 600, int(msh) - 110)
window 1, @"Go Fish", r
windowcenter(1)
WindowSetBackgroundColor(1,fn ColorBlack)
end fn


//--- Start ---
method request_failed rank = ()
end


fn BuildWindow


fn Intro


fn InitCards
class ai_player =
object (s) inherit abstract_player as parent


str255 AddTheS
val mutable cards = []
bool RequestCard = _false
val mutable books = []
short v = 0
val mutable opponent = List.map (fun rank -> (rank, Unknown)) pips
short po = 0
boolean ShowHand = _false
str255 WantsCard


"Main"
method private dump_state() =
let f (pip, state) =
Printf.sprintf "{%s:%s}" (string_of_pip pip) (string_of_state state)
in
print_endline(String.concat ", " (List.map f opponent));


ShowHand = _false
method ask_rank() =
let ranks =
List.fold_left (fun acc card ->
let rank = pip_of_card card in
try
let _,n = List.find (fun (_rank,_) -> _rank = rank) acc in
(replace (rank, n+1) acc)
with Not_found ->
((rank,1)::acc)
)
[] cards
in
let f (rank,_) =
(state_score(List.assoc rank opponent))
in
let ranks = List.sort (fun a b -> (f b) - (f a)) ranks in
(* DEBUG
Printf.printf "Ranks: %s\n%!" (String.concat ", " (List.map string_of_pip ranks));
s#dump_state();
s#dump_cards();
*)
opponent <- List.sort (fun _ _ -> Random.int 9 - Random.int 9) opponent;
if ranks <> []
then fst(List.hd ranks)
else Jack


str255 RequestedCard
method give_cards (_cards : card list) =
let rank = pip_of_card(List.hd _cards) in
opponent <- replace (rank, No_cards) opponent;
(parent#give_cards _cards)


While ShowHand = _false
method give_rank rank =
text ,,fn colorGreen
opponent <- replace (rank, Has_cards) opponent;
Print Chr$(10) + "Points >> ";
(parent#give_rank rank)
text ,,fn colorYellow
print YourName + ": ";
text ,,fn colorGreen
print Points(0);
text ,,fn colorOrange
print " CPU: ";
text ,,fn colorGreen
print Points(1)
text ,,fn colorWhite
Print Chr$(10) + " " + str$(RemainingCards) + " remaining cards"
text ,,fn colorWhite
/*
// Uncomment this to see the CPUs cards For testing
Print Chr$(10) + "CPU Cards: ";
For i = 1 To 13
if Computer(i) <> 0
For j = 1 To Computer(i)
if Mid$(cards,i,1) = "0"
Print @"10"; " ";
else
Print Mid$(cards,i,1); " ";
end if
Next j
End If
Next i
Print
fn PrintViewScrollToBottom( fn WindowPrintView(1))
*/
Print Chr$(10) + "Your Cards: ";
For i = 1 To 13
if play(i) <> 0
For j = 1 To play(i)
if Mid$(cards,i,1) = "0"
Print @"10"; " ";
else
Print Mid$(cards,i,1); " ";
end if
Next j
End If
Next i
Print
fn PrintViewScrollToBottom( fn WindowPrintView(1))
RequestCard = _false
While RequestCard = _false
if fn CheckForEndGame = _True then cls:fn InitCards:goto "Loop"
Someone = YourName
CFStringRef UserInput = 0
"InputCard"
UserInput = input % (20, fn WhatCardInputHeight),@"What card do you want? "
if ( UserInput == NULL ) then "InputCard" // Rich added this 5/1/24
fn CFStringGetPascalString (UserInput, @RequestedCard, 256, _kCFStringEncodingMacRoman)
if RequestedCard = "10" then RequestedCard = "0"// card zero is a 10
text ,,fn ColorYellow
Print
WantsCard = RequestedCard
if WantsCard = "j" || WantsCard = "J" then WantsCard = "Jack"
if WantsCard = "q" || WantsCard = "Q" then WantsCard = "Queen"
if WantsCard = "k" || WantsCard = "K" then WantsCard = "King"
if WantsCard = "a" || WantsCard = "A" then WantsCard = "Ace"
if WantsCard = "0" then WantsCard = "10"
print "-------------------------------------"
print
str255 AorAn
AorAn = "a"
if WantsCard = "Ace" then AorAn = "an"
print YourName + " asked For " + AorAn + " " + WantsCard
print
text ,,fn ColorWhite
fn PrintViewScrollToBottom( fn WindowPrintView(1))
if RequestedCard = "X" || RequestedCard = "x" then fn QuitOrResumeAlert(@"EXIT")
If RequestedCard <> "" Then CardNumber = Instr$(1,cards, Ucase$(RequestedCard)): RequestCard = _true
If CardNumber = 0
text,,fn ColorRed
Print "Sorry, that is not a valid card.": RequestCard = _false
print
fn PrintViewScrollToBottom( fn WindowPrintView(1))
Else if play(CardNumber) = 0 Then text ,,fn colorRed: Print "You don//t have that card!": text ,,fn colorRed: RequestCard = _false
fn PrintViewScrollToBottom( fn WindowPrintView(1))
text,,fn ColorWhite
End If
Wend
guess(CardNumber) = 1
If Computer(CardNumber) = 0
Print Someone + ",";
text ,,fn colorRed
Print " Go fish!"
text ,,fn colorWhite
Print Someone + " got a";: fn youGoFishing
print
fn CheckForCompletedBook
ShowHand = _true
Else
v = Computer(CardNumber)
Computer(CardNumber) = 0
play(CardNumber) += v
if v > 1 then AddTheS = "s" else AddTheS = ""
Print Someone + " got" + str$(v) + " card" + AddTheS
fn CheckForCompletedBook
fn PrintViewScrollToBottom( fn WindowPrintView(1))
ShowHand = _false
End If
Wend


method notify_booked rank =
opponent <- replace (rank, Booked) opponent


method request_failed rank =
opponent <- replace (rank, No_cards) opponent
end


Someone = "CPU"
For i = 1 To 13
asked(i) = 0
Next i


bool CPUsTurn = _false


While CPUsTurn = _false
class random_player =
if fn CheckForEndGame = _True then cls:fn InitCards:goto "Loop"
object (s) inherit ai_player
po = 0
For i = 1 To 13
If (Computer(i) > 0) && (guess(i) > 0) Then poss(i) = 1: po += 1
Next i
short whilecounter
WhileCounter = 0
If po = 0
// this k is the go fish card.
k = rnd(12) +1
while Computer(k) = 0 || asked(k)
whilecounter ++
k = rnd(12) +1
if WhileCounter > 100 then k = 0: exit while //5/5/24 Rich added this to prevent hangs
wend
Else
k = rnd(12) + 1
while poss(k) = 0
k = rnd(12) + 1
if WhileCounter > 100 then k = 0: exit while //5/5/24 Rich added this to prevent hangs
wend
guess(k) = 0
asked(k) = 1
end if
if k = 0 then "Loop" //5/5/24 Rich added this to prevent hangs
WantsCard = fn CheckForFaceCard(k)
if WantsCard = "j" || WantsCard = "J" then WantsCard = "Jack"
if WantsCard = "q" || WantsCard = "Q" then WantsCard = "Queen"
if WantsCard = "k" || WantsCard = "K" then WantsCard = "King"
if WantsCard = "a" || WantsCard = "A" then WantsCard = "Ace"
if WantsCard = "0" then WantsCard = "10"
text ,,fn ColorOrange
print "-------------------------------------"
Print:Print Someone + " wants your " + wantsCard + "'s."
print
text ,,fn ColorWhite
asked(k) = 1
If play(k) = 0
Print Someone + ", ";
text ,,fn colorRed: Print "go fish!"
text ,,fn colorWhite:Print Someone + " got";: fn cpuGoFishing
fn CheckCPUForCompletedBook
CPUsTurn = _true
Else
v = play(k)
play(k) = 0
Computer(k) += v
if v > 1 then AddTheS = "s" else AddTheS = ""
Print Someone + " got" + str$(v) + " card" + AddTheS
fn CheckCPUForCompletedBook
CPUsTurn = _false
End If
Wend


"Loop"
method ask_rank() =
goto "Main"
let ranks =
List.fold_left (fun acc card ->
let rank = pip_of_card card in
if List.mem rank acc
then (acc)
else (rank::acc)
)
[] cards
in
let n = List.length ranks in
let d = Random.int n in
(List.nth ranks d)


handleevents
end


</syntaxhighlight>


=={{header|Go}}==
See [[Go Fish/Go]]


=={{header|Haskell}}==
exception Empty_deck
See [[Go Fish/Haskell]]
let card_to_player deck player op =
match deck with
| card::deck ->
begin match player#give_card card with
| None -> ()
| Some rank -> op#notify_booked rank
end;
(deck)
| _ -> raise Empty_deck


=={{header|Icon}} and {{header|Unicon}}==
let n_cards_to_player n deck player op =
See [[Go Fish/Unicon]]
let rec aux i deck =
if i >= n then (deck) else
let deck = card_to_player deck player op in
aux (succ i) deck
in
aux 0 deck ;;


=={{header|J}}==
See [[Go Fish/J]]


=={{header|Java}}==
let () =
See [[Go Fish/Java]]
Random.self_init();
let deck = List.sort (fun _ _ -> Random.int 9 - Random.int 9) deck in
let player_a = new human_player
and player_b = new ai_player in
let deck = n_cards_to_player 9 deck player_a player_b in
let deck = n_cards_to_player 9 deck player_b player_a in
let deck = ref deck in
let empty_hand player1 player2 =
if player1#empty_hand
then deck := card_to_player !deck player1 player2
in
let rec make_turn id1 id2 player1 player2 =
print_newline();
(try
empty_hand player1 player2;
empty_hand player2 player1;
with Empty_deck -> ());
if player1#books_length + player2#books_length <> 13
then begin
let rank = player1#ask_rank() in
Printf.printf "player %s asked for %ss\n%!" id1 (string_of_pip rank);
let cards = player2#give_rank rank in
match cards with
| [] ->
Printf.printf "player %s has no %ss\n%!" id2 (string_of_pip rank);
player1#request_failed rank;
(try
deck := card_to_player !deck player1 player2;
make_turn id2 id1 player2 player1
with Empty_deck -> ())


=={{header|Julia}}==
| cards ->
see [[Go Fish/Julia]]
let given = String.concat ", " (List.map string_of_card cards) in
Printf.printf "player %s gives %s\n%!" id2 given;
begin match player1#give_cards cards with
| None -> ()
| Some rank ->
Printf.printf "player %s booked [%s]\n%!" id1 (string_of_pip rank);
player2#notify_booked rank;
end;
make_turn id1 id2 player1 player2
end
in
(try
if Random.bool()
then make_turn "a" "b" player_a player_b
else make_turn "b" "a" player_b player_a;
with Exit -> ());


=={{header|Kotlin}}==
Printf.printf "player a has %d books\n" (player_a#books_length);
See [[Go Fish/Kotlin]]
Printf.printf "player b has %d books\n" (player_b#books_length);
;;
</lang>


=={{header|Perl 6}}==
=={{header|Lua}}==
See [[Go Fish/Lua]]
{{works with|Rakudo|#23 "Lisbon"}}


=={{header|Locomotive Basic}}==
<lang perl6>constant BOOKSIZE = 4;
See [[Go Fish/Locomotive Basic]]
constant HANDSIZE = 9;
constant Str @pips = <two three four five six seven eight nine ten jack queen king ace>;
# The elements of @pips are only names. Pips are represented internally
# as indices of this array.
constant Str @piparticles = <a a a a a a an a a a a a an>;
constant Str @ppips = <deuces threes fours fives sixes sevens eights nines tens jacks queens kings aces>;
constant Str @shortpips = <2 3 4 5 6 7 8 9 T J Q K A>;
constant $foe_nominative_pronoun = pick 1, <he she it e xe>;


=={{header|Mathematica}} / {{header|Wolfram Language}}==
sub count ($x, *@a) {
See [[Go Fish/Mathematica]]
my $n = 0;
$_ eqv $x and ++$n for @a;
return $n;
}


=={{header|Nim}}==
sub find ($x, *@a) {
See [[Go Fish/Nim]]
for @a.kv -> $k, $v {
$v eqv $x and return $k;
}
fail 'Not found';
}


=={{header|OCaml}}==
sub maxes (&f, *@a) {
See [[Go Fish/OCaml]]
my $x = [max] map &f, @a;
return grep { f($^e) eqv $x }, @a;
}


=={{header|Perl}}==
sub ncard ($n, $pip) {
<syntaxhighlight lang="perl">#!/usr/bin/perl
$n > 1 ?? "$n {@ppips[$pip]}" !! "{@piparticles[$pip]} {@pips[$pip]}"
}


use strict; # https://rosettacode.org/wiki/Go_Fish
sub readpip (@user_hand) {
use warnings;
my @choices = grep { @user_hand[$^p] }, ^@pips;
use List::Util qw( first shuffle );
if (@choices == 1) {
say "You're obliged to ask for { @ppips[@choices[0]] }.";
return @choices[0];
}
loop {
print 'For what do you ask? (', join(', ', @shortpips[@choices]), '): ';
my $in = substr uc($*IN.get or next), 0, 1;
my $pip = find $in, @shortpips;
if defined $pip {
@user_hand[$pip] and return $pip;
say "You don't have any { @ppips[$pip] }.";
}
else {
say 'No such rank.';
}
}
}


my $pat = qr/[atjqk2-9]/; # ranks
enum Maybe <No Yes Dunno>;
my $deck = join '', shuffle map { my $rank = $_; map "$rank$_", qw( S H C D ) }
qw( a t j q k ), 2 .. 9;


my $mebooks = my $youbooks = 0;
class Knowledge {
# The computer player has an instance of this class for each pip.
# Each instance tracks whether the computer thinks the user has at
# least one card of the corresponding pip.
has Maybe $.maybe = Dunno;
# Yes if the user definitely has this pip, No if they didn't
# have it the last time we checked, Dunno if we haven't yet
# checked.
has Int $.n = 0;
# If $.maybe is No, $.n counts how many cards the user
# has drawn since we last checked.


my $me = substr $deck, 0, 2 * 9, '';
method set (Maybe $!maybe) { $!n = 0 }
my $mepicks = join '', $me =~ /$pat/g;
arrange($me);
$mebooks++ while $me =~ s/($pat).\1.\1.\1.//;
my $you = substr $deck, 0, 2 * 9, '';
my $youpicks = join '', $you =~ /$pat/g;
arrange($you);
$youbooks++ while $you =~ s/($pat).\1.\1.\1.//;


while( $mebooks + $youbooks < 13 )
method incr { $.maybe == No and ++$!n }
{
}
play( \$you, \$youbooks, \$youpicks, \$me, \$mebooks, 1 );
$mebooks + $youbooks == 13 and last;
play( \$me, \$mebooks, \$mepicks, \$you, \$youbooks, 0 );
}
print "me $mebooks you $youbooks\n";


sub arrange { $_[0] = join '', sort $_[0] =~ /../g }
class Player {
has Int @.h;
# @h[$n] is number of cards of pip $n in this player's hand.
has $.deck;
# A reference to whatever deck the player's playing with.
has Int $.books = 0;
has Bool $.cpu;
has Knowledge @.know;


sub human
method new ($cpu, @deck is rw) {
{
my Int @h = 0 xx @pips;
my $have = shift =~ s/($pat).\K(?!\1)/ /gr;
++@h[$_] for @deck[^HANDSIZE];
local $| = 1;
@deck = @deck[HANDSIZE ..^ @deck];
my $pick;
Player.bless(*,
do
h => @h, cpu => $cpu,
{
deck => \@deck,
print "You have $have, enter request: ";
know => ($cpu ?? map { Knowledge.new() }, @pips !! ())
);
($pick) = lc(<STDIN>) =~ /$pat/g;
} until $pick and $have =~ /$pick/;
}
return $pick;
}


sub play
method showhand {
{
say
my ($me, $mb, $lastpicks, $you, $yb, $human) = @_;
($.cpu ?? 'The dealer has ' !! 'You have '),
my $more = 1;
join(' ',
while( arrange( $$me ), $more and $$mb + $$yb < 13 )
map { join ' ', @shortpips[.key] xx .value },
{
grep { .value },
# use Data::Dump 'dd'; dd \@_, "deck $deck";
pairs @.h),
if( $$me =~ s/($pat).\1.\1.\1.// )
'.';
}
{
print "book of $&\n";

method draw () {
$$mb++;
}
my $new = shift $.deck;
elsif( $$me )
$.cpu or print "You got { ncard 1, $new }. ";
{
say "({ $.deck.elems or 'No' } card{ $.deck.elems == 1 ?? '' !! 's' } left.)";
my $pick = $human ? do { human($$me) } : do
self.getcards(1, $new);
}
{
my %picks;

$picks{$_}++ for my @picks = $$me =~ /$pat/g;
method getcards (Int $quantity, Int $pip) {
my $pick = first { $picks{$_} } split(//, $$lastpicks), shuffle @picks;
@!h[$pip] += $quantity;
@.h[$pip] == BOOKSIZE or return;
print "pick $pick\n";
++$!books;
$$lastpicks =~ s/$pick//g;
say
$$lastpicks .= $pick;
($.cpu
$pick;
};
?? "The dealer puts down a book of { @ppips[$pip] }"
!! "That's a book"),
if( $$you =~ s/(?:$pick.)+// )
{
" (for a total of $.books book{ $.books == 1 ?? '' !! 's' }).";
self.losecards($pip);
$$me .= $&;
}

method losecards (Int $pip) {
@.h[$pip] = 0;
while none @.h and $.deck.elems {
say do $.cpu
?? "The dealer's hand is empty, so $foe_nominative_pronoun draws a new card."
!! "Your hand's empty, so you draw a new card.";
self.draw;
}
}

method learn (Int $pip, Maybe $m) { @.know[$pip].set($m) }

method notice_draw () { .incr for @.know }

method choose_request () returns Int {
#self.showhand;
#say 'Know: ', join ', ', map
# { .maybe ~~ Yes ?? 'Yes' !! .maybe ~~ Dunno ?? 'Dunno' !! .n },
# @.know;
my @ps = map { .key }, grep { .value }, pairs @.h;
return pick 1, maxes { @.h[$^p] }, do
# Most of all we should ask for cards we know the
# user has.
grep { @.know[$^p].maybe ~~ Yes }, @ps or
# Then try asking for one we haven't requested
# before.
grep { @.know[$^p].maybe ~~ Dunno }, @ps or
# Then try asking for one we least recently
# asked about.
maxes { @.know[$^p].n }, @ps;
}
}

sub play () {

my Int @deck;
# Shuffle the deck until the first two hands contain no books.
# (If BOOKSIZE is greater than 2 and HANDSIZE is reasonably
# small, this'll probably take only one shuffle.)
repeat { @deck = pick *, ^@pips xx BOOKSIZE }
until none(map { count $^x, @deck[^HANDSIZE] }, ^@pips) >= BOOKSIZE and
none(map { count $^x, @deck[HANDSIZE ..^ 2*HANDSIZE] }, ^@pips) >= BOOKSIZE;

my Player $user .= new(False, @deck);
my Player $foe .= new(True, @deck);
while any |$user.h or any |$foe.h {
# The user goes first.
while any |$user.h {
say '';
$user.showhand;
my $request = readpip $user.h;
$foe.learn($request, Yes);
if $foe.h[$request] -> $quantity is copy {
say 'The dealer reluctantly hands over ',
ncard($quantity, $request),
'.';
$foe.losecards($request);
$user.getcards($quantity, $request);
}
else {
say '"Go fish!"';
$user.draw;
$foe.notice_draw;
last;
}
}
}
else

while any |$foe.h {
{
my $request = $foe.choose_request;
print "GO FISH !!\n";
say "\n\"Got any ", @ppips[$request], '?"';
$$me .= substr $deck, 0, 2, '';
$foe.learn($request, No);
$more = 0;
if $user.h[$request] -> $quantity is copy {
say '"Thanks!"';
$foe.getcards($quantity, $request);
$user.losecards($request);
}
else {
say 'The dealer goes fishing.';
$foe.draw;
last;
}
}
}
}

elsif( $deck )
{
$$me .= substr $deck, 0, 2, '';
}
else
{
$more = 0;
}
}
}
arrange( $$me );
}</syntaxhighlight>


=={{header|Phix}}==
say "\nGame over!";
See [[Go Fish/Phix]]
say 'Your books: ', $user.books;
say "The dealer's books: ", $foe.books;
say do
$user.books > $foe.books
?? 'A winner is you!'
!! $user.books < $foe.books
?? 'Alas, you have lost.'
# A draw is possible if @pips !% 2.
!! "It's a draw.";


=={{header|PicoLisp}}==
}
See [[Go Fish/PicoLisp]]

=={{header|PowerShell}}==
See [[Go Fish/PowerShell]]


=={{header|PureBasic}}==
sub MAIN () { play }</lang>
See [[Go Fish/PureBasic]]


=={{header|Python}}==
=={{header|Python}}==
{{works with|Python|2.5}}
See [[Go Fish/Python]]
<lang Python>import random
import sys
from collections import defaultdict


=={{header|Raku}}==
class HumanPlayer(object):
(formerly Perl 6)
def __init__(self,deck):
See [[Go Fish/Raku]]
self.hand = defaultdict(int)
self.book = []
self.deck = deck #making a copy of deck, all changes within
#this class should affect the global deck
self.score = 0
self.name = raw_input('Name yourself: ')
def Draw(self): #assuming that deck is a global
cardDrawn = self.deck.pop() #removes the last card from deck
self.hand[cardDrawn] += 1 #adds card to hand
print '%s drew %s.' % (self.name,cardDrawn)
self.checkForBooks()
def checkForBooks(self):
# Removes all items of which are 4.
for key,val in self.hand.items(): #can't use iteritems() because we are modifying hand in loop
if val == 4: #completed a book
self.book.append(key)
print '%s completed the book of %s\'s.' % (self.name,key)
self.score += 1
del self.hand[key]
self.emptyCheck()


=={{header|Red}}==
def emptyCheck(self):
<syntaxhighlight lang="red">
if len(self.deck)!=0 and len(self.hand)==0: #checks if deck/hand is empty
Red [
self.Draw()
Title: "Go Fish"
def displayHand(self): #Displays current hand, cards separated by spaces
Author: "gltewalt"
return ' '.join(key for key,val in self.hand.iteritems()
]
for i in range(val)) #meh, make it prettier

chand: [] ;-- c and p = computer and player
def makeTurn(self):
cguesses: []
print '%s\'s hand: %s' % (self.name,self.displayHand())
phand: []
chooseCard = raw_input('What card do you ask for? ').strip()
cbooks: 0
if chooseCard == 'quit':
pbooks: 0
sys.exit(0)
gf: {
if chooseCard not in self.hand:
***************
print 'You don\'t have that card. Try again! (or enter quit to exit)'
chooseCard = self.makeTurn()
* GO FISH *
***************
return chooseCard
def fishFor(self,card):
if card in self.hand: # if card in hand, returns count and removes the card from hand
val = self.hand.pop(card)
self.emptyCheck()
return val
else:
return False
def gotCard(self,card,amount):
self.hand[card] += amount
self.checkForBooks()
class Computer(HumanPlayer):
def __init__(self,deck):
self.name = 'Computer'
self.hand = defaultdict(int)
self.book = []
self.deck = deck
self.opponentHas = set()
self.score = 0

def Draw(self): #assuming that deck is a global
cardDrawn = self.deck.pop() #removes the last card from deck
self.hand[cardDrawn] += 1 #adds card to hand
print '%s drew a card.' % (self.name)
self.checkForBooks()

##AI: guesses cards that knows you have, then tries cards he has at random.
##Improvements: remember if the card was rejected before, guess probabilities
def makeTurn(self):
# print self.displayHand(),self.opponentHas
candidates = list(self.opponentHas & set(self.hand.keys())) #checks for cards in hand that computer knows you have
if not candidates:
candidates = self.hand.keys() #if no intersection between those two, random guess
move = random.choice(candidates)
print '%s fishes for %s.' % (self.name,move)
return move
def fishFor(self,card): #Same as for humans players, but adds the card fished for to opponentHas list.
self.opponentHas.add(card)
if card in self.hand: # if card in hand, returns count and removes the card from hand
val = self.hand.pop(card)
self.emptyCheck()
return val
else:
return False
def gotCard(self,card,amount):
self.hand[card] += amount
self.opponentHas.discard(card)
self.checkForBooks()
class PlayGoFish(object):
def __init__(self):
self.deck = ('2 3 4 5 6 7 8 9 10 J Q K A '*4).split(' ')
self.deck.remove('')
self.player = [HumanPlayer(self.deck),Computer(self.deck)] #makes counting turns easier

def endOfPlayCheck(self):#checks if hands/decks are empty using the any method
return self.deck or self.player[0].hand or self.player[1].hand
def play(self):
random.shuffle(self.deck)
for i in xrange(9): # Deal the first cards
self.player[0].Draw()
self.player[1].Draw()
turn = 0
while self.endOfPlayCheck():
print '\nTurn %d (%s:%d %s:%d) %d cards remaining.' % (turn,self.player[0].name,
self.player[0].score,self.player[1].name,self.player[1].score,len(self.deck))
whoseTurn = turn%2
otherPlayer = (turn+1)%2
while True: #loop until player finishes turn
cardFished = self.player[whoseTurn].makeTurn()
result = self.player[otherPlayer].fishFor(cardFished)
if not result: #Draws and ends turn
self.player[whoseTurn].Draw()
break
print '%s got %d more %s.' % (self.player[whoseTurn].name,result, cardFished)
self.player[whoseTurn].gotCard(cardFished,result)
if not self.endOfPlayCheck(): break
turn+=1
print '\nScores: \n%s: %d\n%s: %d\n' % (self.player[0].name,self.player[0].score,
self.player[1].name,self.player[1].score)
if self.player[0].score>self.player[1].score:
print self.player[0].name,'won!'
elif self.player[0].score==self.player[1].score:
print 'Draw!'
else:
print self.player[1].name,'won!'

if __name__=="__main__":
game = PlayGoFish()
game.play()</lang>

=={{header|Tcl}}==
{{works with|Tcl|8.6}}
<lang tcl>package require Tcl 8.6

# How to sort ranks
proc suitorder {a b} {
set a1 [lsearch -exact {2 3 4 5 6 7 8 9 10 J Q K A} $a]
set b1 [lsearch -exact {2 3 4 5 6 7 8 9 10 J Q K A} $b]
expr {$a1 - $b1}
}
}
pip: ["a" "2" "3" "4" "5" "6" "7" "8" "9" "10" "j" "q" "k"] ;-- suits are not relevant
pile: [] ;-- where discarded cards go
;---------------------
; Helper functions -
;---------------------
clear-screen: does [
"clears the console"
call/console either system/platform = 'Linux ["clear"]["cls"]
]
clear-and-show: func [duration str][
{
Poor persons animation.
Blips message to screen after a pause of duration length.


# Class to manage the deck of cards
oo::class create Deck {
variable deck

constructor {{packs 1}} {
set deck [list]
for {set p 0} {$p < $packs} {incr p} {
foreach suit {C D H S} {
foreach pip {2 3 4 5 6 7 8 9 10 J Q K A} {
lappend deck [list $pip $suit]
}
}
}
}
}
clear-screen
print str
wait duration
clear-screen
]
deal-cards: func [num hand][
loop num [
append hand rejoin [trim/all form take deck]
]
]
find-in: func [blk str][
"Finds a string value in a block. Series in series."
foreach i blk [if find i str [return i]]
]
go-fish: func [num hand][
either not empty? deck [
deal-cards num hand
][
append hand rejoin [trim/all form take pile] ;-- take from pile if deck is empty
]
]
guess-from: func [hand guessed][
{
Randomly picks from hand minus guessed.


Simulates a person asking for different cards on
method shuffle {} {
their next turn if their previous guess resulted
# Shuffle in-place
in a Go Fish.
for {set i [llength $deck]} {[incr i -1] > 0} {} {
set n [expr {int($i * rand())}]
set card [lindex $deck $n]
lset deck $n [lindex $deck $i]
lset deck $i $card
}
}
}
random/seed now/time
either any [empty? guessed empty? exclude hand guessed][
random/only hand
][
random/only exclude hand guessed
]
]
make-deck: function [] [
"make-deck and shuffle from https://rosettacode.org/wiki/Playing_cards#Red"
new-deck: make block! 52
foreach p pip [loop 4 [append/only new-deck p]]
return new-deck
]


show-cards: does [
method deal {num} {
clear-and-show 0 ""
incr num -1
print [newline "Player cards:" newline sort phand newline]
set hand [lrange $deck 0 $num]
print ["Computer books:" cbooks]
set deck [lreplace $deck 0 $num]
print ["Player books:" pbooks newline]
return $hand
]
}


shuffle: function [deck [block!]] [deck: random deck]
method renderCard {card} {
string map {C \u2663 D \u2662 H \u2661 S \u2660 " " {}} $card
;------------- end of helper functions -----------------
}
method print {hand} {
set prev {}
foreach card [my sortHand $hand] {
if {[lindex $card 0] ne $prev} {
if {$prev ne ""} {puts ""}
puts -nonewline \t[my renderCard $card]
} else {
puts -nonewline " [my renderCard $card]"
}
set prev [lindex $card 0]
}
puts ""
}


check-for-books: func [
method sortHand {hand} {
{
lsort -index 0 -command suitorder [lsort -index 1 $hand]
Checks for a book in a players hand.
Increments the players book score, and
discards the book from the players hand
}
}
hand "from or to hand"
kind "rank of cards"
/local
c "collected"
][
c: collect [
forall hand [keep find hand/1 kind]
]
remove-each i c [none = i]
if 4 = length? c [
either hand = phand [pbooks: pbooks + 1][cbooks: cbooks + 1]
remove-each i hand [if find/only c i [i]] ;-- remove book from hand
forall c [append pile c/1] ;-- append discarded book to the pile
]
]
transfer-cards: func [
"Transfers cards from player to player"
fhand "from hand"
thand "to hand"
kind "rank of cards"
/local
c "collected"
][
c: collect [forall fhand [keep find fhand/1 kind]]
remove-each i c [none = i] ;-- remove none values from collected
forall c [append thand c/1] ;-- append remaining values to "to hand"
remove-each i fhand [if find/only c i [i]] ;-- remove those values from "from hand"
]
computer-turn: func [
fhand "from hand"
thand "to hand"
kind "rank of cards"
/local
a
][
a: ask rejoin ["Do you have any " kind " s? "]
if a = "x" [halt]
either any [a = "y" a = "yes"][
check-for-books thand kind
transfer-cards fhand thand kind
show-cards
computer-turn fhand thand guess-from thand cguesses
][
clear-and-show 0.4 gf
go-fish 1 thand
append cguesses kind
]
]
player-turn: func [
fhand "from hand"
thand "to hand"
kind "rank of cards"
/local
p
][
if empty? fhand [go-fish 3 fhand]


if none? find-in thand kind [ ;-- player has to hold rank asked for
proc empty {} {
clear-and-show 1.0
return [expr {[llength $deck] == 0}]
"You have to have that rank in your hand to ask for it.^/Computers turn..."
}
exit
}
]


either find-in fhand kind [
# "Abstract" class of all players; implements core game mechanics
check-for-books thand kind
# from a player's perspective
transfer-cards fhand thand kind
oo::class create GoFishPlayer {
show-cards
variable theDeck hand opponent
if find-in thand kind [
constructor {deck otherPlayer} {
p: ask "Your guess: "
set theDeck $deck
either p = "x" [halt][player-turn fhand thand p]
set hand [$deck deal 9]
check-for-books thand p
set opponent $otherPlayer
}
]
][
clear-and-show 0.4 gf
go-fish 1 thand
]
]
game-round: has [c p][
print {
-------------------
- COMPUTER TURN -
-------------------
}
if empty? chand [ ; computer has no more cards? fish 3 cards.
go-fish 3 chand
show-cards
]
computer-turn phand chand c: guess-from chand cguesses
check-for-books chand c
show-cards
print {
-------------------
- PLAYER TURN -
-------------------
}


if empty? phand [ ;-- player has no more cards? fish 3 cards.
method ask {rank} {
go-fish 3 phand
set response {}
show-cards
set new {}
]
foreach card $hand {
p: ask "Your guess: "
if {[lindex $card 0] eq $rank} {
either p = "x" [halt][player-turn chand phand find-in phand p]
lappend response $card
check-for-books phand p
} else {
show-cards
lappend new $card
]
}
}
main: does [
set hand [expr {[llength $new] ? $new : [$theDeck deal 1]}]
deck: shuffle make-deck
return $response
deal-cards 9 chand
}
deal-cards 9 phand
method AskFor {rank} {
show-cards
set withoutOne 1
while [cbooks + pbooks < 13][
foreach card $hand {
if {[lindex $card 0] eq $rank} {
game-round
]
set withoutOne 0
clear-and-show 0 ""
break
print "GAME OVER"
}
print [newline "Computer books:" cbooks newline "Player books:" pbooks]
}
]
if {$withoutOne} {
error "do not have any $rank cards"
}


main
set response [$opponent ask $rank]
</syntaxhighlight>
if {[llength $response]} {
lappend hand {*}$response
} else {
my GoFish
lappend hand {*}[$theDeck deal 1]
}


=={{header|Rust}}==
return [llength $response]
See [[Go Fish/Rust]]
}


=={{header|Ruby}}==
method MakeBooks {} {
See [[Go Fish/Ruby]]
foreach rank {2 3 4 5 6 7 8 9 10 J Q K A} {
set n {}
set idx -1
foreach card $hand {
incr idx
if {[lindex $card 0] eq $rank} {
lappend n $idx
}
}
if {[llength $n] == 4} {
announceBook $rank [self]
foreach idx [lreverse $n] {
set hand [lreplace $hand $idx $idx]
}
}
}
if {[llength $hand] == 0} {
set hand [$theDeck deal 1]
}
}


=={{header|Tcl}}==
method makeAPlay {} {
See [[Go Fish/Tcl]]
set msg ""
while {$::books(total) < 13} {
set rank [my SelectRank $msg]
try {
if {![my AskFor $rank]} {
my YieldToOpponent
break
}
} on error msg {
# Back round the loop with an error message
} on ok {} {
my MakeBooks
set msg ""
}
}
my MakeBooks
}


=={{header|V (Vlang)}}==
method GoFish {} {
See [[Go Fish/V (Vlang)]]
# Do nothing with this notification by default
}
method madeBook {rank who} {
# Do nothing with this notification by default
}
method YieldToOpponent {} {
# Do nothing with this notification by default
}
method SelectRank {msg} {
error "not implemented"
}
}


=={{header|Wren}}==
# A player that works by communicating with a human
See [[Go Fish/Wren]]
oo::class create HumanPlayer {
superclass GoFishPlayer
variable theDeck hand opponent
method madeBook {rank who} {
if {$who eq [self]} {set who "You"}
puts "$who made a book of $rank"
}
method YieldToOpponent {} {
puts "Now your opponent's turn"
}
method AskFor {rank} {
set count [next $rank]
puts "You asked for ${rank}s and received $count cards"
if {$count > 0} {
puts "You may ask again!"
}
return $count
}
method ask {rank} {
set cards [next $rank]
puts "[namespace tail $opponent] asked for $rank cards, and got [llength $cards] of them"
return $cards
}
method GoFish {} {
puts "You were told to \"Go Fish!\""
}

method SelectRank {msg} {
if {$msg ne ""} {
puts "ERROR: $msg"
}
set I [namespace tail [self]]
puts "You are ${I}: Your cards are:"
$theDeck print $hand
while 1 {
puts -nonewline "What rank to ask for? "
flush stdout
set rank [string toupper [gets stdin]]
if {$rank in {2 3 4 5 6 7 8 9 10 J Q K A}} {
return $rank
}
puts "Rank must be 2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K, or A"
puts "You must also have at least one of them already"
}
}
}

# A computer player that tracks what it's opponent must have
oo::class create ThinkingPlayer {
superclass GoFishPlayer
variable state hand
constructor args {
next {*}$args
foreach rank {2 3 4 5 6 7 8 9 10 J Q K A} {
set state($rank) unknown
}
}

method madeBook {rank who} {
set state($rank) booked
}
method AskFor {rank} {
set count [next $rank]
set state($rank) none
if {$count == 0} {
foreach rank {2 3 4 5 6 7 8 9 10 J Q K A} {
if {$state($rank) eq "none"} {
set state($rank) unknown
}
}
}
return $count
}
method ask {rank} {
set cards [next $rank]
set state($rank) some
return $cards
}

method GoFish {} {
puts "You told your opponent to \"Go Fish!\""
}

method SelectRank {ignored} {
# If we know they have the cards and we can grab them, do so!
# It's a safe move since we get to go again.
foreach {rank s} [array get state] {
if {$s eq "some" && [lsearch -exact -index 0 $hand $rank] >= 0} {
return $rank
}
}
# Only unsafe moves remain; pick a random non-stupid one
foreach c $hand {
set rank [lindex $c 0]
if {$state($rank) ne "none"} {
set r([lindex $c 0]) .
}
}
if {[array size r]} {
return [lindex [array names r] [expr {int([array size r]*rand())}]]
}
# No good choices; oh well...
return [lindex $hand [expr {int([llength $hand]*rand())}] 0]
}
}

# How announcements of a book being made are done
proc announceBook {rank who} {
global books

A madeBook $rank $who
B madeBook $rank $who
lappend books($who) $rank
incr books(total)
}

# Stitch things together to make a whole game.
Deck create deck
deck shuffle
array set books {total 0 ::A {} ::B {}}
HumanPlayer create A deck B
ThinkingPlayer create B deck A
while {$books(total) < 13} {
A makeAPlay
if {$books(total) < 13} {
B makeAPlay
}
}
puts "You have [llength $books(::A)]: [lsort -command suitorder $books(::A)]"
puts "The computer has [llength $books(::B)]: [lsort -command suitorder $books(::B)]"
if {[llength $books(::A)] > [llength $books(::B)]} {
puts "You win!"
} else {
puts "The computer won!"
}</lang>


{{omit from|XSLT|XSLT lacks standard interactive I/O and pseudorandom number generation facilities.}}
===Notes on the Mechanical Player===
The computer player (implemented as a subclass of the generic player class) has four states for ''each'' rank (aside from basic overall state like what cards it is holding, which every player has to have):
;unknown
: Don't know if the opponent has any cards in that rank.
;none
: Opponent has no cards there; I took them away.
;some
: Opponent has cards there; they tried to get them off me and haven't booked them yet.
;booked
: Someone has booked the rank.
It prefers to take cards away from the opponent if it can and tries hard to avoid moves it knows will fail. It never makes illegal moves. It does not bother to look at the number of booked suits, though as that is global state it ''could''. No player or the deck has any method to reveal (within the game world, not the display) what hand of cards it actually has.

Revision as of 23:33, 6 May 2024

Task
Go Fish
You are encouraged to solve this task according to the task description, using any language you may know.

Write a program to let the user play Go Fish against a computer opponent. Use the following rules:

  • Each player is dealt nine cards to start with.
  • On their turn, a player asks their opponent for a given rank (such as threes or kings). A player must already have at least one card of a given rank to ask for more.
    • If the opponent has any cards of the named rank, they must hand over all such cards, and the requester can ask again.
    • If the opponent has no cards of the named rank, the requester draws a card and ends their turn.
  • A book is a collection of every card of a given rank. Whenever a player completes a book, they may remove it from their hand.
  • If at any time a player's hand is empty, they may immediately draw a new card, so long as any new cards remain in the deck.
  • The game ends when every book is complete. The player with the most books wins.

The game's AI need not be terribly smart, but it should use at least some strategy. That is, it shouldn't choose legal moves entirely at random.

You may want to use code from Playing Cards.

Related tasks:


Aime

See Go Fish/Aime

AutoHotkey

See Go Fish/AutoHotkey

C

See Go Fish/C

C++

See Go Fish/C++

D

See Go Fish/D

Erlang

See Go Fish/Erlang

FreeBASIC

' Go Fish ~ ¡Pesca!

Const cartas = "A234567890JQK"

Declare Sub Reparto_Cartas
Declare Sub Pescar_Carta_Jug
Declare Sub Pescar_Carta_CPU
Declare Sub Comprobar_Libro_Jug
Declare Sub Comprobar_Libro_CPU
Declare Sub Comprobar_Fin_Partida
Declare Sub Intro

Dim Shared As Integer play(13), compu(13), deck(13), guess(13), poss(13), asked(13)
Dim Shared As String nombre, Snombre, CartaPedida
Dim Shared puntos(2) As Byte = {0,0}
Dim Shared As Integer remca = 4*Len(cartas)
Dim Shared As Integer i, k, j, cn
For i = 1 To 13
    deck(i) = 4
Next i
For i = 1 To 9
    Reparto_Cartas
    deck(k) -= 1
    compu(k) += 1
    Reparto_Cartas
    deck(k) -= 1
    play(k) += 1
Next i
Dim As Integer v, po

Sub Reparto_Cartas
    remca -= 1
    Dim As Integer sc = remca * Rnd + 1
    For k = 1 To 13
        sc -= deck(k)
        If sc <= 0 Then Return
    Next k
End Sub

Sub Pescar_Carta_Jug
    Reparto_Cartas
    Print " " &Mid(cartas,k,1) &"."
    deck(k) -= 1
    play(k) += 1
End Sub

Sub Pescar_Carta_CPU
    Reparto_Cartas
    Print "a carta."
    deck(k) -= 1
    compu(k) += 1
End Sub

Sub Comprobar_Libro_Jug
    For i = 1 To 13
        If play(i) <> 4 Then
        Else
            Color 11: Print Snombre &" completa el libro de " &Mid(cartas,i,1) &"'s.": Color 7
            play(i) = 0
            puntos(0) += 1
        End If
    Next i
End Sub

Sub Comprobar_Libro_CPU
    For i = 1 To 13
        If compu(i) <> 4 Then
        Else
            Color 11: Print Snombre &" completa el libro de " &Mid(cartas,i,1) &"'s.": Color 7
            compu(i) = 0
            puntos(1) += 1
        End If
    Next i
End Sub

Sub Comprobar_Fin_Partida
    Dim As Integer np = 0, nc = 0
    For i = 1 To 13
        np += play(i)
        nc += compu(i)
    Next i
    If remca = 0 Or np = 0 Or nc = 0 Then         
        Color 15: Print
        Print "*** ­FIN de la partida! ***"
        Print
        If puntos(0) < puntos(1) Then 
            Print "La CPU ha ganado."
        Elseif puntos(0) > puntos(1) Then 
            Print nombre &" ha ganado." 
        Else 
            Print "­Es un empate!"
        End If
        Sleep: End
    End If
End Sub

Sub Intro
    Color 15 
    Print "                __ _     _      "
    Print "  __ _  ___    / _(_)___| |__   "
    Print " /  ` |/ _ \  | |_| / __| '_ \  "
    Print "| (_) | (_) | |  _| \__ \ | | | "
    Print " \__, |\___/  |_| |_|___/_| |_| "
    Print " |___/                          "
    Print "                                "
    Color 14: Locate 10, 2: Input "Como te llamas: ", nombre
End Sub

'--- Programa Principal ---
Cls
Randomize Timer
Intro
Do
    Dim As boolean MuestraMano = false
    While MuestraMano = false
        Color 15: Print Chr(10) &"Puntos >> " &nombre &": "; puntos(0); "  CPU: "; puntos(1)
        Color 13: Print Chr(10) &space(10) &remca &" cartas restantes"
        Color 14: Print Chr(10) &"Tu mano: ";
        For i = 1 To 13
            If Not play(i) Then 
                For j = 1 To play(i)
                    Print Mid(cartas,i,1); " ";
                Next j
            End If
        Next i
        Print
        
        Dim As boolean PideCarta = false
        While PideCarta = false
            Comprobar_Fin_Partida
            Snombre = nombre
            Color 7: Print
            Input "¨Que carta pides... "; CartaPedida
            Print
            If CartaPedida <> "" Then cn = Instr(cartas, Ucase(CartaPedida)): PideCarta = true
            If cn = 0 Then 
                Print "Lo siento, no es una opción valida.": PideCarta = false
                Elseif play(cn) = 0 Then Color 12: Print "­No tienes esa carta!": Color 7: PideCarta = false
            End If
        Wend
        
        guess(cn) = 1
        If compu(cn) = 0 Then 
            Print Snombre &", ";
            Color 15: Print "­ve a pescar!"
            Color 7: Print Snombre &" pesca un";: Pescar_Carta_Jug
            Comprobar_Libro_Jug
            MuestraMano = true
        Else
            v = compu(cn)
            compu(cn) = 0
            play(cn) += v
            Print Snombre &" consigue " &v &" carta(s) mas."
            Comprobar_Libro_Jug
            MuestraMano = false
        End If
    Wend

    Snombre = "CPU"
    For i = 1 To 13
        asked(i) = 0
    Next i
    Dim As boolean Turno_CPU_2 = false
    While Turno_CPU_2 = false
        Comprobar_Fin_Partida
        po = 0
        For i = 1 To 13
            If (compu(i) > 0) And (guess(i) > 0) Then poss(i) = 1: po += 1
        Next i
        If po = 0 Then 
            Do
                k = (Rnd*12)+1
            Loop While compu(k) = 0 Or asked(k)
        Else
            Do
                k = (Rnd*12)+1
            Loop While poss(k) = 0
            guess(k) = 0
            asked(k) = 1
        End If

        Print: Print Snombre &" quiere tus " &Mid(cartas,k,1) &"'s."
        asked(k) = 1
        If play(k) = 0 Then 
            Print Snombre &", ";
            Color 15: Print "­ve a pescar!"
            Color 7:Print Snombre &" pesca un";: Pescar_Carta_CPU
            Comprobar_Libro_CPU
            Turno_CPU_2 = true
        Else
            v = play(k)
            play(k) = 0
            compu(k) += v
            Print Snombre &" consigue " &v &" carta(s) mas."
            Comprobar_Libro_CPU
            Turno_CPU_2 = false
        End If
    Wend 
Loop
End

FutureBasic

Translated from FreeBasic May 5, 2024 Rich Love Fixed intermittent hang Also, now using && and || to be compatible with later versions of FutureBasic. ( replaces AND and OR )

/*

Go Fish
-- Rich Love --

FutureBasic app For Macintosh
Get the latest FutureBasic here 
http://www.brilorsoftware.com/fb/pages/home.html

*/


_window = 1
begin enum 1
  _scrollView
  _textView
end enum

override _forLoopsAlwaysExecuteAtLeastOnce = _true

begin globals

str255 cards
cards = "A234567890JQK"
short play(13), Computer(13), deck(13), guess(13), poss(13), asked(13)
str255 YourName, Someone
//bool gNeedToClearScreen
short Points(2) : Points(0) = 0 : Points(1) = 0
short i, k, j, CardNumber
short RemainingCards

end globals


local fn CheckForFaceCard(TheCard as short) as str255
  str255 WantsCard
  WantsCard = str$(TheCard)
  
  if TheCard = 0 then WantsCard = "10"
  if TheCard = 11 then WantsCard = "jack"
  if TheCard = 12 then WantsCard = "Queen"
  if TheCard = 13 then WantsCard = "King"
  if TheCard = 1 then WantsCard = "Ace"
end fn = WantsCard


void local fn PrintViewScrollToBottom( printView as ViewRef )
  BeginCCode
            NSScrollView *scrollView = [printView enclosingScrollView];
            NSClipView *clipView = [scrollView contentView];
            [clipView scrollToPoint:NSMakePoint(0,printView.frame.size.height-scrollView.contentSize.height + 20)];
            [scrollView reflectScrolledClipView:clipView];
      EndC
end fn


void local fn DealCards
  
  RemainingCards -= 1
  short sc
  sc = rnd(RemainingCards) + 1  // 5/2/24  Rich added + 1
  
  For k = 1 To 13
    sc -= deck(k)
    If sc <= 0 Then exit fn
  Next k
  
End fn

void local fn youGoFishing
  
  fn DealCards
  
  str255 WantsCard
  WantsCard = fn CheckForFaceCard(k)
  
  if WantsCard = "0" then WantsCard = "10"
  
  Print " "  + WantsCard + "."
  deck(k) -= 1
  play(k) += 1
  
End fn


void local fn cpuGoFishing
  
  fn DealCards
  Print " a card from the deck."
  if k > 13 then k = 13
  deck(k) -= 1
  Computer(k) += 1
  
End fn


void local fn CheckForCompletedBook
  
  For i = 1 To 13
    If play(i) <> 4
    Else
      text ,,fn colorcyan
      
      str255 WantsCard
      WantsCard = Mid$(cards,i,1)
      
      if WantsCard = "j" || WantsCard = "J" then WantsCard = "Jack"
      if WantsCard = "q" || WantsCard = "Q" then WantsCard = "Queen"
      if WantsCard = "k" || WantsCard = "K" then WantsCard = "King"
      if WantsCard = "a" || WantsCard = "A" then WantsCard = "Ace"
      if WantsCard = "0" then WantsCard = "10"
      
      Print YourName + " completed the book of " + WantsCard + "'s."
      text ,,fn colorWhite
      play(i) = 0
      Points(0) += 1
      fn PrintViewScrollToBottom( fn WindowPrintView(1))
    End If
  Next i
  
End fn

local fn CheckCPUForCompletedBook
  
  For i = 1 To 13
    
    If Computer(i) <> 4
    Else
      text ,,fn colorCyan
      str255 WantsCard
      WantsCard = Mid$(cards,i,1)
      
      if WantsCard = "j" || WantsCard = "J" then WantsCard = "Jack"
      if WantsCard = "q" || WantsCard = "Q" then WantsCard = "Queen"
      if WantsCard = "k" || WantsCard = "K" then WantsCard = "King"
      if WantsCard = "a" || WantsCard = "A" then WantsCard = "Ace"
      if WantsCard = "0" then WantsCard = "10"
      
      Print "CPU completed the book of " + WantsCard + "'s."
      
      text ,,fn colorWhite
      Computer(i) = 0
      Points(1) += 1
      fn PrintViewScrollToBottom( fn WindowPrintView(1))
    End If
  Next i
  
End fn


local fn InitCards
  
  cards = "A234567890JQK"
  RemainingCards = 4 * len$(cards)  // the length of cards is 13. There are 4 suits of cards. so RemainingCards is 52
  i = 0:k = 0:j = 0:CardNumber = 0
  
  For i = 0 to 1
    Points(i) = 0
  next i
  
  For i = 1 TO 13 // Reset each element to 0
    play(i) = 0
    Computer(i) = 0
    deck(i) = 0
    guess(i) = 0
    poss(i) = 0
    asked(i) = 0
    
  NEXT i
  
  
  For i = 1 To 13
    deck(i) = 4
  Next i
  For i = 1 To 9
    fn DealCards
    deck(k) -= 1
    Computer(k) += 1
    fn DealCards
    deck(k) -= 1
    play(k) += 1
  Next i
  
  fn CheckForCompletedBook  // Rich added 5/1/24
  fn CheckCPUForCompletedBook  // Rich added 5/5/24
end fn


local fn QuitOrPlayAlert(GameResult as CFStringRef)
  
  alert -2,,GameResult,@"Game Over",@"Quit;Play Again"
  AlertButtonSetKeyEquivalent( 2, 2, @"\e" )
  short result
  result = alert 2
  if ( result != NSAlertSecondButtonReturn ) then end
  
end fn

local fn QuitOrResumeAlert(GameResult as CFStringRef)
  
  alert -3,,GameResult,@"Quit the game?",@"Quit;Resume game"
  AlertButtonSetKeyEquivalent( 3, 2, @"\e" )
  short result
  result = alert 3
  if ( result != NSAlertSecondButtonReturn ) then end
  
end fn

local fn CheckForEndGame as boolean
  
  bool PlayAgain = _False
  short np = 0, nc = 0
  For i = 1 To 13
    np += play(i)
    nc += Computer(i)
  Next i
  
  If RemainingCards = 0 || np = 0 || nc = 0
    
    text ,,fn colorRed
    Print "*** Game Over! ***"
    Print
    If Points(0) < Points(1)
      Print "The CPU has won."
      print:print
      fn QuitOrPlayAlert(@"the CPU won!")
      PlayAgain = _True
    Else if Points(0) > Points(1)
      Print YourName + " has won."
      print:print
      fn QuitOrPlayAlert(@"You Won!")
      PlayAgain = _True
    Else
      Print "­It's a tie!"
      fn QuitOrPlayAlert(@"It's a tie!.")
      PlayAgain = _True
    End If
    fn PrintViewScrollToBottom( fn WindowPrintView(1) )
    
  End If
  
  PlayAgain = _True
  
End If

End fn = PlayAgain


void local fn Intro
  
  text ,,fn colorGreen
  Print "                __ _     _      "
  Print "  __ _  ___    / _(_)___| |__   "
  Print " /  ` |/ _ \  | |_| / __| //_ \  "
  Print "| (_) | (_) | |  _| \__ \ | | | "
  Print " \__, |\___/  |_| |_|___/_| |_| "
  Print " |___/                          "
  Print ""
  
  text ,,fn colorCyan
  print %(301,90),"( x to exit the game )"
  text ,,fn colorWhite
  
  print " Go Fish Rules:"
  print
  print " You are playing against the CPU."
  print " You are dealt nine cards to start with."
  print " The remaining cards are placed face down in the center of the table"
  print " to form the draw pile (the fish pond)."
  print " On your turn, you ask the CPU For a card."
  print " You must already have at least one card of a given rank to ask For more."
  print " (A rank is one || more of any card.)"
  print " If the CPU has any cards of the named rank, it must hand over all such cards,"
  print " and you can then ask again."
  print " If the CPU has no cards of the named rank, a card will be drawn from the pile,"
  print " and placed in your hand, which then ends your turn."
  print " A book is a collection of four cards in a given rank."
  print " Whenever you complete a book, it will be removed from your hand."
  print " If at any time, your hand is empty, a new card will be drawn from the pile."
  print " The game ends when every book is complete,"
  print " || there are no more cards left in the pile."
  print " The player with the most books wins."
  
  
  CFStringRef UserInput
  "InputYourName"
  UserInput = input % (300, 70), @"What's your name?: "
  if ( UserInput == NULL ) then "InputYourName" // Rich added this 5/1/24
  fn CFStringGetPascalString (UserInput, @YourName, 256, _kCFStringEncodingMacRoman)
  cls
  
  if YourName = "X" || YourName = "x" || YourName = chr$(127) then fn QuitOrResumeAlert(@"EXIT")
  
End fn


local fn WhatCardInputHeight as short
  
  CGRect mainScreenFrame = fn ScreenMainFrame
  float InputHeight = int(mainScreenFrame.size.height - 120)
  
end fn = InputHeight


local fn BuildWindow
  
  // ---> Get the size of the Main Screen. <---
  CGRect mainScreenFrame = fn ScreenMainFrame
  float msh = mainScreenFrame.size.height
  CGRect r = fn CGRectMake( 0, 0, 600, int(msh) - 110)
  
  window 1, @"Go Fish", r
  windowcenter(1)
  WindowSetBackgroundColor(1,fn ColorBlack)
  
end fn

//--- Start ---

fn BuildWindow

fn Intro

fn InitCards

str255 AddTheS
bool RequestCard = _false
short v = 0
short po = 0
boolean ShowHand = _false
str255 WantsCard

"Main"

ShowHand = _false

str255 RequestedCard

While ShowHand = _false
  text ,,fn colorGreen
  Print Chr$(10)  + "Points >> ";
  text ,,fn colorYellow
  print YourName + ": ";
  text ,,fn colorGreen
  print Points(0);
  text ,,fn colorOrange
  print "  CPU: ";
  text ,,fn colorGreen
  print Points(1)
  text ,,fn colorWhite
  Print Chr$(10) + "          " + str$(RemainingCards) + " remaining cards"
  text ,,fn colorWhite
  
  
  /*
  // Uncomment this to see the CPUs cards For testing
  Print Chr$(10) + "CPU Cards:  ";
  For i = 1 To 13
  if Computer(i) <> 0
  For j = 1 To Computer(i)
  if Mid$(cards,i,1) = "0"
  Print @"10"; " ";
  else
  Print Mid$(cards,i,1); " ";
  end if
  Next j
  End If
  Next i
  Print
  fn PrintViewScrollToBottom( fn WindowPrintView(1))
  */
  
  
  Print Chr$(10) + "Your Cards: ";
  For i = 1 To 13
    if play(i) <> 0
      For j = 1 To play(i)
        if Mid$(cards,i,1) = "0"
          Print @"10"; " ";
        else
          Print Mid$(cards,i,1); " ";
        end if
      Next j
    End If
  Next i
  Print
  fn PrintViewScrollToBottom( fn WindowPrintView(1))
  
  RequestCard = _false
  
  While RequestCard = _false
    if fn CheckForEndGame = _True then cls:fn InitCards:goto "Loop"
    
    Someone = YourName
    
    CFStringRef UserInput = 0
    "InputCard"
    UserInput = input % (20, fn WhatCardInputHeight),@"What card do you want? "
    if ( UserInput == NULL ) then "InputCard" // Rich added this 5/1/24
    fn CFStringGetPascalString (UserInput, @RequestedCard, 256, _kCFStringEncodingMacRoman)
    if RequestedCard = "10" then RequestedCard = "0"// card zero is a 10
    text ,,fn ColorYellow
    Print
    
    WantsCard = RequestedCard
    
    if WantsCard = "j" || WantsCard = "J" then WantsCard = "Jack"
    if WantsCard = "q" || WantsCard = "Q" then WantsCard = "Queen"
    if WantsCard = "k" || WantsCard = "K" then WantsCard = "King"
    if WantsCard = "a" || WantsCard = "A" then WantsCard = "Ace"
    if WantsCard = "0" then WantsCard = "10"
    
    print "-------------------------------------"
    print
    str255 AorAn
    AorAn  = "a"
    if WantsCard = "Ace" then AorAn = "an"
    print YourName + " asked For " + AorAn + " " + WantsCard
    print
    text ,,fn ColorWhite
    
    fn PrintViewScrollToBottom( fn WindowPrintView(1))
    
    if RequestedCard = "X" || RequestedCard = "x" then fn QuitOrResumeAlert(@"EXIT")
    
    If RequestedCard <> "" Then CardNumber = Instr$(1,cards, Ucase$(RequestedCard)): RequestCard = _true
    
    If CardNumber = 0
      text,,fn ColorRed
      Print "Sorry, that is not a valid card.": RequestCard = _false
      print
      fn PrintViewScrollToBottom( fn WindowPrintView(1))
    Else if play(CardNumber) = 0 Then text ,,fn colorRed: Print "You don//t have that card!": text ,,fn colorRed: RequestCard = _false
      fn PrintViewScrollToBottom( fn WindowPrintView(1))
      text,,fn ColorWhite
      
    End If
    
  Wend
  
  guess(CardNumber) = 1
  
  If Computer(CardNumber) = 0
    Print Someone + ",";
    text ,,fn colorRed
    Print " Go fish!"
    text ,,fn colorWhite
    Print Someone + " got a";: fn youGoFishing
    print
    fn CheckForCompletedBook
    ShowHand = _true
  Else
    v = Computer(CardNumber)
    Computer(CardNumber) = 0
    play(CardNumber) += v
    if v > 1 then AddTheS = "s" else AddTheS = ""
    Print Someone + " got" + str$(v) + " card" + AddTheS
    fn CheckForCompletedBook
    fn PrintViewScrollToBottom( fn WindowPrintView(1))
    ShowHand = _false
  End If
  
Wend



Someone = "CPU"
For i = 1 To 13
  asked(i) = 0
Next i

bool CPUsTurn = _false

While CPUsTurn = _false
  if fn CheckForEndGame = _True then cls:fn InitCards:goto "Loop"
  
  po = 0
  For i = 1 To 13
    If (Computer(i) > 0) && (guess(i) > 0) Then poss(i) = 1: po += 1
  Next i
  
  short whilecounter
  WhileCounter = 0
  
  
  If po = 0
    // this k is the go fish card.
    
    k = rnd(12) +1
    while Computer(k) = 0 || asked(k)
      whilecounter ++
      k = rnd(12) +1
      if WhileCounter > 100 then k = 0: exit while  //5/5/24  Rich added this to prevent hangs
    wend
    
  Else
    
    k = rnd(12) + 1
    while poss(k) = 0
      k = rnd(12) + 1
      if WhileCounter > 100 then k = 0: exit while //5/5/24  Rich added this to prevent hangs
    wend
    
    guess(k) = 0
    asked(k) = 1
    
  end if
  
  
  if k = 0 then "Loop"  //5/5/24  Rich added this to prevent hangs
  
  WantsCard = fn CheckForFaceCard(k)
  
  if WantsCard = "j" || WantsCard = "J" then WantsCard = "Jack"
  if WantsCard = "q" || WantsCard = "Q" then WantsCard = "Queen"
  if WantsCard = "k" || WantsCard = "K" then WantsCard = "King"
  if WantsCard = "a" || WantsCard = "A" then WantsCard = "Ace"
  if WantsCard = "0" then WantsCard = "10"
  
  text ,,fn ColorOrange
  
  print "-------------------------------------"
  
  
  
  Print:Print Someone + " wants your " + wantsCard + "'s."
  print
  
  text ,,fn ColorWhite
  
  asked(k) = 1
  
  
  If play(k) = 0
    Print Someone + ", ";
    text ,,fn colorRed: Print "go fish!"
    text ,,fn colorWhite:Print Someone + " got";: fn cpuGoFishing
    
    fn CheckCPUForCompletedBook
    CPUsTurn = _true
    
  Else
    
    v = play(k)
    play(k) = 0
    Computer(k) += v
    if v > 1 then AddTheS = "s" else AddTheS = ""
    Print Someone + " got" + str$(v) + " card" + AddTheS
    fn CheckCPUForCompletedBook
    CPUsTurn = _false
  End If
  
  
Wend

"Loop"
goto "Main"

handleevents

Go

See Go Fish/Go

Haskell

See Go Fish/Haskell

Icon and Unicon

See Go Fish/Unicon

J

See Go Fish/J

Java

See Go Fish/Java

Julia

see Go Fish/Julia

Kotlin

See Go Fish/Kotlin

Lua

See Go Fish/Lua

Locomotive Basic

See Go Fish/Locomotive Basic

Mathematica / Wolfram Language

See Go Fish/Mathematica

Nim

See Go Fish/Nim

OCaml

See Go Fish/OCaml

Perl

#!/usr/bin/perl

use strict; # https://rosettacode.org/wiki/Go_Fish
use warnings;
use List::Util qw( first shuffle );

my $pat = qr/[atjqk2-9]/; # ranks
my $deck = join '', shuffle map { my $rank = $_; map "$rank$_", qw( S H C D ) }
  qw( a t j q k ), 2 .. 9;

my $mebooks = my $youbooks = 0;

my $me = substr $deck, 0, 2 * 9, '';
my $mepicks = join '', $me =~ /$pat/g;
arrange($me);
$mebooks++ while $me =~ s/($pat).\1.\1.\1.//;
my $you = substr $deck, 0, 2 * 9, '';
my $youpicks = join '', $you =~ /$pat/g;
arrange($you);
$youbooks++ while $you =~ s/($pat).\1.\1.\1.//;

while( $mebooks + $youbooks < 13 )
  {
  play( \$you, \$youbooks, \$youpicks, \$me, \$mebooks, 1 );
  $mebooks + $youbooks == 13 and last;
  play( \$me, \$mebooks, \$mepicks, \$you, \$youbooks, 0 );
  }
print "me $mebooks you $youbooks\n";

sub arrange { $_[0] = join '', sort $_[0] =~ /../g }

sub human
  {
  my $have = shift =~ s/($pat).\K(?!\1)/ /gr;
  local $| = 1;
  my $pick;
  do
    {
    print "You have $have, enter request: ";
    ($pick) = lc(<STDIN>) =~ /$pat/g;
    } until $pick and $have =~ /$pick/;
  return $pick;
  }

sub play
  {
  my ($me, $mb, $lastpicks, $you, $yb, $human) = @_;
  my $more = 1;
  while( arrange( $$me ), $more and $$mb + $$yb < 13 )
    {
#   use Data::Dump 'dd'; dd \@_, "deck $deck";
    if( $$me =~ s/($pat).\1.\1.\1.// )
      {
      print "book of $&\n";
      $$mb++;
      }
    elsif( $$me )
      {
      my $pick = $human ? do { human($$me) } : do
        {
        my %picks;
        $picks{$_}++ for my @picks = $$me =~ /$pat/g;
        my $pick = first { $picks{$_} } split(//, $$lastpicks), shuffle @picks;
        print "pick $pick\n";
        $$lastpicks =~ s/$pick//g;
        $$lastpicks .= $pick;
        $pick;
        };
      if( $$you =~ s/(?:$pick.)+// )
        {
        $$me .= $&;
        }
      else
        {
        print "GO FISH !!\n";
        $$me .= substr $deck, 0, 2, '';
        $more = 0;
        }
      }
    elsif( $deck )
      {
      $$me .= substr $deck, 0, 2, '';
      }
    else
      {
      $more = 0;
      }
    }
  arrange( $$me );
  }

Phix

See Go Fish/Phix

PicoLisp

See Go Fish/PicoLisp

PowerShell

See Go Fish/PowerShell

PureBasic

See Go Fish/PureBasic

Python

See Go Fish/Python

Raku

(formerly Perl 6) See Go Fish/Raku

Red

Red [
    Title:  "Go Fish"
    Author: "gltewalt"
]
 
chand: []   ;-- c and p = computer and player
cguesses: []
phand: []
cbooks: 0
pbooks: 0
gf: {
    ***************
    *   GO FISH   *
    ***************
}
pip: ["a" "2" "3" "4" "5" "6" "7" "8" "9" "10" "j" "q" "k"] ;-- suits are not relevant
pile: []    ;-- where discarded cards go
 
;---------------------
;  Helper functions  -                                           
;---------------------
 
clear-screen: does [
    "clears the console"
    call/console either system/platform = 'Linux ["clear"]["cls"]
]
 
clear-and-show: func [duration str][
    {
        Poor persons animation. 
        Blips message to screen after a pause of duration length.

    }
    clear-screen
    print str 
    wait duration 
    clear-screen
]
 
deal-cards: func [num hand][ 
    loop num [
        append hand rejoin [trim/all form take deck]
    ] 
]
 
find-in: func [blk str][
    "Finds a string value in a block. Series in series."
    foreach i blk [if find i str [return i]]
]
 
go-fish: func [num hand][
    either not empty? deck [
        deal-cards num hand
    ][
        append hand rejoin [trim/all form take pile]    ;-- take from pile if deck is empty
    ]
]
 
guess-from: func [hand guessed][
    {
        Randomly picks from hand minus guessed.

        Simulates a person asking for different cards on
        their next turn if their previous guess resulted
        in a Go Fish.
    }
    random/seed now/time
    either any [empty? guessed empty? exclude hand guessed][
        random/only hand 
    ][
        random/only exclude hand guessed
    ]
]
 
make-deck: function [] [
    "make-deck and shuffle from https://rosettacode.org/wiki/Playing_cards#Red"
     new-deck: make block! 52
     foreach p pip [loop 4 [append/only new-deck p]]
     return new-deck
]

show-cards: does [
    clear-and-show 0 ""
    print [newline "Player cards:" newline sort phand newline]
    print ["Computer books:" cbooks]
    print ["Player books:" pbooks newline]
]

shuffle: function [deck [block!]] [deck: random deck]
 
;------------- end of helper functions -----------------

check-for-books: func [
    {
        Checks for a book in a players hand.
        Increments the players book score, and
        discards the book from the players hand
    }
    hand "from or to hand"
    kind "rank of cards"
    /local 
        c "collected"
][
    c: collect [
        forall hand [keep find hand/1 kind]
    ]
    remove-each i c [none = i] 
    if 4 = length? c [
        either hand = phand [pbooks: pbooks + 1][cbooks: cbooks + 1]
        remove-each i hand [if find/only c i [i]]   ;-- remove book from hand
        forall c [append pile c/1]  ;-- append discarded book to the pile
    ]
]
 
transfer-cards: func [
    "Transfers cards from player to player"
    fhand "from hand"
    thand "to hand"
    kind "rank of cards"
    /local 
        c "collected"
][
    c: collect [forall fhand [keep find fhand/1 kind]]
    remove-each i c [none = i]  ;-- remove none values from collected
    forall c [append thand c/1] ;-- append remaining values to "to hand"
    remove-each i fhand [if find/only c i [i]] ;-- remove those values from "from hand"
]
 
computer-turn: func [
    fhand "from hand"
    thand "to hand"
    kind  "rank of cards"
    /local 
        a 
][
    a: ask rejoin ["Do you have any " kind " s? "]
    if a = "x" [halt]
    either any [a = "y" a = "yes"][
        check-for-books thand kind
        transfer-cards fhand thand kind
        show-cards
        computer-turn fhand thand guess-from thand cguesses
    ][  
        clear-and-show 0.4 gf 
        go-fish 1 thand   
        append cguesses kind 
    ]
]
 
player-turn: func [
    fhand "from hand"
    thand "to hand"
    kind  "rank of cards"
    /local
        p 
][
    if empty? fhand [go-fish 3 fhand]

    if none? find-in thand kind [   ;-- player has to hold rank asked for
        clear-and-show 1.0 
        "You have to have that rank in your hand to ask for it.^/Computers turn..."
        exit
    ]

    either find-in fhand kind [
        check-for-books thand kind
        transfer-cards fhand thand kind 
        show-cards
        if find-in thand kind [ 
            p: ask "Your guess: "
            either p = "x" [halt][player-turn fhand thand p]
            check-for-books thand p 
        ]
    ][
        clear-and-show 0.4 gf 
        go-fish 1 thand 
    ]
]
 
game-round: has [c p][
    print {
          -------------------
          -  COMPUTER TURN  -
          -------------------
          }
 
    if empty? chand [  ; computer has no more cards? fish 3 cards.
        go-fish 3 chand
        show-cards
    ]
    computer-turn phand chand c: guess-from chand cguesses
    check-for-books chand c
    show-cards
 
    print {
          -------------------
          -   PLAYER TURN   -
          -------------------
          }

    if empty? phand [   ;-- player has no more cards? fish 3 cards.
        go-fish 3 phand
        show-cards
    ]
    p: ask "Your guess: "
    either p = "x" [halt][player-turn chand phand find-in phand p]
    check-for-books phand p 
    show-cards
]
 
main: does [
    deck: shuffle make-deck
    deal-cards 9 chand
    deal-cards 9 phand
    show-cards
    while [cbooks + pbooks < 13][
        game-round  
    ]
    clear-and-show 0 ""
    print "GAME OVER" 
    print [newline "Computer books:" cbooks newline "Player books:" pbooks]
]

main

Rust

See Go Fish/Rust

Ruby

See Go Fish/Ruby

Tcl

See Go Fish/Tcl

V (Vlang)

See Go Fish/V (Vlang)

Wren

See Go Fish/Wren