Skip to content

Instantly share code, notes, and snippets.

@stravant
Last active February 28, 2017 22:47
Show Gist options
  • Save stravant/859c2f52d26b637ab75c0e0f20151c42 to your computer and use it in GitHub Desktop.
Save stravant/859c2f52d26b637ab75c0e0f20151c42 to your computer and use it in GitHub Desktop.
--[[
Flow:
-
]]
local WistServer = {}
function WistServer.new(players)
local this = {}
local mPlayers = players --[[{
isAI -- AI or player?
id -- Index into player list
name -- visual name for player
player / ai -- Which Roblox player or ai implementation
missedAction -- Did the player let the AI perform their last action?
points -- Current number of points
bid -- How much did they bid
tricks -- How many tricks have they gotten?
hand -- Cards in hand
lastThrow -- What was the last card they played out
onBecomeAI -- event fired when the player drops
onThrowCard -- event fired when they throw a card
onBid -- submitted a bid
}]]
local mOut = nil;
this.Send = Signal.new()
this.Recv = Signal.new()
local function eachPlayerFrom(player)
end
local function eachPlayer()
end
local function sendAll(packet)
for player in eachPlayer() do
if not player.isAI then
this.Send:fire(player.player, packet)
end
end
end
local function becomeAI(player)
player.isAI = true
player.onBecomeAI:fire()
sendAll{
msg = 'becomeAI';
player = player.id;
}
end
local function tryBecomeAI(player)
if player.missedAction then
becomeAI(player)
else
player.missedAction = true
end
end
local function cancelBecomeAI(player)
player.missedAction = false
end
-- Generate the deck
local DECK = {} do
local suit = {'s', 'h', 'c', 'd'}
local cardMT = {
__tostring = function(card)
return {suit[card[1]] .. card[2]}
end;
}
for i = 1, 13 do
for j = 1, 4 do
table.insert(DECK, setmetatable({j, i}, cardMT))
end
end
end
local function getBidForHuman(player)
local index, args = WaitAny(
player.onBid,
player.onBecomeAI,
TimeoutEvent(10))
if index == 1 then
if type(args[1]) == 'number' then
player.bid = args[1]
cancelBecomeAI(player)
else
print("ERROR: Bad bid type: " .. type(args[1]))
tryBecomeAI(player)
return false
end
else
tryBecomeAI(player)
return false
end
end
local function getBidForAI(player)
-- Just bid an even fraction of the total
player.bid = math.ceil(#player.hand / #mPlayers)
end
local function getBidFor(player)
if player.isAI or not getBidForHuman(player) then
getBidForAI(player)
end
sendAll{
msg = 'bid';
player = player.id;
data = player.bid;
}
end
-- Get each player's bid and let the players know about it
local function getBids()
for _, player in pairs(mPlayers) do
getBidFor(player)
sendBidFor(player)
end
end
-- Shuffle an array (Fisher-Yates)
local function shuffle(arr)
for i = 1, #arr do
local j = math.random(i, #arr)
arr[i], arr[j] = arr[j], arr[i]
end
end
-- Deal out the hands and tell each player their hand
local function dealHands(totalCards)
-- Shuffle the deck
shuffle(DECK)
-- Deal out cards
local deckIndex = 1
for player in eachPlayer() do
player.hand = {}
for i = 1, totalCards do
table.insert(player.hand, DECK[deckIndex])
deckIndex = deckIndex + 1
end
-- Network send
if not player.isAI then
this.Send:fire(player.player, {
msg = 'hand';
data = player.hand;
})
end
end
end
local function checkThrow(player, card)
if mOut then
local hasMatch = false
for _, card in pairs(player.hand) do
if card[1] == mOut[1] then
hasMatch = true
break
end
end
return not hasMatch or card[1] == mOut[1]
else
mOut = card
return true
end
end
-- AI logic to determine card to throw
local function aiDoThrow(ai)
-- Just throw the first card for now
-- TODO: AI Logic
local card = ai.hand[1]
table.remove(ai.hand, 1)
ai.throw = card
checkThrow(ai, ai.throw)
end
-- Is an object a valid card
local function isValidCard(obj)
return obj[1] and obj[2] and type(obj[1]) == 'number' and type(obj[2]) == 'number'
end
-- Player logic to determine card to throw
local function playerDoThrow(player)
local index, args = WaitAny(
player.onThrowCard,
player.onBecomeAI,
TimeoutEvent(10))
if index == 1 then
local throw = args[1]
if isValidCard(throw) and checkThrow(player, throw) then
for i, card in pairs(player.hand) do
if card[1] == throw[1] && card[2] == throw[2] then
table.remove(player.hand, i)
player.throw = throw
cancelBecomeAI(player)
return true
end
end
end
print("ERROR: Invalid throw")
tryBecomeAI(player)
return false
else
tryBecomeAI(player)
return false
end
end
-- Get card to throw, throw it, update lastThrow and tell the other players
local function throwCard(player)
if player.isAI or not playerDoThrow(player) then
aiDoThrow(player)
end
sendAll{
msg = 'throw';
player = player.id;
data = player.throw;
}
end
-- Use lastThrow to determine who won the trick and tell the players
local function winTrick()
-- Find winner
local winner = nil
local bestCard = {0, 0}
for player in eachPlayer() do
local thrown = player.throw
if thrown[1] == 1 and bestCard[1] ~= 1 then
bestCard = throw
winner = player
elseif thrown[1] == bestCard[1] and thrown[2] > bestCard[2] then
bestCard = throw
winner = player
end
end
-- Add trick
winner.tricks = winner.tricks + 1
end
-- Declare winner for round and award points, send
local function awardRoundPoints()
for player in eachPlayer() do
if player.tricks == player.bid then
player.points = player.points + 10 + player.bid
end
end
end
local function playTrick(toPlayOut)
mOut = nil
for player in eachPlayerFrom(toPlayOut) do
throwCard(player)
end
winTrick()
end
local function playRound(totalCards)
sendAll{
msg = 'start';
}
dealHands(totalCards)
getBids()
for player in eachPlayer() do
player.tricks = 0
end
local nextToPlayOut = mPlayers[1]
for i = 1, totalCards do
nextToPlayOut = playTrick(nextToPlayOut)
end
awardRoundPoints()
end
local function getMatchWinner()
local winningPoints = -1
for player in eachPlayer() do
if player.points > winningPoints then
winningPoints = player.points
end
end
local winners = {}
for player in eachPlayer() do
if player.points == winningPoints then
table.insert(winners, player)
end
end
return winners
end
local function getPlayer(rbxPlayer)
for player in eachPlayer() do
if player.player == rbxPlayer then
return player
end
end
return nil
end
this.Recv:connect(function(sourcePlayer, packet)
local player;
for _, myPlayer in eachPlayer() do
if myPlayer.player == sourcePlayer then
player = myPlayer
break
end
end
if not player then
print("ERROR: Packet from player not in game")
return
end
if packet.msg == 'throw' then
player.onThrowCard:fire(packet.data)
elseif packet.msg == 'bid' then
player.onBid:fire(packet.data)
else
error("ERROR: Unreckognized message: " .. packet.msg)
end
end)
function this:main()
FastSpawn(function()
playRound(13)
local winners = getMatchWinner()
-- TODO: Process winners
mTimeOutSig:disconnect()
end)
end
mTimeOutSig = game.Players.ChildRemoved:connect(function(player)
for myPlayer in eachPlayer() do
if myPlayer.player == player then
becomeAI(myPlayer)
end
end
end)
return this
end
return WistServer
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment