Last active
February 28, 2017 22:47
-
-
Save stravant/859c2f52d26b637ab75c0e0f20151c42 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ | |
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