5 function sp_init(cards, tmp0, tmp1)
8 cards ="As Ks Qs Js 10s 9s 8s 7s 6s 5s 4s 3s 2s "\
9 "Ah Kh Qh Jh 10h 9h 8h 7h 6h 5h 4h 3h 2h "\
10 "Ac Kc Qc Jc 10c 9c 8c 7c 6c 5c 4c 3c 2c "\
11 "Ad Kd Qd Jd 10d 9d 8d 7d 6d 5d 4d 3d 2d"
13 for (i=1; i<=length(tmp0); i++)
17 function sp_reset(type)
21 sp_from = "" # The speakers player name
22 sp_valid = "" # It is the speaker turn
27 sp_suit = "" # The lead suit {s,h,d,c}
28 sp_piles = "" # [x] Played cards this turn
29 delete sp_pile # [x] Played cards this turn
34 sp_state = "bid" # {new,join,bid,pass,play}
35 sp_broken = 0 # Whether spades are broken
36 delete sp_hands # [p] Each players cards
37 delete sp_looked # [i] Whether a player has looked a their cards
38 delete sp_bids # [i] Each players bid
39 delete sp_nil # [i] Nil multiplier 0=regular, 1=nil, 2=blind
40 delete sp_pass # [i] Cards to pass
41 delete sp_tricks # [i] Tricks this round
46 sp_channel = "" # channel to play in
47 sp_state = "new" # {new,join,bid,pass,play}
48 sp_owner = "" # Who started the game
49 sp_playto = 0 # Score the game will go to
50 sp_dealer =-1 # Who is dealing this round
51 sp_turn = 0 # Index of who's turn it is
52 sp_player = "" # Who's turn it is
53 sp_limit = 10 # Bag out limit / nil bonus
54 delete sp_players # [p] Player names players["name"] -> i
55 delete sp_auths # [c] Player auth names auths["auth"] -> "name"
56 delete sp_share # [c] Player teammates share["friend"] -> "name"
57 delete sp_order # [i] Player order order[i] -> "name"
58 delete sp_scores # [i] Teams score
63 delete sp_notify # [p] E-mail notification address
67 function sp_acopy(dst, src, key)
72 json_copy(dst, key, src[key])
76 function sp_save(file, game)
79 game["suit"] = sp_suit;
80 game["piles"] = sp_piles;
81 json_copy(game, "pile", sp_pile);
84 game["state"] = sp_state;
85 game["broken"] = sp_broken;
86 json_copy(game, "looked", sp_looked);
87 json_copy(game, "bids", sp_bids);
88 json_copy(game, "nil", sp_nil);
89 json_copy(game, "pass", sp_pass);
90 json_copy(game, "tricks", sp_tricks);
93 game["channel"] = sp_channel;
94 game["owner"] = sp_owner;
95 game["playto"] = sp_playto;
96 game["dealer"] = sp_dealer;
97 game["turn"] = sp_turn;
98 game["player"] = sp_player;
99 game["limit"] = sp_limit;
100 json_copy(game, "hands", sp_hands);
101 json_copy(game, "players", sp_players);
102 json_copy(game, "auths", sp_auths);
103 json_copy(game, "share", sp_share);
104 json_copy(game, "order", sp_order);
105 json_copy(game, "scores", sp_scores);
106 json_copy(game, "notify", sp_notify);
109 json_save(file, game);
112 function sp_load(file, game)
115 if (!json_load(file, game))
119 sp_suit = game["suit"];
120 sp_piles = game["piles"];
121 sp_acopy(sp_pile, game["pile"]);
124 sp_state = game["state"];
125 sp_broken = game["broken"];
126 sp_acopy(sp_looked, game["looked"]);
127 sp_acopy(sp_bids, game["bids"]);
128 sp_acopy(sp_nil, game["nil"]);
129 sp_acopy(sp_pass, game["pass"]);
130 sp_acopy(sp_tricks, game["tricks"]);
133 sp_channel = game["channel"];
134 sp_owner = game["owner"];
135 sp_playto = game["playto"];
136 sp_dealer = game["dealer"];
137 sp_turn = game["turn"];
138 sp_player = game["player"];
139 sp_limit = game["limit"];
140 sp_acopy(sp_hands, game["hands"]);
141 sp_acopy(sp_players, game["players"]);
142 sp_acopy(sp_auths, game["auths"]);
143 sp_acopy(sp_share, game["share"]);
144 sp_acopy(sp_order, game["order"]);
145 sp_acopy(sp_scores, game["scores"]);
146 sp_acopy(sp_notify, game["notify"]);
149 function sp_pretty(cards, who)
152 gsub(/[0-9JQKA]*[sc]/, "\0031,00\002&\017", cards) # black
153 gsub(/[0-9JQKA]*[hd]/, "\0034,00\002&\017", cards) # red
155 if (!nounicode[who]) {
156 gsub(/s/, "\002♠", cards)
157 gsub(/h/, "\002♥", cards)
158 gsub(/d/, "\002♦", cards)
159 gsub(/c/, "\002♣", cards)
164 function sp_next(who, prev)
167 sp_turn = who ? sp_players[who] : (sp_turn + 1) % 4
168 if (length(sp_order) == 4)
169 sp_player = sp_order[sp_turn]
173 function sp_shuf(i, mixed)
175 sp_usort(sp_players, mixed)
177 sp_order[i-1] = mixed[i]
178 sp_players[mixed[i]] = i-1
182 function sp_deal( shuf)
184 say("/me deals the cards")
185 sp_usort(sp_deck, shuf)
186 for (i=1; i<=52; i++)
187 sp_hands[sp_order[i%4]][shuf[i]] = 1
189 sp_dealer = (sp_dealer+1)%4
191 sp_player = sp_order[sp_turn]
192 say(sp_player ": you bid first!")
195 function sp_hand(to, who, sort, str)
197 asorti(sp_hands[who], sort, "sp_csort")
198 for (i=0; i<length(sort); i++)
199 str = str "" sprintf("%4s", sort[i])
200 gsub(/^ +| +$/, "", str)
201 return sp_pretty(str, to)
204 function sp_hasa(who, expr)
206 for (c in sp_hands[who]) {
213 function sp_type(card)
215 return substr(card, length(card))
218 function sp_usort(list, out) {
221 asorti(out, out, "@val_num_asc")
224 function sp_csort(i1,v1,i2,v2) {
225 return sp_deck[i1] > sp_deck[i2] ? +1 :
226 sp_deck[i1] < sp_deck[i2] ? -1 : 0;
229 function sp_winner( card, tmp)
231 for (card in sp_pile)
232 if (card !~ sp_suit && card !~ /s/)
234 asorti(sp_pile, tmp, "sp_csort")
235 #print "pile: " tmp[1] ">" tmp[2] ">" tmp[3] ">" tmp[4]
241 #return "{" sp_order[i+0] "," sp_order[i+2] "}"
242 return sp_order[i+0] "/" sp_order[i+2]
245 function sp_bags(i, bags)
247 bags = sp_scores[i] % sp_limit
255 return sp_nil[who] == 0 ? sp_bids[who] :
256 sp_nil[who] == 1 ? "nil" :
257 sp_nil[who] == 2 ? "blind" : "n/a"
260 function sp_passer(who)
262 return sp_nil[(who+0)%4] == 2 || sp_nil[(who+1)%4] != 0 ||
263 sp_nil[(who+2)%4] == 2 || sp_nil[(who+3)%4] != 0
266 function sp_bidders( i, turn, bid, bids)
268 for (i = 0; i < 4; i++) {
269 turn = (sp_dealer + i) % 4
270 if (bid = sp_bid(turn))
271 bids = bids " " sp_order[turn] ":" bid
273 gsub(/^ +| +$/, "", bids)
277 function sp_score( bids, times, tricks)
279 for (i=0; i<2; i++) {
280 bids = sp_bids[i] + sp_bids[i+2]
281 tricks = sp_tricks[i] + sp_tricks[i+2]
283 times = int((sp_bags(i) + bags) / sp_limit)
285 say(sp_team(i) " bag" (times>1?" way ":" ") "out")
286 sp_scores[i] -= sp_limit * 10 * times;
288 if (tricks >= bids) {
289 say(sp_team(i) " make their bid: " tricks "/" bids)
290 sp_scores[i] += bids*10 + bags;
292 say(sp_team(i) " go bust: " tricks "/" bids)
293 sp_scores[i] -= bids*10;
296 for (i=0; i<4; i++) {
299 say(sp_order[i] " " \
300 (sp_nil[i] == 1 && !sp_tricks[i] ? "makes nil!" :
301 sp_nil[i] == 1 && sp_tricks[i] ? "fails at nil!" :
302 sp_nil[i] == 2 && !sp_tricks[i] ? "makes blind nil!" :
303 sp_nil[i] == 2 && sp_tricks[i] ? "fails miserably at blind nil!" :
305 sp_scores[i%2] += sp_limit * 10 * sp_nil[i] * \
306 (sp_tricks[i] == 0 ? 1 : -1)
308 if (sp_scores[0] > sp_scores[1])
309 say(sp_team(0) " lead " sp_scores[0] " to " sp_scores[1] " of " sp_playto)
310 else if (sp_scores[1] > sp_scores[0])
311 say(sp_team(1) " lead " sp_scores[1] " to " sp_scores[0] " of " sp_playto)
313 say("tied at " sp_scores[0])
316 function sp_play(card, winner, pi)
318 delete sp_hands[sp_from][card]
319 sp_pile[card] = sp_player
320 sp_piles = sp_piles (sp_piles?",":"") card
327 if (length(sp_pile) == 1)
328 sp_suit = sp_type(card)
331 if (length(sp_pile) == 4) {
333 pi = sp_players[sp_pile[winner]]
335 say(sp_pile[winner] " wins with " sp_pretty(winner, FROM) \
336 " (" sp_pretty(sp_piles, FROM) ")")
337 sp_next(sp_pile[winner])
342 if (sp_tricks[0] + sp_tricks[1] + \
343 sp_tricks[2] + sp_tricks[3] == 13) {
346 if (sp_scores[0] >= sp_playto || sp_scores[1] >= sp_playto &&
347 sp_scores[0] != sp_scores[1]) {
349 winner = sp_scores[0] > sp_scores[1] ? 0 : 1
351 say(CHANNEL, sp_team(winner) " wins the game " \
352 sp_scores[winner] " to " sp_scores[looser])
353 say(CHANNEL, sp_order[winner+0] "++")
354 say(CHANNEL, sp_order[winner+2] "++")
358 if (sp_scores[0] == sp_scores[1] &&
359 sp_scores[0] >= sp_playto)
360 say("It's tie! Playing an extra round!");
369 cmd = "od -An -N4 -td4 /dev/random"
375 sp_load("var/sp_cur.json");
377 # say(sp_channel, "Game restored.")
381 sp_from = AUTH in sp_auths ? sp_auths[AUTH] : \
382 AUTH in sp_share ? sp_share[AUTH] : FROM
383 sp_valid = sp_from && sp_from == sp_player
389 say("Spades! " sp_pretty("As,Ah,Ad,Ac", FROM))
394 sp_save("var/sp_save.json");
400 sp_load("var/sp_save.json");
406 say(".help spades -- play a game of spades")
409 /^\.help [Ss]pades$/ {
410 say("Spades -- play a game of spades")
411 say(".help game -- setup and administer the game")
412 say(".help play -- commands for playing spades")
413 say(".help auth -- control player authorization")
418 say(".newgame [score] -- start a game to <score> points, default 500")
419 say(".endgame -- abort the current game")
420 say(".savegame -- save the current game to disk")
421 say(".loadgame -- load the previously saved game")
426 say(".join -- join the current game")
427 say(".look -- look at your cards")
428 say(".bid [n] -- bid for <n> tricks")
429 say(".pass [card] -- pass a card to your partner")
430 say(".play [card] -- play a card")
431 say(".turn -- check whose turn it is")
432 say(".bids -- check what everyone bid")
433 say(".tricks -- check how many trick have been taken")
434 say(".score -- check the score")
439 say(".auth [who] -- display authentication info for a user")
440 say(".allow [who] -- allow another person to play on your behalf")
441 say(".deny [who] -- prevent a previously allowed user from playing")
442 say(".show -- display which users can play for which players")
443 say(".notify [addr] -- email user when it is their turn")
449 /^\.deal (\w+) (.*)/ {
450 say(sp_channel, FROM " is cheating for " $2)
452 for (i=3; i<=NF; i++)
458 /^\.order (\w+) ([0-4])/ {
459 say(sp_channel, FROM " is cheating for " $2)
462 sp_player = sp_order[sp_turn]
466 sp_state == "play" &&
467 /^\.force (\w+) (\S+)$/ {
468 say(sp_channel, FROM " is cheating for " $2)
476 match($0, /^\.newgame ?([1-9][0-9]*) *- *([1-9][0-9]*)$/, _arr) {
477 if (_arr[2] > _arr[1])
478 $0 = $1 " " int(rand() * (_arr[2]-_arr[1])+_arr[1])
481 /^\.newgame ?([1-9][0-9]*)?$/ {
482 if (sp_state != "new") {
483 reply("There is already a game in progress.")
487 sp_playto = $2 ? $2 : 200
488 sp_limit = sp_playto > 200 ? 10 : 5;
491 say(sp_owner " starts a game of Spades to " sp_playto " with " sp_limit " bags!")
495 (sp_from == sp_owner || AUTH == OWNER) &&
497 if (sp_state == "new") {
498 reply("There is no game in progress.")
500 say(FROM " ends the game")
506 if (sp_state == "new") {
507 reply("There is no game in progress")
509 else if (sp_state == "play") {
510 reply("The game has already started")
512 else if (sp_state == "join" && sp_from in sp_players) {
513 reply("You are already playing")
515 else if (sp_state == "join") {
519 sp_auths[AUTH] = FROM
521 say(FROM " joins the game!")
523 if (sp_state == "join" && sp_turn == 0) {
530 _who = $2 in USERS ? USERS[$2]["auth"] : ""
531 _str = _who && _who != $2 ? $2 " (" _who ")" : $2
532 if (sp_state ~ "new|join") {
533 reply("The game has not yet started")
535 else if (!(sp_from in sp_players)) {
536 reply("You are not playing")
539 reply(_str " is not logged in")
541 else if (_who in sp_players || _who in sp_auths) {
542 reply(_str " is a primary player")
544 else if (_who in sp_share) {
545 reply(_str " is already playing for " sp_share[_who])
548 reply(_str " can now play for " sp_from)
549 sp_share[_who] = sp_from
554 _who = $2 in USERS ? USERS[$2]["auth"] : $2
555 _str = _who && _who != $2 ? $2 " (" _who ")" : $2
556 if (sp_state ~ "new|join") {
557 reply("The game has not yet started")
559 else if (!(sp_from in sp_players)) {
560 reply("You are not playing")
562 else if (_who in sp_players || _who in sp_auths) {
563 reply(_str " is a primary player")
565 else if (!(_who in sp_share) || sp_share[_who] != sp_from) {
566 reply(_str " is not playing for " sp_from)
569 reply(_str " can no longer play for " sp_from)
570 delete sp_share[_who]
575 if (sp_from in sp_notify)
576 reply("Your address is " sp_notify[sp_from])
578 reply("Your address is not set")
582 if (sp_from in sp_notify) {
583 reply("Removing address " sp_notify[sp_from])
584 delete sp_notify[sp_from]
586 reply("Your address is not set")
590 /^\.notify \S+@\S+.\S+$/ {
592 gsub(/[^a-zA-Z0-9_+@.-]/, "", _addr)
593 sp_notify[sp_from] = _addr
594 reply("Notifying you at " _addr)
597 sp_state ~ "(bid|pass|play)" &&
601 _lines[sp_share[_i]] = _lines[sp_share[_i]] " " _i
603 say(_i " allowed:" _lines[_i])
607 (sp_state == "bid" || sp_state == "play") &&
609 if (sp_from in sp_players)
610 say(".slap " FROM ", it is not your turn.")
612 say(".slap " FROM ", you are not playing.")
617 /^\.bid (0|[1-9][0-9]*)$/ {
618 if ($2 < 0 || $2 > 13) {
619 reply("You can only bid from 0 to 13")
623 if ($2 == 0 && !sp_looked[i]) {
624 say(FROM " goes blind nil!")
626 } else if ($2 == 0) {
627 say(FROM " goes nil!")
632 if (sp_turn != sp_dealer) {
633 say(sp_player ": it is your bid! (" sp_bidders() ")")
635 for (p in sp_players)
636 say(p, "You have: " sp_hand(p, p))
638 for (i=0; i<2; i++) {
640 say(sp_team(i) ": select a card to pass " \
641 "(/msg " NICK " .pass <card>)")
645 if (sp_state == "play")
646 say(sp_player ": you have the opening lead!")
651 sp_state == "pass" &&
654 _team = sp_from in sp_players ? sp_players[sp_from] % 2 : 0
656 # check validity and pass
657 if (!(sp_from in sp_players)) {
658 say(".slap " FROM ", you are not playing.")
660 else if (!sp_passer(_team)) {
661 reply("Your team did not go blind")
663 else if (sp_pass[sp_players[sp_from]]) {
664 reply("You have already passed a card")
666 else if (!(_card in sp_deck)) {
667 reply("Invalid card")
669 else if (!(_card in sp_hands[sp_from])) {
670 reply("You do not have that card")
673 sp_pass[sp_players[sp_from]] = $2
674 say(sp_channel, FROM " passes a card")
677 # check for end of passing
678 if ((!sp_passer(0) || (sp_pass[0] && sp_pass[2])) &&
679 (!sp_passer(1) || (sp_pass[1] && sp_pass[3]))) {
683 delete sp_hands[sp_order[i]][_card]
684 sp_hands[sp_order[_partner]][_card] = 1
686 say(sp_channel, "Cards have been passed!")
687 say(sp_channel, sp_player ": you have the opening lead!")
688 for (p in sp_players)
689 say(p, "You have: " sp_hand(p, p))
694 sp_state ~ "(bid|pass|play)" &&
696 if (!(sp_from in sp_players)) {
697 say(".slap " FROM ", you are not playing.")
699 sp_looked[sp_players[sp_from]] = 1
700 say(FROM, "You have: " sp_hand(FROM, sp_from))
705 sp_state == "play" &&
708 gsub(/[^A-Za-z0-9]/, "", _card);
709 if (!(_card in sp_deck)) {
710 reply("Invalid card")
712 else if (sp_suit && _card !~ sp_suit && sp_hasa(sp_from, sp_suit)) {
713 reply("You must follow suit (" sp_suit ")")
715 else if (_card ~ /s/ && length(sp_hands[sp_from]) == 13 && sp_hasa(sp_from, "[^s]$")) {
716 reply("You cannot trump on the first hand")
718 else if (_card ~ /s/ && length(sp_pile) == 0 && sp_hasa(sp_from, "[^s]$") && !sp_broken) {
719 reply("Spades have not been broken")
721 else if (!(_card in sp_hands[sp_from])) {
722 reply("You do not have that card")
726 if (sp_state == "play") {
727 if (length(sp_hands[sp_from]))
728 say(FROM, "You have: " sp_hand(FROM, sp_from))
730 say(sp_player ": it is your turn! " \
731 "(" sp_pretty(sp_piles, sp_player) ")")
733 say(sp_player ": it is your turn!")
738 /^\.bids/ && sp_state == "bid" ||
739 /^\.turn/ && sp_state ~ "(bid|pass|play)" {
741 _pile = sp_pretty(sp_piles, FROM)
745 if (/!/ && sp_share[_i] == sp_player)
746 _extra = _extra " " _i "!"
748 if (sp_state == "bid" && !_bids)
749 say("It is " sp_player "'s bid!" _extra)
750 if (sp_state == "bid" && _bids)
751 say("It is " sp_player "'s bid!" _extra " (" _bids ")")
752 if (sp_state == "play" && !_pile)
753 say("It is " sp_player "'s turn!" _extra)
754 if (sp_state == "play" && _pile)
755 say("It is " sp_player "'s turn!" _extra " (" _pile ")")
757 for (_i=0; sp_state == "pass" && _i<4; _i++)
758 if (sp_passer(_i) && !sp_pass[_i])
759 say("Waiting for " sp_order[_i] " to pass a card!")
761 if (/!!/ && (sp_state == "bid" || sp_state == "play")) {
762 if (sp_player in sp_notify) {
763 _bids = _bids ? _bids : "none"
764 _pile = _pile ? sp_piles : "none"
765 mail_send(sp_notify[sp_player], \
766 "It is your " sp_state "!", \
767 "Bids so far: " _bids "\n" \
768 "Cards played: " _pile)
769 say("Notified " sp_player " at " sp_notify[sp_player])
771 say("No email address for " sp_player)
776 /^\.bids$/ && sp_state ~ "(pass|play)" {
777 say(sp_order[0] " bid " sp_bid(0) ", " \
778 sp_order[2] " bid " sp_bid(2) ", " \
779 "total: " sp_bids[0] + sp_bids[2])
780 say(sp_order[1] " bid " sp_bid(1) ", " \
781 sp_order[3] " bid " sp_bid(3) ", " \
782 "total: " sp_bids[1] + sp_bids[3])
785 /^\.tricks$/ && sp_state == "play" {
786 say(sp_order[0] " took " int(sp_tricks[0]) "/" sp_bid(0) ", " \
787 sp_order[2] " took " int(sp_tricks[2]) "/" sp_bid(2))
788 say(sp_order[1] " took " int(sp_tricks[1]) "/" sp_bid(1) ", " \
789 sp_order[3] " took " int(sp_tricks[3]) "/" sp_bid(3))
792 (TO == NICK || DST == sp_channel) &&
793 /^\.(score|status)$/ {
794 if (sp_state == "new") {
795 say("There is no game in progress")
797 if (sp_state ~ "join|bid|pass|play") {
799 sp_playto " points, " \
802 if (sp_state == "join") {
803 say("Waiting for players: " \
804 sp_order[0] " " sp_order[1] " " \
805 sp_order[2] " " sp_order[3])
807 if (sp_state ~ "bid|pass|play") {
808 say(sp_team(0) ": " \
809 int(sp_scores[0]) " points, " \
810 int(sp_bags(0)) " bags")
811 say(sp_team(1) ": " \
812 int(sp_scores[1]) " points, " \
813 int(sp_bags(1)) " bags")
817 /^\.((new|end|load)game|join|look|bid|pass|play|notify)/ {
818 sp_save("var/sp_cur.json");