Go Fish/Phix

From Rosetta Code
Revision as of 01:15, 21 September 2018 by Petelomax (talk | contribs)
Translation of: Ruby
Go Fish/Phix is part of Go Fish. You may find other members of Go Fish at Category:Go Fish.

<lang Phix>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()</lang>