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
59 delete sp_teams # [i] Teams names
64 sp_channel = "" # channel to play in
65 sp_log = "" # Log file name
66 sp_sock = "" # UDP log socket
67 delete sp_notify # [p] E-mail notification address
71 function sp_acopy(dst, src, key)
76 json_copy(dst, key, src[key])
80 function sp_save(file, game)
83 game["suit"] = sp_suit;
84 game["piles"] = sp_piles;
85 json_copy(game, "pile", sp_pile);
88 game["state"] = sp_state;
89 game["broken"] = sp_broken;
90 json_copy(game, "last", sp_last);
91 json_copy(game, "looked", sp_looked);
92 json_copy(game, "bids", sp_bids);
93 json_copy(game, "nil", sp_nil);
94 json_copy(game, "pass", sp_pass);
95 json_copy(game, "tricks", sp_tricks);
98 game["owner"] = sp_owner;
99 game["playto"] = sp_playto;
100 game["dealer"] = sp_dealer;
101 game["turn"] = sp_turn;
102 game["player"] = sp_player;
103 game["limit"] = sp_limit;
104 json_copy(game, "hands", sp_hands);
105 json_copy(game, "players", sp_players);
106 json_copy(game, "auths", sp_auths);
107 json_copy(game, "share", sp_share);
108 json_copy(game, "order", sp_order);
109 json_copy(game, "scores", sp_scores);
110 json_copy(game, "teams", sp_teams);
113 game["channel"] = sp_channel;
114 game["log"] = sp_log;
115 json_copy(game, "notify", sp_notify);
118 json_save(file, game);
121 function sp_load(file, game)
124 if (!json_load(file, game))
128 sp_suit = game["suit"];
129 sp_piles = game["piles"];
130 sp_acopy(sp_pile, game["pile"]);
133 sp_state = game["state"];
134 sp_broken = game["broken"];
135 sp_acopy(sp_last, game["last"]);
136 sp_acopy(sp_looked, game["looked"]);
137 sp_acopy(sp_bids, game["bids"]);
138 sp_acopy(sp_nil, game["nil"]);
139 sp_acopy(sp_pass, game["pass"]);
140 sp_acopy(sp_tricks, game["tricks"]);
143 sp_owner = game["owner"];
144 sp_playto = game["playto"];
145 sp_dealer = game["dealer"];
146 sp_turn = game["turn"];
147 sp_player = game["player"];
148 sp_limit = game["limit"];
149 sp_acopy(sp_hands, game["hands"]);
150 sp_acopy(sp_players, game["players"]);
151 sp_acopy(sp_auths, game["auths"]);
152 sp_acopy(sp_share, game["share"]);
153 sp_acopy(sp_order, game["order"]);
154 sp_acopy(sp_scores, game["scores"]);
155 sp_acopy(sp_teams, game["teams"]);
158 sp_channel = game["channel"];
159 sp_log = game["log"];
160 sp_acopy(sp_notify, game["notify"]);
167 print strftime("%Y-%m-%d %H:%M:%S | ") sp_ugly(msg) >> "logs/" sp_log
168 fflush("logs/" sp_log)
171 function sp_pretty(cards, who)
174 gsub(/[0-9JQKA]*[sc]/, "\0031,00\002&\017", cards) # black
175 gsub(/[0-9JQKA]*[hd]/, "\0034,00\002&\017", cards) # red
177 if (!nounicode[who]) {
178 gsub(/s/, "\002♠", cards)
179 gsub(/h/, "\002♥", cards)
180 gsub(/d/, "\002♦", cards)
181 gsub(/c/, "\002♣", cards)
186 function sp_ugly(cards, who)
188 gsub(/[\2\17]|\3[14],00|/, "", cards)
189 gsub(/♠/, "s", cards)
190 gsub(/♥/, "h", cards)
191 gsub(/♦/, "d", cards)
192 gsub(/♣/, "c", cards)
196 function sp_next(who, prev)
199 sp_turn = who ? sp_players[who] : (sp_turn + 1) % 4
200 if (length(sp_order) == 4)
201 sp_player = sp_order[sp_turn]
205 function sp_shuf(i, mixed)
207 sp_usort(sp_players, mixed)
209 sp_order[i-1] = mixed[i]
210 sp_players[mixed[i]] = i-1
214 function sp_deal( shuf)
216 sp_say("/me deals the cards")
217 sp_usort(sp_deck, shuf)
218 for (i=1; i<=52; i++)
219 sp_hands[sp_order[i%4]][shuf[i]] = 1
221 sp_dealer = (sp_dealer+1)%4
223 sp_player = sp_order[sp_turn]
224 sp_say(sp_player ": you bid first!")
227 function sp_hand(to, who, sort, str)
229 asorti(sp_hands[who], sort, "sp_csort")
230 for (i=0; i<length(sort); i++)
231 str = str "" sprintf("%4s", sort[i])
232 gsub(/^ +| +$/, "", str)
233 return sp_pretty(str, to)
236 function sp_hasa(who, expr)
238 for (c in sp_hands[who]) {
245 function sp_type(card)
247 return substr(card, length(card))
250 function sp_usort(list, out) {
253 asorti(out, out, "@val_num_asc")
256 function sp_csort(i1,v1,i2,v2) {
257 return sp_deck[i1] > sp_deck[i2] ? +1 :
258 sp_deck[i1] < sp_deck[i2] ? -1 : 0;
261 function sp_winner( card, tmp)
263 for (card in sp_pile)
264 if (card !~ sp_suit && card !~ /s/)
266 asorti(sp_pile, tmp, "sp_csort")
267 #print "pile: " tmp[1] ">" tmp[2] ">" tmp[3] ">" tmp[4]
271 function sp_team(i, players)
273 #return "{" sp_order[i+0] "," sp_order[i+2] "}"
274 if ((i in sp_teams) && !players)
277 return sp_order[i+0] "/" sp_order[i+2]
280 function sp_bags(i, bags)
282 bags = sp_scores[i] % sp_limit
290 return sp_nil[who] == 0 ? sp_bids[who] :
291 sp_nil[who] == 1 ? "nil" :
292 sp_nil[who] == 2 ? "blind" : "n/a"
295 function sp_passer(who)
297 return sp_nil[(who+0)%4] == 2 || sp_nil[(who+1)%4] != 0 ||
298 sp_nil[(who+2)%4] == 2 || sp_nil[(who+3)%4] != 0
301 function sp_bidders( i, turn, bid, bids)
303 for (i = 0; i < 4; i++) {
304 turn = (sp_dealer + i) % 4
305 if (bid = sp_bid(turn))
306 bids = bids " " sp_order[turn] ":" bid
308 gsub(/^ +| +$/, "", bids)
312 function sp_extra( n, s)
314 n = sp_bids[0] + sp_bids[1] + sp_bids[2] + sp_bids[3];
315 s = n == 12 || n == 14 ? "" : "s";
317 return n<13 ? "Playing with " 13-n " bag" s "!" :
318 n>13 ? "Fighting for " n-13 " trick" s "!" : "No bags!";
321 function sp_score( bids, times, tricks)
323 for (i=0; i<2; i++) {
324 bids = sp_bids[i] + sp_bids[i+2]
325 tricks = sp_tricks[i] + sp_tricks[i+2]
327 times = int((sp_bags(i) + bags) / sp_limit)
329 sp_say(sp_team(i) " bag" (times>1?" way ":" ") "out")
330 sp_scores[i] -= sp_limit * 10 * times;
332 if (tricks >= bids) {
333 sp_say(sp_team(i) " make their bid: " tricks "/" bids)
334 sp_scores[i] += bids*10 + bags;
336 sp_say(sp_team(i) " go bust: " tricks "/" bids)
337 sp_scores[i] -= bids*10;
340 for (i=0; i<4; i++) {
343 sp_say(sp_order[i] " " \
344 (sp_nil[i] == 1 && !sp_tricks[i] ? "makes nil!" :
345 sp_nil[i] == 1 && sp_tricks[i] ? "fails at nil!" :
346 sp_nil[i] == 2 && !sp_tricks[i] ? "makes blind nil!" :
347 sp_nil[i] == 2 && sp_tricks[i] ? "fails miserably at blind nil!" :
349 sp_scores[i%2] += sp_limit * 10 * sp_nil[i] * \
350 (sp_tricks[i] == 0 ? 1 : -1)
352 if (sp_scores[0] > sp_scores[1])
353 sp_say(sp_team(0) " lead " sp_scores[0] " to " sp_scores[1] " of " sp_playto)
354 else if (sp_scores[1] > sp_scores[0])
355 sp_say(sp_team(1) " lead " sp_scores[1] " to " sp_scores[0] " of " sp_playto)
357 sp_say("tied at " sp_scores[0] " of " sp_playto)
360 function sp_play(card, winner, pi)
362 delete sp_hands[sp_from][card]
363 sp_pile[card] = sp_player
364 sp_piles = sp_piles (sp_piles?",":"") card
371 if (length(sp_pile) == 1)
372 sp_suit = sp_type(card)
375 if (length(sp_pile) == 4) {
377 pi = sp_players[sp_pile[winner]]
379 sp_say(sp_pile[winner] " wins with " sp_pretty(winner, FROM) \
380 " (" sp_pretty(sp_piles, FROM) ")")
381 sp_last["player"] = sp_pile[winner];
382 sp_last["pile"] = sp_piles;
383 sp_next(sp_pile[winner])
388 if (sp_tricks[0] + sp_tricks[1] + \
389 sp_tricks[2] + sp_tricks[3] == 13) {
390 sp_say("Round over!")
392 if ((sp_scores[0] >= sp_playto || sp_scores[1] >= sp_playto) &&
393 (sp_scores[0] != sp_scores[1])) {
395 winner = sp_scores[0] > sp_scores[1] ? 0 : 1
397 say(CHANNEL, sp_team(winner) " wins the game " \
398 sp_scores[winner] " to " sp_scores[looser])
399 say(CHANNEL, sp_order[winner+0] "++")
400 say(CHANNEL, sp_order[winner+2] "++")
404 if (sp_scores[0] == sp_scores[1] &&
405 sp_scores[0] >= sp_playto)
406 sp_say("It's a tie! Playing an extra round!");
414 function sp_delay(sec)
416 return (sec > 60*60*24 ? int(sec/60/60/24) "d " : "") \
417 (sec > 60*60 ? int(sec/60/60)%24 "h " : "") \
421 function sp_max(list, i, max)
423 for (i=0; i<length(list); i++)
424 if (max == "" || list[i] > max)
429 function sp_avg(list, i, sum)
431 for (i=0; i<length(list); i++)
433 return sum / length(list)
436 function sp_cur(list)
438 return list[length(list)-1]
441 function sp_stats(file, line, arr, time, user, turn, start, delay, short, extra)
444 while ((stat = getline line < file) > 0) {
446 if (!match(line, /^([0-9\- \:]*) \| (.*)$/, arr))
448 gsub(/[:-]/, " ", arr[1])
449 time = mktime(arr[1])
452 if (!match(arr[2], /^([^:]*): (.*)$/, arr))
456 # Record user latency
458 delay[turn][length(delay[turn])] = time - start
461 if (match(arr[2], /^(it is your|you .*(first|lead)!$)/, arr)) {
468 # Add current latency
470 delay[turn][length(delay[turn])] = systime() - start
471 debug("time: " (systime() - start))
476 reply("File does not exist: " file);
479 for (user in delay) {
480 short = length(user) <= 4 ? user : substr(user, 0, 4)
481 extra = (user != turn) ? "" : \
482 ", " sp_delay(sp_cur(delay[user])) " (cur)";
483 say("latency for " short \
484 ": " sp_delay(sp_avg(delay[user])) " (avg)" \
485 ", " sp_delay(sp_max(delay[user])) " (max)" extra)
491 cmd = "od -An -N4 -td4 /dev/random"
497 sp_load("var/sp_cur.json")
498 sp_sock = "/inet/udp/0/localhost/6173"
499 print "starting rhawk" |& sp_sock
501 # sp_say("Game restored.")
505 sp_from = AUTH in sp_auths ? sp_auths[AUTH] : \
506 AUTH in sp_share ? sp_share[AUTH] : FROM
507 sp_valid = sp_from && sp_from == sp_player
513 say("Spades! " sp_pretty("As,Ah,Ad,Ac", FROM))
518 sp_save("var/sp_save.json");
524 sp_load("var/sp_save.json");
530 say(".help spades -- play a game of spades")
533 /^\.help [Ss]pades$/ {
534 say("Spades -- play a game of spades")
535 say(".help game -- setup and administer the game")
536 say(".help play -- commands for playing spades")
537 say(".help auth -- control player authorization")
542 say(".newgame [score] -- start a game to <score> points, default 300")
543 say(".endgame -- abort the current game")
544 say(".savegame -- save the current game to disk")
545 say(".loadgame -- load the previously saved game")
550 say(".join -- join the current game")
551 say(".look -- look at your cards")
552 say(".bid [n] -- bid for <n> tricks")
553 say(".pass [card] -- pass a card to your partner")
554 say(".play [card] -- play a card")
555 say(".team [name] -- set your team name")
556 say(".last -- show who took the previous trick")
557 say(".turn -- check whose turn it is")
558 say(".bids -- check what everyone bid")
559 say(".tricks -- check how many trick have been taken")
560 say(".score -- check the score")
565 say(".auth [who] -- display authentication info for a user")
566 say(".allow [who] -- allow another person to play on your behalf")
567 say(".deny [who] -- prevent a previously allowed user from playing")
568 say(".show -- display which users can play for which players")
569 say(".notify [addr] -- email user when it is their turn")
575 /^\.deal (\w+) (.*)/ {
576 sp_say(FROM " is cheating for " $2)
578 for (i=3; i<=NF; i++)
584 /^\.order (\w+) ([0-4])/ {
585 sp_say(FROM " is cheating for " $2)
588 sp_player = sp_order[sp_turn]
592 sp_state == "play" &&
593 /^\.force (\w+) (\S+)$/ {
594 sp_say(FROM " is cheating for " $2)
602 match($0, /^\.newgame ?([1-9][0-9]*) *- *([1-9][0-9]*)$/, _arr) {
603 if (_arr[2] > _arr[1])
604 $0 = $1 " " int(rand() * (_arr[2]-_arr[1])+_arr[1])
607 /^\.newgame ?([1-9][0-9]*)?$/ {
608 if (sp_state != "new") {
609 reply("There is already a game in progress.")
613 sp_playto = $2 ? $2 : 300
614 sp_limit = sp_playto > 200 ? 10 : 5;
617 sp_log = strftime("%Y%m%d_%H%M%S.log")
618 sp_say(sp_owner " starts a game of Spades to " sp_playto " with " sp_limit " bags!")
622 (sp_from == sp_owner || AUTH == OWNER) &&
624 if (sp_state == "new") {
625 reply("There is no game in progress.")
627 sp_say(FROM " ends the game")
633 if (sp_state == "new") {
634 reply("There is no game in progress")
636 else if (sp_state == "play") {
637 reply("The game has already started")
639 else if (sp_state == "join" && sp_from in sp_players) {
640 reply("You are already playing")
642 else if (sp_state == "join") {
646 sp_auths[AUTH] = FROM
648 sp_say(FROM " joins the game!")
650 if (sp_state == "join" && sp_turn == 0) {
657 _who = $2 in USERS ? USERS[$2]["auth"] : ""
658 _str = _who && _who != $2 ? $2 " (" _who ")" : $2
659 if (sp_state ~ "new|join") {
660 reply("The game has not yet started")
662 else if (!(sp_from in sp_players)) {
663 reply("You are not playing")
666 reply(_str " is not logged in")
668 else if (_who in sp_players || _who in sp_auths) {
669 reply(_str " is a primary player")
671 else if (_who in sp_share) {
672 reply(_str " is already playing for " sp_share[_who])
675 sp_say(_str " can now play for " sp_from)
676 sp_share[_who] = sp_from
681 _who = $2 in USERS ? USERS[$2]["auth"] : $2
682 _str = _who && _who != $2 ? $2 " (" _who ")" : $2
683 if (sp_state ~ "new|join") {
684 reply("The game has not yet started")
686 else if (!(sp_from in sp_players)) {
687 reply("You are not playing")
689 else if (_who in sp_players || _who in sp_auths) {
690 reply(_str " is a primary player")
692 else if (!(_who in sp_share) || sp_share[_who] != sp_from) {
693 reply(_str " is not playing for " sp_from)
696 sp_say(_str " can no longer play for " sp_from)
697 delete sp_share[_who]
702 gsub(/^\.team */, "")
703 _team = sp_from in sp_players ? sp_players[sp_from] % 2 : 0
704 if (sp_state ~ "new|join") {
705 reply("The game has not yet started")
707 else if (!(sp_from in sp_players)) {
708 reply("You are not playing")
710 else if ($0 ~ /^[^a-zA-Z0-9]/) {
711 reply("Invalid team name")
713 else if ($0 ~ /^./) {
714 sp_teams[_team] = substr($0, 0, 32)
715 sp_say(sp_team(_team,1) " are now known as " sp_team(_team))
718 delete sp_teams[_team]
719 sp_say(sp_team(_team,1) " are boring")
724 if (!(sp_from in sp_players))
725 reply("You are not playing")
726 else if (sp_from == FROM)
727 say(FROM " has an existential crisis")
729 reply("You are playing for " sp_from);
733 if (sp_from in sp_notify)
734 reply("Your address is " sp_notify[sp_from])
736 reply("Your address is not set")
740 if (sp_from in sp_notify) {
741 reply("Removing address " sp_notify[sp_from])
742 delete sp_notify[sp_from]
744 reply("Your address is not set")
748 /^\.notify \S+@\S+.\S+$/ {
750 gsub(/[^a-zA-Z0-9_+@.-]/, "", _addr)
751 sp_notify[sp_from] = _addr
752 reply("Notifying you at " _addr)
755 sp_state ~ "(bid|pass|play)" &&
759 _lines[sp_share[_i]] = _lines[sp_share[_i]] " " _i
761 say(_i " allowed:" _lines[_i])
765 (sp_state == "bid" || sp_state == "play") &&
767 if (sp_from in sp_players)
768 reply("It is not your turn.")
770 reply("You are not playing.")
775 /^\.bid (0|[1-9][0-9]*)$/ {
776 if ($2 < 0 || $2 > 13) {
777 reply("You can only bid from 0 to 13")
781 if ($2 == 0 && !sp_looked[i]) {
782 sp_say(FROM " goes blind nil!")
784 } else if ($2 == 0) {
785 sp_say(FROM " goes nil!")
790 if (sp_turn != sp_dealer) {
791 sp_say(sp_player ": it is your bid! (" sp_bidders() ")")
793 sp_say(sp_extra() " (" sp_bidders() ")")
794 for (p in sp_players)
795 say(p, "You have: " sp_hand(p, p))
797 for (i=0; i<2; i++) {
799 sp_say(sp_team(i,1) ": select a card to pass " \
800 "(/msg " NICK " .pass <card>)")
804 if (sp_state == "play")
805 sp_say(sp_player ": you have the opening lead!")
810 sp_state == "pass" &&
813 _team = sp_from in sp_players ? sp_players[sp_from] % 2 : 0
815 # check validity and pass
816 if (!(sp_from in sp_players)) {
817 reply("You are not playing.")
819 else if (!sp_passer(_team)) {
820 reply("Your team did not go blind")
822 else if (sp_pass[sp_players[sp_from]]) {
823 reply("You have already passed a card")
825 else if (!(_card in sp_deck)) {
826 reply("Invalid card")
828 else if (!(_card in sp_hands[sp_from])) {
829 reply("You do not have that card")
832 sp_pass[sp_players[sp_from]] = $2
833 sp_say(FROM " passes a card")
836 # check for end of passing
837 if ((!sp_passer(0) || (sp_pass[0] && sp_pass[2])) &&
838 (!sp_passer(1) || (sp_pass[1] && sp_pass[3]))) {
842 delete sp_hands[sp_order[i]][_card]
843 sp_hands[sp_order[_partner]][_card] = 1
845 sp_say("Cards have been passed!")
846 sp_say(sp_player ": you have the opening lead!")
847 for (p in sp_players)
848 say(p, "You have: " sp_hand(p, p))
853 sp_state ~ "(bid|pass|play)" &&
855 if (!(sp_from in sp_players)) {
856 reply("You are not playing.")
858 sp_looked[sp_players[sp_from]] = 1
859 say(FROM, "You have: " sp_hand(FROM, sp_from))
864 sp_state == "play" &&
867 gsub(/[^A-Za-z0-9]/, "", _card);
868 if (!(_card in sp_deck)) {
869 reply("Invalid card")
871 else if (sp_suit && _card !~ sp_suit && sp_hasa(sp_from, sp_suit)) {
872 reply("You must follow suit (" sp_suit ")")
874 else if (_card ~ /s/ && length(sp_hands[sp_from]) == 13 && sp_hasa(sp_from, "[^s]$")) {
875 reply("You cannot trump on the first hand")
877 else if (_card ~ /s/ && length(sp_pile) == 0 && sp_hasa(sp_from, "[^s]$") && !sp_broken) {
878 reply("Spades have not been broken")
880 else if (!(_card in sp_hands[sp_from])) {
881 reply("You do not have that card")
885 if (sp_state == "play") {
886 if (length(sp_hands[sp_from]))
887 say(FROM, "You have: " sp_hand(FROM, sp_from))
889 sp_say(sp_player ": it is your turn! " \
890 "(" sp_pretty(sp_piles, sp_player) ")")
892 sp_say(sp_player ": it is your turn!")
897 /^\.last/ && sp_state == "play" {
898 if (!isarray(sp_last))
899 say("No tricks have been taken!");
901 say(sp_last["player"] " took " \
902 sp_pretty(sp_last["pile"], FROM));
905 /^\.bids/ && sp_state == "bid" ||
906 /^\.turn/ && sp_state ~ "(bid|pass|play)" {
908 _pile = sp_pretty(sp_piles, FROM)
913 _notify[0] = sp_player
914 for (_i in sp_share) {
915 if (sp_share[_i] != sp_player)
918 _extra = _extra " " _i "!"
920 _notify[length(_notify)] = _i
923 if (sp_state == "bid" && !_bids)
924 say("It is " sp_player "'s bid!" _extra)
925 if (sp_state == "bid" && _bids)
926 say("It is " sp_player "'s bid!" _extra " (" _bids ")")
927 if (sp_state == "play" && !_pile)
928 say("It is " sp_player "'s turn!" _extra)
929 if (sp_state == "play" && _pile)
930 say("It is " sp_player "'s turn!" _extra " (" _pile ")")
932 if (sp_state == "bid" || sp_state == "play") {
933 for (_i in _notify) {
934 if (_notify[_i] in sp_notify) {
935 _bids = _bids ? _bids : "none"
936 _pile = _pile ? sp_piles : "none"
937 mail_send(sp_notify[_notify[_i]], \
938 "It is your " sp_state "!", \
939 "Bids so far: " _bids "\n" \
940 "Cards played: " _pile)
941 say("Notified " _notify[_i] " at " sp_notify[_notify[_i]])
943 say("No email address for " _notify[_i])
948 for (_i=0; sp_state == "pass" && _i<4; _i++)
949 if (sp_passer(_i) && !sp_pass[_i])
950 say("Waiting for " sp_order[_i] " to pass a card!")
953 /^\.bids$/ && sp_state ~ "(pass|play)" {
954 say(sp_order[0] " bid " sp_bid(0) ", " \
955 sp_order[2] " bid " sp_bid(2) ", " \
956 "total: " sp_bids[0] + sp_bids[2])
957 say(sp_order[1] " bid " sp_bid(1) ", " \
958 sp_order[3] " bid " sp_bid(3) ", " \
959 "total: " sp_bids[1] + sp_bids[3])
962 /^\.tricks$/ && sp_state == "play" {
963 say(sp_order[0] " took " int(sp_tricks[0]) "/" sp_bid(0) ", " \
964 sp_order[2] " took " int(sp_tricks[2]) "/" sp_bid(2))
965 say(sp_order[1] " took " int(sp_tricks[1]) "/" sp_bid(1) ", " \
966 sp_order[3] " took " int(sp_tricks[3]) "/" sp_bid(3))
969 (TO == NICK || DST == sp_channel) &&
970 /^\.(score|status)$/ {
971 if (sp_state == "new") {
972 say("There is no game in progress")
974 if (sp_state ~ "join|bid|pass|play") {
976 sp_playto " points, " \
979 if (sp_state == "join") {
980 say("Waiting for players: " \
981 sp_order[0] " " sp_order[1] " " \
982 sp_order[2] " " sp_order[3])
984 if (sp_state ~ "bid|pass|play") {
985 say(sp_team(0) ": " \
986 int(sp_scores[0]) " points, " \
987 int(sp_bags(0)) " bags")
988 say(sp_team(1) ": " \
989 int(sp_scores[1]) " points, " \
990 int(sp_bags(1)) " bags")
994 (TO == NICK || DST == sp_channel) &&
996 say("http://pileus.org/andy/spades/" sp_log)
999 (TO == NICK || DST == sp_channel) &&
1001 sp_stats("logs/" sp_log);
1004 (TO == NICK || DST == sp_channel) &&
1005 /^\.stats ([0-9]+_[0-9]+)(\.log)$/ {
1006 gsub(/\.log$/, "", $2);
1007 sp_stats("logs/" $2 ".log");
1010 /^\.((new|end|load)game|join|look|bid|pass|play|allow|deny|team|notify)/ {
1011 sp_save("var/sp_cur.json");