]> Pileus Git - ~andy/rhawk/blobdiff - spades.awk
Fix broken playto scores
[~andy/rhawk] / spades.awk
index 3eeab902d350454fdd95d37a05ce53afa3b8fb22..8475bf9036fece6785b410f4f4e8253018ba71bf 100644 (file)
@@ -1,11 +1,9 @@
+# For saving
+@include "json.awk"
+
 # Functions
 function sp_init(cards, tmp0, tmp1)
 {
-       sp_valid    = 0     #     Message sent from sp_player
-       sp_player   = ""    #     Who's turn it is
-       sp_turn     = 0     #     Index of who's turn it is
-       delete sp_hands     # [p] Each players cards
-
        # Init deck
        cards ="As Ks Qs Js 10s 9s 8s 7s 6s 5s 4s 3s 2s "\
               "Ah Kh Qh Jh 10h 9h 8h 7h 6h 5h 4h 3h 2h "\
@@ -18,6 +16,12 @@ function sp_init(cards, tmp0, tmp1)
 
 function sp_reset(type)
 {
+       # Per message
+       if (type <  0) {
+               sp_from     = ""    #    The speakers player name
+               sp_valid    = ""    #    It is the speaker turn
+       }
+
        # Per hand
        if (type >= 0) {
                sp_suit     = ""    #     The lead suit {s,h,d,c}
@@ -43,17 +47,100 @@ function sp_reset(type)
                sp_owner    = ""    #     Who started the game
                sp_playto   = 0     #     Score the game will go to
                sp_dealer   =-1     #     Who is dealing this round
+               sp_turn     = 0     #     Index of who's turn it is
+               sp_player   = ""    #     Who's turn it is
+               sp_limit    = 10    #     Bag out limit
+               delete sp_hands     # [p] Each players cards
                delete sp_players   # [p] Player names players["name"] -> i
+               delete sp_auths     # [c] Player auth names auths["auth"] -> "name"
                delete sp_order     # [i] Player order order[i] -> "name"
                delete sp_scores    # [i] Teams score
        }
 }
 
+function sp_acopy(dst, src,    key)
+{
+       if (isarray(src))
+               for (key in src)
+                       json_copy(dst, key, src[key])
+}
+
+function sp_save(file, game)
+{
+       # Per hand
+       game["suit"]    = sp_suit;
+       game["piles"]   = sp_piles;
+       json_copy(game, "pile",    sp_pile);
+
+       # Per round
+       game["state"]   = sp_state;
+       game["broken"]  = sp_broken;
+       json_copy(game, "looked",  sp_looked);
+       json_copy(game, "bids",    sp_bids);
+       json_copy(game, "nil",     sp_nil);
+       json_copy(game, "pass",    sp_pass);
+       json_copy(game, "tricks",  sp_tricks);
+
+       # Per game
+       game["channel"] = sp_channel;
+       game["owner"]   = sp_owner;
+       game["playto"]  = sp_playto;
+       game["dealer"]  = sp_dealer;
+       game["turn"]    = sp_turn;
+       game["player"]  = sp_player;
+       game["limit"]   = sp_limit;
+       json_copy(game, "hands",   sp_hands);
+       json_copy(game, "players", sp_players);
+       json_copy(game, "auths",   sp_auths);
+       json_copy(game, "order",   sp_order);
+       json_copy(game, "scores",  sp_scores);
+
+       # Save
+       json_save(file, game);
+}
+
+function sp_load(file, game)
+{
+       # Load
+       if (!json_load(file, game))
+               return
+
+       # Per hand
+       sp_suit    = game["suit"];
+       sp_piles   = game["piles"];
+       sp_acopy(sp_pile,    game["pile"]);
+
+       # Per round
+       sp_state   = game["state"];
+       sp_broken  = game["broken"];
+       sp_acopy(sp_looked,  game["looked"]);
+       sp_acopy(sp_bids,    game["bids"]);
+       sp_acopy(sp_nil,     game["nil"]);
+       sp_acopy(sp_pass,    game["pass"]);
+       sp_acopy(sp_tricks,  game["tricks"]);
+
+       # Per game
+       sp_channel = game["channel"];
+       sp_owner   = game["owner"];
+       sp_playto  = game["playto"];
+       sp_dealer  = game["dealer"];
+       sp_turn    = game["turn"];
+       sp_player  = game["player"];
+       sp_limit   = game["limit"];
+       sp_acopy(sp_hands,   game["hands"]);
+       sp_acopy(sp_players, game["players"]);
+       sp_acopy(sp_auths,   game["auths"]);
+       sp_acopy(sp_order,   game["order"]);
+       sp_acopy(sp_scores,  game["scores"]);
+}
+
 function sp_pretty(cards, who)
 {
-       if (!plain[who]) {
+       if (!nocolor[who]) {
                gsub(/[0-9JQKA]*[sc]/, "\0031,00\002&\017", cards) # black
                gsub(/[0-9JQKA]*[hd]/, "\0034,00\002&\017", cards) # red
+       }
+       if (!nounicode[who]) {
                gsub(/s/, "\002♠", cards)
                gsub(/h/, "\002♥", cards)
                gsub(/d/, "\002♦", cards)
@@ -66,7 +153,8 @@ function sp_next(who, prev)
 {
        prev      = sp_turn
        sp_turn   = who ? sp_players[who] : (sp_turn + 1) % 4
-       sp_player = sp_order[sp_turn]
+       if (length(sp_order) == 4)
+               sp_player = sp_order[sp_turn]
        return prev
 }
 
@@ -83,13 +171,13 @@ function sp_deal(  shuf)
        say("Bidding starts with " sp_player "!")
 }
 
-function sp_hand(who,  sort, str)
+function sp_hand(to, who,      sort, str)
 {
        asorti(sp_hands[who], sort, "sp_csort")
        for (i=0; i<length(sort); i++)
                str = str "" sprintf("%4s", sort[i])
        gsub(/^ +| +$/, "", str)
-       return sp_pretty(str, who)
+       return sp_pretty(str, to)
 }
 
 function sp_hasa(who, expr)
@@ -133,27 +221,46 @@ function sp_team(i)
 
 function sp_bags(i,    bags)
 {
-       bags = sp_scores[i] % 10
+       bags = sp_scores[i] % sp_limit
        if (bags < 0)
-               bags += 10
+               bags += sp_limit
        return bags
 }
 
-function sp_score(     bids, tricks)
+function sp_bidders(   i, turn, bid, bids)
+{
+       for (i = 0; i < 4; i++) {
+               turn = (sp_dealer + i) % 4
+               if (sp_bids[turn] && !sp_nil[turn])
+                       bid  = sp_order[turn] ":" sp_bids[turn]
+               else if (sp_nil[turn] == 1)
+                       bid  = sp_order[turn] ":" "nil"
+               else if (sp_nil[turn] == 2)
+                       bid  = sp_order[turn] ":" "blind"
+               else
+                       continue
+               bids = bids " " bid
+       }
+       gsub(/^ +| +$/, "", bids)
+       return bids
+}
+
+function sp_score(     bids, times, tricks)
 {
        for (i=0; i<2; i++) {
                bids   = sp_bids[i]   + sp_bids[i+2]
                tricks = sp_tricks[i] + sp_tricks[i+2]
                bags   = tricks - bids
-               if (sp_bags(i) + bags >= 10) {
-                       say(sp_team(i) " bag out")
-                       sp_scores[i] -= 100
+               times  = int((sp_bags(i) + bags) / sp_limit)
+               if (times > 0) {
+                       say(sp_team(i) " bag" (times>1?" way ":" ") "out")
+                       sp_scores[i] -= sp_limit * 10 * times;
                }
                if (tricks >= bids) {
-                       say(sp_team(i) " make their bid")
+                       say(sp_team(i) " make their bid: " tricks "/" bids)
                        sp_scores[i] += bids*10 + bags;
                } else {
-                       say(sp_team(i) " go bust")
+                       say(sp_team(i) " go bust: " tricks "/" bids)
                        sp_scores[i] -= bids*10;
                }
        }
@@ -173,7 +280,7 @@ function sp_score(  bids, tricks)
 
 function sp_play(card, winner, pi)
 {
-       delete sp_hands[FROM][card]
+       delete sp_hands[sp_from][card]
        sp_pile[card] = sp_player
        sp_piles      = sp_piles (sp_piles?",":"") card
        sp_next()
@@ -206,12 +313,10 @@ function sp_play(card,    winner, pi)
                        say("Game over!")
                        winner = sp_scores[0] > sp_scores[1] ? 0 : 1
                        looser = !winner
-                       say(sp_team(winner) " wins the game " \
+                       say(CHANNEL, sp_team(winner) " wins the game " \
                            sp_scores[winner] " to " sp_scores[looser])
-                       say(sp_order[winner+0] "++")
-                       say(sp_order[winner+2] "++")
-                       say(sp_order[looser+0] "--")
-                       say(sp_order[looser+2] "--")
+                       say(CHANNEL, sp_order[winner+0] "++")
+                       say(CHANNEL, sp_order[winner+2] "++")
                        sp_reset(2)
 
                } else {
@@ -232,23 +337,42 @@ BEGIN {
        srand(seed)
        sp_init()
        sp_reset(2)
+       sp_load("var/sp_cur.json");
+       #if (sp_channel)
+       #       say(sp_channel, "Game restored.")
 }
 
 // {
-       sp_valid = (FROM && FROM == sp_player)
+       sp_from  = AUTH in sp_auths ? sp_auths[AUTH] : FROM
+       sp_valid = sp_from && sp_from == sp_player
 }
 
+CMD == "PRIVMSG" &&
 ! /help/ &&
 /[Ss]pades/ {
        say("Spades! " sp_pretty("As,Ah,Ad,Ac", FROM))
 }
 
+AUTH == OWNER &&
+/^\.savegame/ {
+       sp_save("var/sp_save.json");
+       say("Game saved.")
+}
+
+AUTH == OWNER &&
+/^\.loadgame/ {
+       sp_load("var/sp_save.json");
+       say("Game loaded.")
+}
+
 # Help
 /^\.help [Ss]pades$/ {
        say("Spades -- play a game of spades")
        say("Examples:")
        say(".newgame [score] -- start a game to <score> points, default 500")
        say(".endgame -- abort the current game")
+       say(".savegame -- save the current game to disk")
+       say(".loadgame -- load the previously saved game")
        say(".join -- join the current game")
        say(".look -- look at your cards")
        say(".bid n -- bid for <n> tricks")
@@ -260,12 +384,22 @@ BEGIN {
 }
 
 # Debugging
-FROM == OWNER &&
+AUTH == OWNER &&
 /^\.deal (\w+) (.*)/ {
+       say(sp_channel, FROM " is cheating for " $2)
        delete sp_hands[$2]
        for (i=3; i<=NF; i++)
                sp_hands[$2][$i] = 1
-       privmsg(sp_channel, FROM " is cheating for " $2)
+       next
+}
+
+AUTH == OWNER &&
+sp_state == "play" &&
+/^\.force (\w+) (\S+)$/ {
+       say(sp_channel, FROM " is cheating for " $2)
+       sp_from = $2
+       sp_play($3)
+       next
 }
 
 
@@ -274,22 +408,23 @@ FROM == OWNER &&
        if (sp_state != "new") {
                reply("There is already a game in progress.")
        } else {
+               $1         = ".join"
                sp_owner   = FROM
                sp_playto  = $2 ? $2 : 200
+               sp_limit   = sp_playto > 200 ? 10 : 5;
                sp_state   = "join"
                sp_channel = DST
-               say(sp_owner " starts a game of Spades to " sp_playto "!")
-               #privmsg("#rhnoise", sp_owner " starts a game of Spades in " DST "!")
+               say(sp_owner " starts a game of Spades to " sp_playto " with " sp_limit " bags!")
        }
 }
 
-(FROM == sp_owner || FROM == OWNER) &&
+(sp_from == sp_owner || AUTH == OWNER) &&
 /^\.endgame$/ {
        if (sp_state == "new") {
                reply("There is no game in progress.")
        } else {
                say(FROM " ends the game")
-               sp_reset(5)
+               sp_reset(2)
        }
 }
 
@@ -300,13 +435,15 @@ FROM == OWNER &&
        else if (sp_state == "play") {
                reply("The game has already started")
        }
-       else if (sp_state == "join" && FROM in sp_players) {
+       else if (sp_state == "join" && sp_from in sp_players) {
                reply("You are already playing")
        }
        else if (sp_state == "join") {
                i = sp_next()
-               sp_order[i] = FROM
                sp_players[FROM] = i
+               if (AUTH)
+                       sp_auths[AUTH] = FROM
+               sp_order[i] = FROM
                say(FROM " joins the game!")
        }
        if (sp_state == "join" && sp_turn == 0)
@@ -315,8 +452,8 @@ FROM == OWNER &&
 
 !sp_valid &&
 (sp_state "bid" || sp_state == "play") &&
-/^\.(bid|play)\>$/ {
-       if (FROM in sp_players)
+/^\.(bid|play)\>/ {
+       if (sp_from in sp_players)
                say(".slap " FROM ", it is not your turn.")
        else
                say(".slap " FROM ", you are not playing.")
@@ -336,12 +473,14 @@ sp_state == "bid" &&
                } else if ($2 == 0) {
                        say(FROM " goes nil!")
                        sp_nil[i] = 1
+               } else {
+                       sp_nil[i] = 0
                }
                if (sp_turn != sp_dealer) {
                        say("Bidding goes to " sp_player "!")
                } else {
                        for (p in sp_players)
-                               privmsg(p, "You have: " sp_hand(p))
+                               say(p, "You have: " sp_hand(p, p))
                        sp_state = "play"
                        for (i=0; i<2; i++) {
                                if (sp_nil[i] == 2 || sp_nil[i+2] == 2) {
@@ -358,75 +497,81 @@ sp_state == "bid" &&
 
 sp_state == "pass" &&
 /^\.pass (\S+)$/ {
-       card = $2
-       team = sp_players[FROM] % 2
-       if (!(FROM in sp_players)) {
+       _card = $2
+       _team = sp_from in sp_players ? sp_players[sp_from] % 2 : 0
+
+       # check validity and pass
+       if (!(sp_from in sp_players)) {
                say(".slap " FROM ", you are not playing.")
        }
-       else if (sp_nil[team] != 2 && sp_nil[team+2] != 2) {
+       else if (sp_nil[_team] != 2 && sp_nil[_team+2] != 2) {
                reply("Your team did not go blind")
        }
-       else if (sp_pass[sp_players[FROM]]) {
+       else if (sp_pass[sp_players[sp_from]]) {
                reply("You have already passed a card")
        }
-       else if (!(card in sp_deck)) {
+       else if (!(_card in sp_deck)) {
                reply("Invalid card")
        }
-       else if (!(card in sp_hands[FROM])) {
+       else if (!(_card in sp_hands[sp_from])) {
                reply("You do not have that card")
        }
        else {
-               sp_pass[sp_players[FROM]] = $2
-               privmsg(sp_channel, FROM " passes a card")
+               sp_pass[sp_players[sp_from]] = $2
+               say(sp_channel, FROM " passes a card")
        }
+
+       # check for end of passing
        if (((sp_nil[0] != 2 && sp_nil[2] != 2) || (sp_pass[0] && sp_pass[2])) &&
            ((sp_nil[1] != 2 && sp_nil[3] != 2) || (sp_pass[1] && sp_pass[3]))) {
                for (i in sp_pass) {
-                       partner = (i+2)%4
-                       card    = sp_pass[i]
-                       delete sp_hands[sp_order[i]][card]
-                       sp_hands[sp_order[partner]][card] = 1
+                       _partner = (i+2)%4
+                       _card    = sp_pass[i]
+                       delete sp_hands[sp_order[i]][_card]
+                       sp_hands[sp_order[_partner]][_card] = 1
                }
-               privmsg(sp_channel, "Cards have been passed, play starts with " sp_player "!")
+               say(sp_channel, "Cards have been passed, play starts with " sp_player "!")
                for (p in sp_players)
-                       privmsg(p, "You have: " sp_hand(p))
+                       say(p, "You have: " sp_hand(p, p))
                sp_state = "play"
        }
 }
 
-sp_state ~ "(play|bid)" &&
+sp_state ~ "(bid|pass|play)" &&
 /^\.look$/ {
-       if (!(FROM in sp_players)) {
+       if (!(sp_from in sp_players)) {
                say(".slap " FROM ", you are not playing.")
        } else {
-               sp_looked[sp_players[FROM]] = 1
-               privmsg(FROM, "You have: " sp_hand(FROM))
+               sp_looked[sp_players[sp_from]] = 1
+               say(FROM, "You have: " sp_hand(FROM, sp_from))
        }
 }
 
 sp_valid &&
 sp_state == "play" &&
-/^\.play (\S+)$/ {
-       card = $2
-       if (!(card in sp_deck)) {
+/^\.play (\S+)/ {
+       _card = $2
+       gsub(/[^A-Za-z0-9]/, "", _card);
+       if (!(_card in sp_deck)) {
                reply("Invalid card")
        }
-       else if (!(card in sp_hands[FROM])) {
+       else if (!(_card in sp_hands[sp_from])) {
                reply("You do not have that card")
        }
-       else if (sp_suit && card !~ sp_suit && sp_hasa(FROM, sp_suit)) {
+       else if (sp_suit && _card !~ sp_suit && sp_hasa(sp_from, sp_suit)) {
                reply("You must follow suit (" sp_suit ")")
        }
-       else if (card ~ /s/ && length(sp_hands[FROM]) == 13 && sp_hasa(FROM, "[^s]$")) {
+       else if (_card ~ /s/ && length(sp_hands[sp_from]) == 13 && sp_hasa(sp_from, "[^s]$")) {
                reply("You cannot trump on the first hand")
        }
-       else if (card ~ /s/ && length(sp_pile) == 0 && sp_hasa(FROM, "[^s]$") && !sp_broken) {
+       else if (_card ~ /s/ && length(sp_pile) == 0 && sp_hasa(sp_from, "[^s]$") && !sp_broken) {
                reply("Spades have not been broken")
        }
        else {
-               sp_play(card)
-               privmsg(FROM, "You have: " sp_hand(FROM))
+               sp_play(_card)
                if (sp_state == "play") {
+                       if (length(sp_hands[sp_from]))
+                               say(FROM, "You have: " sp_hand(FROM, sp_from))
                        if (sp_piles)
                                say(sp_player ": it is your turn! " \
                                    "(" sp_pretty(sp_piles, sp_player) ")")
@@ -436,7 +581,7 @@ sp_state == "play" &&
        }
 }
 
-/^\.bids$/ && sp_state == "play" {
+/^\.bids$/ && sp_state ~ "(pass|play)" {
        say(sp_order[0] " bid " sp_bids[0] ", " \
            sp_order[2] " bid " sp_bids[2] ", " \
            "total: " sp_bids[0] + sp_bids[2])
@@ -452,17 +597,36 @@ sp_state == "play" &&
            sp_order[3] " took " int(sp_tricks[3]) "/" int(sp_bids[3]))
 }
 
+/^\.turn/ && sp_state ~ "(bid|pass|play)" {
+       _bids = sp_bidders()
+       _pile = sp_pretty(sp_piles, FROM)
+       if (sp_state == "bid" && !_bids)
+               say("It is " sp_player "'s bid!")
+       if (sp_state == "bid" && _bids)
+               say("It is " sp_player "'s bid! (" _bids ")")
+       if (sp_state == "play" && !_pile)
+               say("It is " sp_player "'s turn!")
+       if (sp_state == "play" && _pile)
+               say("It is " sp_player "'s turn! (" _pile ")")
+       for (_i=0; sp_state == "pass" && _i<4; _i++)
+               if ((sp_nil[_i%2+0]==2 || sp_nil[_i%2+2]==2) && !sp_pass[_i])
+                       say("Waiting for " sp_order[_i] " to pass a card!")
+}
+
 (TO == NICK || DST == sp_channel) &&
 /^\.(score|status)$/ {
        if (sp_state == "new") {
                say("There is no game in progress")
        }
        if (sp_state == "join") {
-               say("Waiting for palyers: " \
+               say("Waiting for players: " \
                    sp_order[0] " " sp_order[1] " " \
                    sp_order[2] " " sp_order[3])
        }
-       if (sp_state == "bid" || sp_state == "play") {
+       if (sp_state ~ "bid|pass|play") {
+               say("Playing to: " \
+                   sp_playto " points, " \
+                   sp_limit  " bags")
                say(sp_team(0) ": " \
                    int(sp_scores[0]) " points, " \
                    int(sp_bags(0))   " bags")
@@ -472,6 +636,10 @@ sp_state == "play" &&
        }
 }
 
+/^\.((new|end|load)game|join|look|bid|play)/ {
+       sp_save("var/sp_cur.json");
+}
+
 # Standin
 #/^\.playfor [^ ]*$/ {
 #}