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_last # [x] The result of the last hand
37 delete sp_hands # [p] Each players cards
38 delete sp_looked # [i] Whether a player has looked a their cards
39 delete sp_bids # [i] Each players bid
40 delete sp_nil # [i] Nil multiplier 0=regular, 1=nil, 2=blind
41 delete sp_pass # [i] Cards to pass
42 delete sp_tricks # [i] Tricks this round
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 sp_channel = "" # channel to play in
64 sp_log = "" # Log file name
65 delete sp_notify # [p] E-mail notification address
69 function sp_acopy(dst, src, key)
74 json_copy(dst, key, src[key])
78 function sp_save(file, game)
81 game["suit"] = sp_suit;
82 game["piles"] = sp_piles;
83 json_copy(game, "pile", sp_pile);
86 game["state"] = sp_state;
87 game["broken"] = sp_broken;
88 json_copy(game, "last", sp_last);
89 json_copy(game, "looked", sp_looked);
90 json_copy(game, "bids", sp_bids);
91 json_copy(game, "nil", sp_nil);
92 json_copy(game, "pass", sp_pass);
93 json_copy(game, "tricks", sp_tricks);
96 game["owner"] = sp_owner;
97 game["playto"] = sp_playto;
98 game["dealer"] = sp_dealer;
99 game["turn"] = sp_turn;
100 game["player"] = sp_player;
101 game["limit"] = sp_limit;
102 json_copy(game, "hands", sp_hands);
103 json_copy(game, "players", sp_players);
104 json_copy(game, "auths", sp_auths);
105 json_copy(game, "share", sp_share);
106 json_copy(game, "order", sp_order);
107 json_copy(game, "scores", sp_scores);
110 game["channel"] = sp_channel;
111 game["log"] = sp_log;
112 json_copy(game, "notify", sp_notify);
115 json_save(file, game);
118 function sp_load(file, game)
121 if (!json_load(file, game))
125 sp_suit = game["suit"];
126 sp_piles = game["piles"];
127 sp_acopy(sp_pile, game["pile"]);
130 sp_state = game["state"];
131 sp_broken = game["broken"];
132 sp_acopy(sp_last, game["last"]);
133 sp_acopy(sp_looked, game["looked"]);
134 sp_acopy(sp_bids, game["bids"]);
135 sp_acopy(sp_nil, game["nil"]);
136 sp_acopy(sp_pass, game["pass"]);
137 sp_acopy(sp_tricks, game["tricks"]);
140 sp_owner = game["owner"];
141 sp_playto = game["playto"];
142 sp_dealer = game["dealer"];
143 sp_turn = game["turn"];
144 sp_player = game["player"];
145 sp_limit = game["limit"];
146 sp_acopy(sp_hands, game["hands"]);
147 sp_acopy(sp_players, game["players"]);
148 sp_acopy(sp_auths, game["auths"]);
149 sp_acopy(sp_share, game["share"]);
150 sp_acopy(sp_order, game["order"]);
151 sp_acopy(sp_scores, game["scores"]);
154 sp_channel = game["channel"];
155 sp_log = game["log"];
156 sp_acopy(sp_notify, game["notify"]);
161 print strftime("%Y-%m-%d %H:%M:%S | ") msg >> "logs/" sp_log
162 fflush("logs/" sp_log)
163 say(sp_channel, msg);
166 function sp_pretty(cards, who)
169 gsub(/[0-9JQKA]*[sc]/, "\0031,00\002&\017", cards) # black
170 gsub(/[0-9JQKA]*[hd]/, "\0034,00\002&\017", cards) # red
172 if (!nounicode[who]) {
173 gsub(/s/, "\002♠", cards)
174 gsub(/h/, "\002♥", cards)
175 gsub(/d/, "\002♦", cards)
176 gsub(/c/, "\002♣", cards)
181 function sp_next(who, prev)
184 sp_turn = who ? sp_players[who] : (sp_turn + 1) % 4
185 if (length(sp_order) == 4)
186 sp_player = sp_order[sp_turn]
190 function sp_shuf(i, mixed)
192 sp_usort(sp_players, mixed)
194 sp_order[i-1] = mixed[i]
195 sp_players[mixed[i]] = i-1
199 function sp_deal( shuf)
201 sp_say("/me deals the cards")
202 sp_usort(sp_deck, shuf)
203 for (i=1; i<=52; i++)
204 sp_hands[sp_order[i%4]][shuf[i]] = 1
206 sp_dealer = (sp_dealer+1)%4
208 sp_player = sp_order[sp_turn]
209 sp_say(sp_player ": you bid first!")
212 function sp_hand(to, who, sort, str)
214 asorti(sp_hands[who], sort, "sp_csort")
215 for (i=0; i<length(sort); i++)
216 str = str "" sprintf("%4s", sort[i])
217 gsub(/^ +| +$/, "", str)
218 return sp_pretty(str, to)
221 function sp_hasa(who, expr)
223 for (c in sp_hands[who]) {
230 function sp_type(card)
232 return substr(card, length(card))
235 function sp_usort(list, out) {
238 asorti(out, out, "@val_num_asc")
241 function sp_csort(i1,v1,i2,v2) {
242 return sp_deck[i1] > sp_deck[i2] ? +1 :
243 sp_deck[i1] < sp_deck[i2] ? -1 : 0;
246 function sp_winner( card, tmp)
248 for (card in sp_pile)
249 if (card !~ sp_suit && card !~ /s/)
251 asorti(sp_pile, tmp, "sp_csort")
252 #print "pile: " tmp[1] ">" tmp[2] ">" tmp[3] ">" tmp[4]
258 #return "{" sp_order[i+0] "," sp_order[i+2] "}"
259 return sp_order[i+0] "/" sp_order[i+2]
262 function sp_bags(i, bags)
264 bags = sp_scores[i] % sp_limit
272 return sp_nil[who] == 0 ? sp_bids[who] :
273 sp_nil[who] == 1 ? "nil" :
274 sp_nil[who] == 2 ? "blind" : "n/a"
277 function sp_passer(who)
279 return sp_nil[(who+0)%4] == 2 || sp_nil[(who+1)%4] != 0 ||
280 sp_nil[(who+2)%4] == 2 || sp_nil[(who+3)%4] != 0
283 function sp_bidders( i, turn, bid, bids)
285 for (i = 0; i < 4; i++) {
286 turn = (sp_dealer + i) % 4
287 if (bid = sp_bid(turn))
288 bids = bids " " sp_order[turn] ":" bid
290 gsub(/^ +| +$/, "", bids)
294 function sp_extra( n, s)
296 n = sp_bids[0] + sp_bids[1] + sp_bids[2] + sp_bids[3];
297 s = n == 12 || n == 14 ? "" : "s";
299 return n<13 ? "Playing with " 13-n " bag" s "!" :
300 n>13 ? "Fighting for " n-13 " trick" s "!" : "No bags!";
303 function sp_score( bids, times, tricks)
305 for (i=0; i<2; i++) {
306 bids = sp_bids[i] + sp_bids[i+2]
307 tricks = sp_tricks[i] + sp_tricks[i+2]
309 times = int((sp_bags(i) + bags) / sp_limit)
311 sp_say(sp_team(i) " bag" (times>1?" way ":" ") "out")
312 sp_scores[i] -= sp_limit * 10 * times;
314 if (tricks >= bids) {
315 sp_say(sp_team(i) " make their bid: " tricks "/" bids)
316 sp_scores[i] += bids*10 + bags;
318 sp_say(sp_team(i) " go bust: " tricks "/" bids)
319 sp_scores[i] -= bids*10;
322 for (i=0; i<4; i++) {
325 sp_say(sp_order[i] " " \
326 (sp_nil[i] == 1 && !sp_tricks[i] ? "makes nil!" :
327 sp_nil[i] == 1 && sp_tricks[i] ? "fails at nil!" :
328 sp_nil[i] == 2 && !sp_tricks[i] ? "makes blind nil!" :
329 sp_nil[i] == 2 && sp_tricks[i] ? "fails miserably at blind nil!" :
331 sp_scores[i%2] += sp_limit * 10 * sp_nil[i] * \
332 (sp_tricks[i] == 0 ? 1 : -1)
334 if (sp_scores[0] > sp_scores[1])
335 sp_say(sp_team(0) " lead " sp_scores[0] " to " sp_scores[1] " of " sp_playto)
336 else if (sp_scores[1] > sp_scores[0])
337 sp_say(sp_team(1) " lead " sp_scores[1] " to " sp_scores[0] " of " sp_playto)
339 sp_say("tied at " sp_scores[0] " of " sp_playto)
342 function sp_play(card, winner, pi)
344 delete sp_hands[sp_from][card]
345 sp_pile[card] = sp_player
346 sp_piles = sp_piles (sp_piles?",":"") card
353 if (length(sp_pile) == 1)
354 sp_suit = sp_type(card)
357 if (length(sp_pile) == 4) {
359 pi = sp_players[sp_pile[winner]]
361 sp_say(sp_pile[winner] " wins with " sp_pretty(winner, FROM) \
362 " (" sp_pretty(sp_piles, FROM) ")")
363 sp_last["player"] = sp_pile[winner];
364 sp_last["pile"] = sp_piles;
365 sp_next(sp_pile[winner])
370 if (sp_tricks[0] + sp_tricks[1] + \
371 sp_tricks[2] + sp_tricks[3] == 13) {
372 sp_say("Round over!")
374 if (sp_scores[0] >= sp_playto || sp_scores[1] >= sp_playto &&
375 sp_scores[0] != sp_scores[1]) {
377 winner = sp_scores[0] > sp_scores[1] ? 0 : 1
379 say(CHANNEL, sp_team(winner) " wins the game " \
380 sp_scores[winner] " to " sp_scores[looser])
381 say(CHANNEL, sp_order[winner+0] "++")
382 say(CHANNEL, sp_order[winner+2] "++")
386 if (sp_scores[0] == sp_scores[1] &&
387 sp_scores[0] >= sp_playto)
388 sp_say("It's tie! Playing an extra round!");
396 function sp_delay(sec)
398 return (sec > 60*60*24 ? int(sec/60/60/24) "d " : "") \
399 (sec > 60*60 ? int(sec/60/60)%24 "h " : "") \
403 function sp_max(list, i, max)
405 for (i=0; i<length(list); i++)
406 if (max == "" || list[i] > max)
411 function sp_avg(list, i, sum)
413 for (i=0; i<length(list); i++)
415 return sum / length(list)
418 function sp_stats(file, line, arr, time, user, turn, start, delay)
421 while ((stat = getline line < file) > 0) {
423 if (!match(line, /^([0-9\- \:]*) \| (.*)$/, arr))
425 gsub(/[:-]/, " ", arr[1])
426 time = mktime(arr[1])
429 if (!match(arr[2], /^([^:]*): (.*)$/, arr))
433 # Record user latency
435 delay[turn][length(delay[turn])] = time - start
438 if (match(arr[2], /^it is your.*$/, arr)) {
447 reply("File does not exist: " file);
450 for (user in delay) {
451 say("latency for " user \
452 ": " sp_delay(sp_avg(delay[user])) " (avg)" \
453 ", " sp_delay(sp_max(delay[user])) " (max)")
459 cmd = "od -An -N4 -td4 /dev/random"
465 sp_load("var/sp_cur.json");
467 # sp_say("Game restored.")
471 sp_from = AUTH in sp_auths ? sp_auths[AUTH] : \
472 AUTH in sp_share ? sp_share[AUTH] : FROM
473 sp_valid = sp_from && sp_from == sp_player
479 say("Spades! " sp_pretty("As,Ah,Ad,Ac", FROM))
484 sp_save("var/sp_save.json");
490 sp_load("var/sp_save.json");
496 say(".help spades -- play a game of spades")
499 /^\.help [Ss]pades$/ {
500 say("Spades -- play a game of spades")
501 say(".help game -- setup and administer the game")
502 say(".help play -- commands for playing spades")
503 say(".help auth -- control player authorization")
508 say(".newgame [score] -- start a game to <score> points, default 500")
509 say(".endgame -- abort the current game")
510 say(".savegame -- save the current game to disk")
511 say(".loadgame -- load the previously saved game")
516 say(".join -- join the current game")
517 say(".look -- look at your cards")
518 say(".bid [n] -- bid for <n> tricks")
519 say(".pass [card] -- pass a card to your partner")
520 say(".play [card] -- play a card")
521 say(".last -- show who took the previous trick")
522 say(".turn -- check whose turn it is")
523 say(".bids -- check what everyone bid")
524 say(".tricks -- check how many trick have been taken")
525 say(".score -- check the score")
530 say(".auth [who] -- display authentication info for a user")
531 say(".allow [who] -- allow another person to play on your behalf")
532 say(".deny [who] -- prevent a previously allowed user from playing")
533 say(".show -- display which users can play for which players")
534 say(".notify [addr] -- email user when it is their turn")
540 /^\.deal (\w+) (.*)/ {
541 sp_say(FROM " is cheating for " $2)
543 for (i=3; i<=NF; i++)
549 /^\.order (\w+) ([0-4])/ {
550 sp_say(FROM " is cheating for " $2)
553 sp_player = sp_order[sp_turn]
557 sp_state == "play" &&
558 /^\.force (\w+) (\S+)$/ {
559 sp_say(FROM " is cheating for " $2)
567 match($0, /^\.newgame ?([1-9][0-9]*) *- *([1-9][0-9]*)$/, _arr) {
568 if (_arr[2] > _arr[1])
569 $0 = $1 " " int(rand() * (_arr[2]-_arr[1])+_arr[1])
572 /^\.newgame ?([1-9][0-9]*)?$/ {
573 if (sp_state != "new") {
574 reply("There is already a game in progress.")
578 sp_playto = $2 ? $2 : 200
579 sp_limit = sp_playto > 200 ? 10 : 5;
582 sp_log = strftime("%Y%m%d_%H%M%S.log")
583 sp_say(sp_owner " starts a game of Spades to " sp_playto " with " sp_limit " bags!")
587 (sp_from == sp_owner || AUTH == OWNER) &&
589 if (sp_state == "new") {
590 reply("There is no game in progress.")
592 sp_say(FROM " ends the game")
598 if (sp_state == "new") {
599 reply("There is no game in progress")
601 else if (sp_state == "play") {
602 reply("The game has already started")
604 else if (sp_state == "join" && sp_from in sp_players) {
605 reply("You are already playing")
607 else if (sp_state == "join") {
611 sp_auths[AUTH] = FROM
613 sp_say(FROM " joins the game!")
615 if (sp_state == "join" && sp_turn == 0) {
622 _who = $2 in USERS ? USERS[$2]["auth"] : ""
623 _str = _who && _who != $2 ? $2 " (" _who ")" : $2
624 if (sp_state ~ "new|join") {
625 reply("The game has not yet started")
627 else if (!(sp_from in sp_players)) {
628 reply("You are not playing")
631 reply(_str " is not logged in")
633 else if (_who in sp_players || _who in sp_auths) {
634 reply(_str " is a primary player")
636 else if (_who in sp_share) {
637 reply(_str " is already playing for " sp_share[_who])
640 sp_say(_str " can now play for " sp_from)
641 sp_share[_who] = sp_from
646 _who = $2 in USERS ? USERS[$2]["auth"] : $2
647 _str = _who && _who != $2 ? $2 " (" _who ")" : $2
648 if (sp_state ~ "new|join") {
649 reply("The game has not yet started")
651 else if (!(sp_from in sp_players)) {
652 reply("You are not playing")
654 else if (_who in sp_players || _who in sp_auths) {
655 reply(_str " is a primary player")
657 else if (!(_who in sp_share) || sp_share[_who] != sp_from) {
658 reply(_str " is not playing for " sp_from)
661 sp_say(_str " can no longer play for " sp_from)
662 delete sp_share[_who]
667 if (!(sp_from in sp_players))
668 reply("You are not playing")
669 else if (sp_from == FROM)
670 say(FROM " has an existential crisis")
672 reply("You are playing for " sp_from);
676 if (sp_from in sp_notify)
677 reply("Your address is " sp_notify[sp_from])
679 reply("Your address is not set")
683 if (sp_from in sp_notify) {
684 reply("Removing address " sp_notify[sp_from])
685 delete sp_notify[sp_from]
687 reply("Your address is not set")
691 /^\.notify \S+@\S+.\S+$/ {
693 gsub(/[^a-zA-Z0-9_+@.-]/, "", _addr)
694 sp_notify[sp_from] = _addr
695 reply("Notifying you at " _addr)
698 sp_state ~ "(bid|pass|play)" &&
702 _lines[sp_share[_i]] = _lines[sp_share[_i]] " " _i
704 sp_say(_i " allowed:" _lines[_i])
708 (sp_state == "bid" || sp_state == "play") &&
710 if (sp_from in sp_players)
711 reply("It is not your turn.")
713 reply("You are not playing.")
718 /^\.bid (0|[1-9][0-9]*)$/ {
719 if ($2 < 0 || $2 > 13) {
720 reply("You can only bid from 0 to 13")
724 if ($2 == 0 && !sp_looked[i]) {
725 sp_say(FROM " goes blind nil!")
727 } else if ($2 == 0) {
728 sp_say(FROM " goes nil!")
733 if (sp_turn != sp_dealer) {
734 sp_say(sp_player ": it is your bid! (" sp_bidders() ")")
736 sp_say(sp_extra() " (" sp_bidders() ")")
737 for (p in sp_players)
738 say(p, "You have: " sp_hand(p, p))
740 for (i=0; i<2; i++) {
742 sp_say(sp_team(i) ": select a card to pass " \
743 "(/msg " NICK " .pass <card>)")
747 if (sp_state == "play")
748 sp_say(sp_player ": you have the opening lead!")
753 sp_state == "pass" &&
756 _team = sp_from in sp_players ? sp_players[sp_from] % 2 : 0
758 # check validity and pass
759 if (!(sp_from in sp_players)) {
760 reply("You are not playing.")
762 else if (!sp_passer(_team)) {
763 reply("Your team did not go blind")
765 else if (sp_pass[sp_players[sp_from]]) {
766 reply("You have already passed a card")
768 else if (!(_card in sp_deck)) {
769 reply("Invalid card")
771 else if (!(_card in sp_hands[sp_from])) {
772 reply("You do not have that card")
775 sp_pass[sp_players[sp_from]] = $2
776 sp_say(FROM " passes a card")
779 # check for end of passing
780 if ((!sp_passer(0) || (sp_pass[0] && sp_pass[2])) &&
781 (!sp_passer(1) || (sp_pass[1] && sp_pass[3]))) {
785 delete sp_hands[sp_order[i]][_card]
786 sp_hands[sp_order[_partner]][_card] = 1
788 sp_say("Cards have been passed!")
789 sp_say(sp_player ": you have the opening lead!")
790 for (p in sp_players)
791 say(p, "You have: " sp_hand(p, p))
796 sp_state ~ "(bid|pass|play)" &&
798 if (!(sp_from in sp_players)) {
799 reply("You are not playing.")
801 sp_looked[sp_players[sp_from]] = 1
802 say(FROM, "You have: " sp_hand(FROM, sp_from))
807 sp_state == "play" &&
810 gsub(/[^A-Za-z0-9]/, "", _card);
811 if (!(_card in sp_deck)) {
812 reply("Invalid card")
814 else if (sp_suit && _card !~ sp_suit && sp_hasa(sp_from, sp_suit)) {
815 reply("You must follow suit (" sp_suit ")")
817 else if (_card ~ /s/ && length(sp_hands[sp_from]) == 13 && sp_hasa(sp_from, "[^s]$")) {
818 reply("You cannot trump on the first hand")
820 else if (_card ~ /s/ && length(sp_pile) == 0 && sp_hasa(sp_from, "[^s]$") && !sp_broken) {
821 reply("Spades have not been broken")
823 else if (!(_card in sp_hands[sp_from])) {
824 reply("You do not have that card")
828 if (sp_state == "play") {
829 if (length(sp_hands[sp_from]))
830 say(FROM, "You have: " sp_hand(FROM, sp_from))
832 sp_say(sp_player ": it is your turn! " \
833 "(" sp_pretty(sp_piles, sp_player) ")")
835 sp_say(sp_player ": it is your turn!")
840 /^\.last/ && sp_state == "play" {
841 if (!isarray(sp_last))
842 say("No tricks have been taken!");
844 say(sp_last["player"] " took " \
845 sp_pretty(sp_last["pile"], FROM));
848 /^\.bids/ && sp_state == "bid" ||
849 /^\.turn/ && sp_state ~ "(bid|pass|play)" {
851 _pile = sp_pretty(sp_piles, FROM)
855 if (/!/ && sp_share[_i] == sp_player)
856 _extra = _extra " " _i "!"
858 if (sp_state == "bid" && !_bids)
859 say("It is " sp_player "'s bid!" _extra)
860 if (sp_state == "bid" && _bids)
861 say("It is " sp_player "'s bid!" _extra " (" _bids ")")
862 if (sp_state == "play" && !_pile)
863 say("It is " sp_player "'s turn!" _extra)
864 if (sp_state == "play" && _pile)
865 say("It is " sp_player "'s turn!" _extra " (" _pile ")")
867 for (_i=0; sp_state == "pass" && _i<4; _i++)
868 if (sp_passer(_i) && !sp_pass[_i])
869 say("Waiting for " sp_order[_i] " to pass a card!")
871 if (/!!/ && (sp_state == "bid" || sp_state == "play")) {
872 if (sp_player in sp_notify) {
873 _bids = _bids ? _bids : "none"
874 _pile = _pile ? sp_piles : "none"
875 mail_send(sp_notify[sp_player], \
876 "It is your " sp_state "!", \
877 "Bids so far: " _bids "\n" \
878 "Cards played: " _pile)
879 say("Notified " sp_player " at " sp_notify[sp_player])
881 say("No email address for " sp_player)
886 /^\.bids$/ && sp_state ~ "(pass|play)" {
887 say(sp_order[0] " bid " sp_bid(0) ", " \
888 sp_order[2] " bid " sp_bid(2) ", " \
889 "total: " sp_bids[0] + sp_bids[2])
890 say(sp_order[1] " bid " sp_bid(1) ", " \
891 sp_order[3] " bid " sp_bid(3) ", " \
892 "total: " sp_bids[1] + sp_bids[3])
895 /^\.tricks$/ && sp_state == "play" {
896 say(sp_order[0] " took " int(sp_tricks[0]) "/" sp_bid(0) ", " \
897 sp_order[2] " took " int(sp_tricks[2]) "/" sp_bid(2))
898 say(sp_order[1] " took " int(sp_tricks[1]) "/" sp_bid(1) ", " \
899 sp_order[3] " took " int(sp_tricks[3]) "/" sp_bid(3))
902 (TO == NICK || DST == sp_channel) &&
903 /^\.(score|status)$/ {
904 if (sp_state == "new") {
905 say("There is no game in progress")
907 if (sp_state ~ "join|bid|pass|play") {
909 sp_playto " points, " \
912 if (sp_state == "join") {
913 say("Waiting for players: " \
914 sp_order[0] " " sp_order[1] " " \
915 sp_order[2] " " sp_order[3])
917 if (sp_state ~ "bid|pass|play") {
918 say(sp_team(0) ": " \
919 int(sp_scores[0]) " points, " \
920 int(sp_bags(0)) " bags")
921 say(sp_team(1) ": " \
922 int(sp_scores[1]) " points, " \
923 int(sp_bags(1)) " bags")
927 (TO == NICK || DST == sp_channel) &&
929 say("http://pileus.org/andy/spades/" sp_log)
932 (TO == NICK || DST == sp_channel) &&
934 sp_stats("logs/" sp_log);
937 (TO == NICK || DST == sp_channel) &&
938 /^\.stats ([0-9]+_[0-9]+)(\.log)$/ {
939 gsub(/\.log$/, "", $2);
940 sp_stats("logs/" $2 ".log");
943 /^\.((new|end|load)game|join|look|bid|pass|play|notify)/ {
944 sp_save("var/sp_cur.json");