I'm working on modernizing Rosetta Code's infrastructure. Starting with communications. Please accept this time-limited open invite to RC's Slack.. --Michael Mol (talk) 20:59, 30 May 2020 (UTC)

Go Fish/Lua

From Rosetta Code
Go Fish/Lua is part of Go_Fish. You may find other members of Go_Fish at Category:Go_Fish.

Library: LÖVE
[edit]

LÖVE (Love2D) v.11.3 solution. There are no card suit characters in the standard font, so suits are A, B, C and D, but ranks are 1 to 13.

 
function createDeck ()
Deck = {}
for iSuit = 1, #CardSuits do
for iRank = 1, #CardRanks do
local card = {
iSuit = iSuit,
iRank = iRank,
suit = CardSuits[iSuit],
rank = CardRanks[iRank],
name = CardSuits[iSuit]..'-'..CardRanks[iRank]
}
local i = math.random (#Deck + 1)
table.insert (Deck, i, card)
end
end
end
 
function createPlayers (nPlayers)
Players = {}
for index = 1, nPlayers do
local player = {
cards = {},
moves = {},
score = 0,
index = index,
}
table.insert (Players, player)
end
end
 
function dealPlayers ()
local nCards = 7
if #Players > 2 then nCards = 5 end
 
for i = 1, nCards do
for iPlayer = 1, #Players do
-- player takes a card from deck
takeCardFromDeck (Players[iPlayer])
end
end
end
 
function isKeyValueInList (key, value, list)
for index, element in ipairs (list) do
if element[key] == value then
return element, index
end
end
return false
end
 
function updateMoves ()
for iPlayer = 1, #Players do
local player = Players[iPlayer]
 
local moves = {}
for iCard = 1, #player.cards do
local rank = player.cards[iCard].rank
local move = isKeyValueInList ("rank", rank, moves)
if not move then
move = {rank = rank, amount = 1}
table.insert (moves, move)
else
move.amount = move.amount + 1
end
end
player.moves = moves
end
end
 
function takeCardFromDeck (player)
if #Deck > 0 then
-- player takes last card from deck
table.insert (player.cards, table.remove(Deck, #Deck))
return true
end
return false
end
 
function updateGUI ()
local player = Players[1]
local nMoves = #player.moves
local x1, y1 = 100, 450
local cardW = 50
local cardH = 30
 
GUI = {}
local totalCards = 0
totalCards = totalCards + #Deck
for iPlayer = 1, #Players do
local player = Players [iPlayer]
totalCards = totalCards + #player.cards
end
if totalCards == 0 then
GameOver = true
table.insert (Statistics, 1, "Game Over")
local winners = {}
local max = 0
for iPlayer = 1, #Players do
local player = Players [iPlayer]
if player.score > max then
winners = {player.index}
max = player.score
elseif player.score == max then
table.insert (winners, player.index)
end
end
if #winners == 1 then
table.insert (Statistics, 1, "Player " .. winners[1] .. ' wins the game')
elseif #winners > 1 then
table.insert (Statistics, 1, "Players " .. table.concat(winners, ', ') .. ' win the game')
end
end
 
if #Players > 2 then
GUI.selectedPlayer = nil
local amount, last = 0, nil
for iPlayer = 2, #Players do
local player = Players [iPlayer]
if #player.cards > 0 then
local x = x1 + cardW*(iPlayer-2)
local y = y1
local button = {
x=x,
y=y,
w=cardW,
h=cardH,
text = 'Player' .. iPlayer,
iPlayer = iPlayer,
playerButton = true,
}
table.insert (GUI, button)
amount = amount + 1
last = player
end
end
if amount == 1 then
-- only one player
GUI.selectedPlayer = last
end
else
GUI.selectedPlayer = 2
end
for iMove, move in ipairs (player.moves) do
local x = x1 + cardW*(iMove-1)
local y = y1 + cardH
local button = {
x=x,
y=y,
w=cardW,
h=cardH,
text = move.rank .. ' (' .. move.amount..')',
rank = move.rank,
moveButton = true,
}
table.insert (GUI, button)
end
end
 
function love.load()
love.window.setTitle( 'Go Fish' )
Statistics = {}
CardSuits = {"A", "B", "C", "D"}
CardRanks = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13"}
NPlayers = 6
Hidden = true
 
-- new game
createDeck ()
createPlayers (NPlayers)
dealPlayers ()
 
updateMoves ()
updateGUI ()
end
 
function love.draw()
if not Hidden then
for i, t in ipairs (Deck) do
love.graphics.print (t.name, 0, i*11)
end
end
for iPlayer, player in ipairs (Players) do
local x = 60 * iPlayer
local y = 0
love.graphics.print ('player '..iPlayer, x, y)
y = y + 14
love.graphics.print ('score: '.. player.score, x, y)
if not Hidden or (iPlayer == 1) then
for iCard, card in ipairs (player.cards) do
y = y + 14
love.graphics.print (card.name, x, y)
end
end
if not Hidden then
y = y + 14
for iMove, move in ipairs (player.moves) do
y = y + 14
love.graphics.print (move.rank .. ' ' .. move.amount, x, y)
end
end
end
 
-- draw first player moves
for iGUI, button in ipairs (GUI) do
-- print (GUI.selectedPlayer or '', button.iPlayer or '')
if GUI.hoveredMove and (GUI.hoveredMove == iGUI) or
GUI.selectedPlayer and button.iPlayer and (GUI.selectedPlayer.index == button.iPlayer) then
love.graphics.setColor (0.3,0.3,0.3)
love.graphics.rectangle ('fill', button.x, button.y, button.w, button.h)
end
love.graphics.setColor (1,1,1)
love.graphics.rectangle ('line', button.x, button.y, button.w, button.h)
love.graphics.printf(button.text, button.x, button.y, button.w, 'center', 0, 1, 1, 0, -math.floor(button.h/4))
end
for i = 1, math.min(30, #Statistics) do
local str = Statistics[i]
love.graphics.print (str, 430, 20+(i-1)*14)
end
end
 
function love.keypressed(key, scancode, isrepeat)
if false then
elseif key == "h" then
Hidden = not Hidden
elseif key == "escape" then
love.event.quit()
end
end
 
function love.mousemoved( x, y, dx, dy, istouch )
GUI.hoveredMove = nil
for iGUI, button in ipairs (GUI) do
if x > button.x and x < button.x + button.w and y > button.y and y < button.y + button.h then
GUI.hoveredMove = iGUI
end
end
end
 
function checkHandCards (player)
local sets = {}
for iCard = 1, #player.cards do
local rank = player.cards[iCard].rank
local set = isKeyValueInList ("rank", rank, sets)
if not set then
set = {rank = rank, amount = 1}
table.insert (sets, set)
else
set.amount = set.amount + 1
end
end
for iSet = 1, #sets do
local set = sets[iSet]
if set.amount == 4 then -- full set
local rank = set.rank
 
table.insert (Statistics, 1, "Player "..player.index .. ' has set of "' .. rank .. '"')
-- remove cards
for iCard = #player.cards, 1, -1 do -- backwards
local card = player.cards[iCard]
if card.rank == rank then
table.remove (player.cards, iCard)
end
end
player.score = player.score + 1
end
end
end
 
function fillHandCards (player)
 
if #player.cards == 0 and #Deck > 0 then
local nCards = math.min(5, #Deck)
if nCards == 1 then
table.insert (Statistics, 1, "Player ".. player.index .. ' takes one card from deck')
else
table.insert (Statistics, 1, "Player ".. player.index .. ' takes '.. nCards ..' cards from deck')
end
for i = 1, nCards do
takeCardFromDeck (player)
end
end
end
 
function doMove (activePlayer, selectedPlayer, rank)
local takenCards = 0
for iCard = #selectedPlayer.cards, 1, -1 do -- iterate backwards
local card = selectedPlayer.cards[iCard]
if card.rank == rank then
table.insert (activePlayer.cards, table.remove (selectedPlayer.cards, iCard))
takenCards = takenCards + 1
end
end
if takenCards > 0 then
if takenCards == 1 then
table.insert (Statistics, 1,
"Player "..activePlayer.index .. ' asks the Player '.. selectedPlayer.index .. ' for card "'.. rank ..
'" and get one card.')
else
table.insert (Statistics, 1,
"Player "..activePlayer.index .. ' asks the Player '.. selectedPlayer.index .. ' for card "'.. rank ..
'" and get ' .. takenCards .. ' cards.')
end
checkHandCards (activePlayer)
fillHandCards (activePlayer)
fillHandCards (selectedPlayer)
updateMoves ()
if #activePlayer.cards > 0 then
-- repeat the turn
return true
else
return false
end
else
table.insert (Statistics, 1,
"Player "..activePlayer.index .. ' asks the Player '.. selectedPlayer.index .. ' for card "'.. rank ..
'", but he has no one.')
if takeCardFromDeck (activePlayer) then
table.insert (Statistics, 1, "Player ".. activePlayer.index .. ' takes one card from deck')
end
checkHandCards (activePlayer)
-- next player turn
return false
end
 
end
 
 
function AI_moves ()
for iPlayer = 2, #Players do
local activePlayer = Players[iPlayer]
if #activePlayer.moves > 0 then
local iMove = math.random (#activePlayer.moves)
local rank = activePlayer.moves[iMove].rank
local activePlayers = {}
for iPlayer, player in ipairs (Players) do
if #player.cards > 0 and not (activePlayer == player) then
table.insert (activePlayers, player)
end
end
local selectedPlayer = activePlayers[math.random (#activePlayers)]
while doMove (activePlayer, selectedPlayer, rank) do
activePlayers = {}
for iPlayer, player in ipairs (Players) do
if #player.cards > 0 and not (activePlayer == player) then
table.insert (activePlayers, player)
end
end
iMove = math.random (#activePlayer.moves)
rank = activePlayer.moves[iMove].rank
end
else
table.insert (Statistics, 1, "Player ".. activePlayer.index .. ' has no cards')
end
end
end
 
function love.mousereleased( x, y, button, istouch, presses )
if GameOver then
GameOver = false
 
createDeck ()
createPlayers (NPlayers)
dealPlayers ()
 
updateMoves ()
updateGUI ()
elseif GUI.hoveredMove then
local button = GUI[GUI.hoveredMove]
 
if button.playerButton then
GUI.selectedPlayer = Players[button.iPlayer]
table.insert (Statistics, 1, 'Selected Player: ' .. button.iPlayer)
elseif button.moveButton then
-- check if the player has these cards
GUI.hoveredMove = nil
if GUI.selectedPlayer then -- no SelectedPlayer means no move
if doMove (Players[1], GUI.selectedPlayer, button.rank) then
-- player has another move
else
-- other players
AI_moves ()
end
updateGUI ()
end
 
end
elseif #Players[1].cards == 0 then
-- player has no cards
table.insert (Statistics, 1, 'Player 1 has no cards;')
AI_moves ()
updateGUI ()
end
end