]> Pileus Git - ~andy/rhawk/blob - spades.awk
c7cf9cbe87c4c250f815d53bdadfaf6419bf6077
[~andy/rhawk] / spades.awk
1 # For saving
2 @include "json.awk"
3
4 # Functions
5 function sp_init(cards, tmp0, tmp1)
6 {
7         # Init deck
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"
12         split(cards, tmp0)
13         for (i=1; i<=length(tmp0); i++)
14                 sp_deck[tmp0[i]] = i
15 }
16
17 function sp_reset(type)
18 {
19         # Per message
20         if (type <  0) {
21                 sp_from     = ""    #    The speakers player name
22                 sp_valid    = ""    #    It is the speaker turn
23         }
24
25         # Per hand
26         if (type >= 0) {
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
30         }
31
32         # Per round
33         if (type >= 1) {
34                 sp_state    = "bid" #     {new,join,bid,pass,play}
35                 sp_broken   = 0     #     Whether spades are broken
36                 delete sp_looked    # [i] Whether a player has looked a their cards
37                 delete sp_bids      # [i] Each players bid
38                 delete sp_nil       # [i] Nil multiplier 0=regular, 1=nil, 2=blind
39                 delete sp_pass      # [i] Cards to pass
40                 delete sp_tricks    # [i] Tricks this round
41         }
42
43         # Per game
44         if (type >= 2) {
45                 sp_channel  = ""    #     channel to play in
46                 sp_state    = "new" #     {new,join,bid,pass,play}
47                 sp_owner    = ""    #     Who started the game
48                 sp_playto   = 0     #     Score the game will go to
49                 sp_dealer   =-1     #     Who is dealing this round
50                 sp_turn     = 0     #     Index of who's turn it is
51                 sp_player   = ""    #     Who's turn it is
52                 sp_limit    = 10    #     Bag out limit / nil bonus
53                 delete sp_hands     # [p] Each players cards
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         }
60 }
61
62 function sp_acopy(dst, src,     key)
63 {
64         if (isarray(src))
65                 for (key in src)
66                         json_copy(dst, key, src[key])
67 }
68
69 function sp_save(file,  game)
70 {
71         # Per hand
72         game["suit"]    = sp_suit;
73         game["piles"]   = sp_piles;
74         json_copy(game, "pile",    sp_pile);
75
76         # Per round
77         game["state"]   = sp_state;
78         game["broken"]  = sp_broken;
79         json_copy(game, "looked",  sp_looked);
80         json_copy(game, "bids",    sp_bids);
81         json_copy(game, "nil",     sp_nil);
82         json_copy(game, "pass",    sp_pass);
83         json_copy(game, "tricks",  sp_tricks);
84
85         # Per game
86         game["channel"] = sp_channel;
87         game["owner"]   = sp_owner;
88         game["playto"]  = sp_playto;
89         game["dealer"]  = sp_dealer;
90         game["turn"]    = sp_turn;
91         game["player"]  = sp_player;
92         game["limit"]   = sp_limit;
93         json_copy(game, "hands",   sp_hands);
94         json_copy(game, "players", sp_players);
95         json_copy(game, "auths",   sp_auths);
96         json_copy(game, "share",   sp_share);
97         json_copy(game, "order",   sp_order);
98         json_copy(game, "scores",  sp_scores);
99
100         # Save
101         json_save(file, game);
102 }
103
104 function sp_load(file,  game)
105 {
106         # Load
107         if (!json_load(file, game))
108                 return
109
110         # Per hand
111         sp_suit    = game["suit"];
112         sp_piles   = game["piles"];
113         sp_acopy(sp_pile,    game["pile"]);
114
115         # Per round
116         sp_state   = game["state"];
117         sp_broken  = game["broken"];
118         sp_acopy(sp_looked,  game["looked"]);
119         sp_acopy(sp_bids,    game["bids"]);
120         sp_acopy(sp_nil,     game["nil"]);
121         sp_acopy(sp_pass,    game["pass"]);
122         sp_acopy(sp_tricks,  game["tricks"]);
123
124         # Per game
125         sp_channel = game["channel"];
126         sp_owner   = game["owner"];
127         sp_playto  = game["playto"];
128         sp_dealer  = game["dealer"];
129         sp_turn    = game["turn"];
130         sp_player  = game["player"];
131         sp_limit   = game["limit"];
132         sp_acopy(sp_hands,   game["hands"]);
133         sp_acopy(sp_players, game["players"]);
134         sp_acopy(sp_auths,   game["auths"]);
135         sp_acopy(sp_share,   game["share"]);
136         sp_acopy(sp_order,   game["order"]);
137         sp_acopy(sp_scores,  game["scores"]);
138 }
139
140 function sp_pretty(cards, who)
141 {
142         if (!nocolor[who]) {
143                 gsub(/[0-9JQKA]*[sc]/, "\0031,00\002&\017", cards) # black
144                 gsub(/[0-9JQKA]*[hd]/, "\0034,00\002&\017", cards) # red
145         }
146         if (!nounicode[who]) {
147                 gsub(/s/, "\002♠", cards)
148                 gsub(/h/, "\002♥", cards)
149                 gsub(/d/, "\002♦", cards)
150                 gsub(/c/, "\002♣", cards)
151         }
152         return cards
153 }
154
155 function sp_next(who, prev)
156 {
157         prev      = sp_turn
158         sp_turn   = who ? sp_players[who] : (sp_turn + 1) % 4
159         if (length(sp_order) == 4)
160                 sp_player = sp_order[sp_turn]
161         return prev
162 }
163
164 function sp_shuf(i, mixed)
165 {
166         sp_usort(sp_players, mixed)
167         for (i in mixed) {
168                 sp_order[i-1] = mixed[i]
169                 sp_players[mixed[i]] = i-1
170         }
171 }
172
173 function sp_deal(       shuf)
174 {
175         say("/me deals the cards")
176         sp_usort(sp_deck, shuf)
177         for (i=1; i<=52; i++)
178                 sp_hands[sp_order[i%4]][shuf[i]] = 1
179         sp_state  = "bid"
180         sp_dealer = (sp_dealer+1)%4
181         sp_turn   =  sp_dealer
182         sp_player =  sp_order[sp_turn]
183         say("Bidding starts with " sp_player "!")
184 }
185
186 function sp_hand(to, who,       sort, str)
187 {
188         asorti(sp_hands[who], sort, "sp_csort")
189         for (i=0; i<length(sort); i++)
190                 str = str "" sprintf("%4s", sort[i])
191         gsub(/^ +| +$/, "", str)
192         return sp_pretty(str, to)
193 }
194
195 function sp_hasa(who, expr)
196 {
197         for (c in sp_hands[who]) {
198                 if (c ~ expr)
199                         return 1
200         }
201         return 0
202 }
203
204 function sp_type(card)
205 {
206         return substr(card, length(card))
207 }
208
209 function sp_usort(list, out) {
210         for (i in list)
211                 out[i] = rand()
212         asorti(out, out, "@val_num_asc")
213 }
214
215 function sp_csort(i1,v1,i2,v2) {
216         return sp_deck[i1] > sp_deck[i2] ? +1 :
217                sp_deck[i1] < sp_deck[i2] ? -1 : 0;
218 }
219
220 function sp_winner(     card, tmp)
221 {
222         for (card in sp_pile)
223                 if (card !~ sp_suit && card !~ /s/)
224                         delete sp_pile[card]
225         asorti(sp_pile, tmp, "sp_csort")
226         #print "pile: " tmp[1] ">" tmp[2] ">" tmp[3] ">" tmp[4]
227         return tmp[1]
228 }
229
230 function sp_team(i)
231 {
232         #return "{" sp_order[i+0] "," sp_order[i+2] "}"
233         return sp_order[i+0] "/" sp_order[i+2]
234 }
235
236 function sp_bags(i,     bags)
237 {
238         bags = sp_scores[i] % sp_limit
239         if (bags < 0)
240                 bags += sp_limit
241         return bags
242 }
243
244 function sp_bid(who)
245 {
246         return sp_nil[who] == 0 ? sp_bids[who] :
247                sp_nil[who] == 1 ? "nil"        :
248                sp_nil[who] == 2 ? "blind"      : "n/a"
249 }
250
251 function sp_bidders(    i, turn, bid, bids)
252 {
253         for (i = 0; i < 4; i++) {
254                 turn = (sp_dealer + i) % 4
255                 if (bid = sp_bid(turn))
256                         bids = bids " " sp_order[turn] ":" bid
257         }
258         gsub(/^ +| +$/, "", bids)
259         return bids
260 }
261
262 function sp_score(      bids, times, tricks)
263 {
264         for (i=0; i<2; i++) {
265                 bids   = sp_bids[i]   + sp_bids[i+2]
266                 tricks = sp_tricks[i] + sp_tricks[i+2]
267                 bags   = tricks - bids
268                 times  = int((sp_bags(i) + bags) / sp_limit)
269                 if (times > 0) {
270                         say(sp_team(i) " bag" (times>1?" way ":" ") "out")
271                         sp_scores[i] -= sp_limit * 10 * times;
272                 }
273                 if (tricks >= bids) {
274                         say(sp_team(i) " make their bid: " tricks "/" bids)
275                         sp_scores[i] += bids*10 + bags;
276                 } else {
277                         say(sp_team(i) " go bust: " tricks "/" bids)
278                         sp_scores[i] -= bids*10;
279                 }
280         }
281         for (i=0; i<4; i++) {
282                 if (!sp_nil[i])
283                         continue
284                 say(sp_order[i] " " \
285                     (sp_nil[i] == 1 && !sp_tricks[i] ? "makes nil!"       :
286                      sp_nil[i] == 1 &&  sp_tricks[i] ? "fails at nil!"    :
287                      sp_nil[i] == 2 && !sp_tricks[i] ? "makes blind nil!" :
288                      sp_nil[i] == 2 &&  sp_tricks[i] ? "fails miserably at blind nil!" :
289                                                        "unknown"))
290                 sp_scores[i%2] += sp_limit * 10 * sp_nil[i] * \
291                         (sp_tricks[i] == 0 ? 1 : -1)
292         }
293 }
294
295 function sp_play(card,  winner, pi)
296 {
297         delete sp_hands[sp_from][card]
298         sp_pile[card] = sp_player
299         sp_piles      = sp_piles (sp_piles?",":"") card
300         sp_next()
301
302         if (card ~ /s/)
303                 sp_broken = 1
304
305         # Start hand
306         if (length(sp_pile) == 1)
307                 sp_suit = sp_type(card)
308
309         # Finish hand
310         if (length(sp_pile) == 4) {
311                 winner = sp_winner()
312                 pi     = sp_players[sp_pile[winner]]
313                 sp_tricks[pi]++
314                 say(sp_pile[winner] " wins with " sp_pretty(winner, FROM) \
315                     " (" sp_pretty(sp_piles, FROM) ")")
316                 sp_next(sp_pile[winner])
317                 sp_reset(0)
318         }
319
320         # Finish round
321         if (sp_tricks[0] + sp_tricks[1] + \
322             sp_tricks[2] + sp_tricks[3] == 13) {
323                 say("Round over!")
324                 sp_score()
325                 if (sp_scores[0] >= sp_playto || sp_scores[1] >= sp_playto &&
326                     sp_scores[0]              != sp_scores[1]) {
327                         say("Game over!")
328                         winner = sp_scores[0] > sp_scores[1] ? 0 : 1
329                         looser = !winner
330                         say(CHANNEL, sp_team(winner) " wins the game " \
331                             sp_scores[winner] " to " sp_scores[looser])
332                         say(CHANNEL, sp_order[winner+0] "++")
333                         say(CHANNEL, sp_order[winner+2] "++")
334                         sp_reset(2)
335
336                 } else {
337                         if (sp_scores[0] == sp_scores[1] && 
338                             sp_scores[0] >= sp_playto)
339                                 say("It's tie! Playing an extra round!");
340                         sp_reset(1)
341                         sp_deal()
342                 }
343         }
344 }
345
346 # Misc
347 BEGIN {
348         cmd = "od -An -N4 -td4 /dev/random"
349         cmd | getline seed
350         close(cmd)
351         srand(seed)
352         sp_init()
353         sp_reset(2)
354         sp_load("var/sp_cur.json");
355         #if (sp_channel)
356         #       say(sp_channel, "Game restored.")
357 }
358
359 // {
360         sp_from  = AUTH in sp_auths ? sp_auths[AUTH] : \
361                    AUTH in sp_share ? sp_share[AUTH] : FROM
362         sp_valid = sp_from && sp_from == sp_player
363 }
364
365 CMD == "PRIVMSG" &&
366 ! /help/ &&
367 /[Ss]pades/ {
368         say("Spades! " sp_pretty("As,Ah,Ad,Ac", FROM))
369 }
370
371 AUTH == OWNER &&
372 /^\.savegame/ {
373         sp_save("var/sp_save.json");
374         say("Game saved.")
375 }
376
377 AUTH == OWNER &&
378 /^\.loadgame/ {
379         sp_load("var/sp_save.json");
380         say("Game loaded.")
381 }
382
383 # Help
384 /^\.help [Ss]pades$/ {
385         say("Spades -- play a game of spades")
386         say(".help game -- setup and administer the game")
387         say(".help play -- commands for playing spades")
388         say(".help auth -- control player authorization")
389         next
390 }
391
392 /^\.help game$/ {
393         say(".newgame [score] -- start a game to <score> points, default 500")
394         say(".endgame -- abort the current game")
395         say(".savegame -- save the current game to disk")
396         say(".loadgame -- load the previously saved game")
397         next
398 }
399
400 /^\.help play$/ {
401         say(".join -- join the current game")
402         say(".look -- look at your cards")
403         say(".bid [n] -- bid for <n> tricks")
404         say(".pass [card] -- pass a card to your partner")
405         say(".play [card] -- play a card")
406         say(".turn -- check whose turn it is")
407         say(".bids -- check what everyone bid")
408         say(".tricks -- check how many trick have been taken")
409         say(".score -- check the score")
410         next
411 }
412
413 /^\.help auth$/ {
414         say(".auth [who] -- display authentication info for a user")
415         say(".allow [who] -- allow another person to play on your behalf")
416         say(".deny [who] -- prevent a previously allowed user from playing")
417         say(".show -- display which users can play for which players")
418         next
419 }
420
421 # Debugging
422 AUTH == OWNER &&
423 /^\.deal (\w+) (.*)/ {
424         say(sp_channel, FROM " is cheating for " $2)
425         delete sp_hands[$2]
426         for (i=3; i<=NF; i++)
427                 sp_hands[$2][$i] = 1
428         next
429 }
430
431 AUTH == OWNER &&
432 sp_state == "play" &&
433 /^\.force (\w+) (\S+)$/ {
434         say(sp_channel, FROM " is cheating for " $2)
435         sp_from = $2
436         sp_play($3)
437         next
438 }
439
440
441 # Setup
442 match($0, /^\.newgame ?([0-9]+) *- *([0-9]+)$/, _arr) {
443         if (_arr[2] > _arr[1])
444                 $0 = $1 " " int(rand() * (_arr[2]-_arr[1])+_arr[1])
445 }
446
447 /^\.newgame ?([0-9]+)?$/ {
448         if (sp_state != "new") {
449                 reply("There is already a game in progress.")
450         } else {
451                 $1         = ".join"
452                 sp_owner   = FROM
453                 sp_playto  = $2 ? $2 : 200
454                 sp_limit   = sp_playto > 200 ? 10 : 5;
455                 sp_state   = "join"
456                 sp_channel = DST
457                 say(sp_owner " starts a game of Spades to " sp_playto " with " sp_limit " bags!")
458         }
459 }
460
461 (sp_from == sp_owner || AUTH == OWNER) &&
462 /^\.endgame$/ {
463         if (sp_state == "new") {
464                 reply("There is no game in progress.")
465         } else {
466                 say(FROM " ends the game")
467                 sp_reset(2)
468         }
469 }
470
471 /^\.join$/ {
472         if (sp_state == "new") {
473                 reply("There is no game in progress")
474         }
475         else if (sp_state == "play") {
476                 reply("The game has already started")
477         }
478         else if (sp_state == "join" && sp_from in sp_players) {
479                 reply("You are already playing")
480         }
481         else if (sp_state == "join") {
482                 i = sp_next()
483                 sp_players[FROM] = i
484                 if (AUTH)
485                         sp_auths[AUTH] = FROM
486                 sp_order[i] = FROM
487                 say(FROM " joins the game!")
488         }
489         if (sp_state == "join" && sp_turn == 0) {
490                 sp_shuf()
491                 sp_deal()
492         }
493 }
494
495 /^\.allow \S+$/ {
496         _who = $2 in USERS ? USERS[$2]["auth"] : ""
497         _str = _who && _who != $2 ? $2 " (" _who ")" : $2
498         if (sp_state ~ "new|join") {
499                 reply("The game has not yet started")
500         }
501         else if (!(sp_from in sp_players)) {
502                 reply("You are not playing")
503         }
504         else if (!_who) {
505                 reply(_str " is not logged in")
506         }
507         else if (_who in sp_players || _who in sp_auths) {
508                 reply(_str " is a primary player")
509         }
510         else if (_who in sp_share) {
511                 reply(_str " is already playing for " sp_share[_who])
512         }
513         else {
514                 reply(_str " can now play for " sp_from)
515                 sp_share[_who] = sp_from
516         }
517 }
518
519 /^\.deny \S+$/ {
520         _who = $2 in USERS ? USERS[$2]["auth"] : $2
521         _str = _who && _who != $2 ? $2 " (" _who ")" : $2
522         if (sp_state ~ "new|join") {
523                 reply("The game has not yet started")
524         }
525         else if (!(sp_from in sp_players)) {
526                 reply("You are not playing")
527         }
528         else if (_who in sp_players || _who in sp_auths) {
529                 reply(_str " is a primary player")
530         }
531         else if (!(_who in sp_share) || sp_share[_who] != sp_from) {
532                 reply(_str " is not playing for " sp_from)
533         }
534         else {
535                 reply(_str " can no longer play for " sp_from)
536                 delete sp_share[_who]
537         }
538 }
539
540 sp_state ~ "(bid|pass|play)" &&
541 /^\.show/ {
542         for (_i in sp_share)
543                 say(_i " can play for " sp_share[_i]);
544 }
545
546 !sp_valid &&
547 (sp_state == "bid" || sp_state == "play") &&
548 /^\.(bid|play)\>/ {
549         if (sp_from in sp_players)
550                 say(".slap " FROM ", it is not your turn.")
551         else
552                 say(".slap " FROM ", you are not playing.")
553 }
554
555 sp_valid &&
556 sp_state == "bid" &&
557 /^\.bid [0-9]+$/ {
558         if ($2 < 0 || $2 > 13) {
559                 say("You can only bid from 0 to 13")
560         } else {
561                 i = sp_next()
562                 sp_bids[i] = $2
563                 if ($2 == 0 && !sp_looked[i]) {
564                         say(FROM " goes blind nil!")
565                         sp_nil[i] = 2
566                 } else if ($2 == 0) {
567                         say(FROM " goes nil!")
568                         sp_nil[i] = 1
569                 } else {
570                         sp_nil[i] = 0
571                 }
572                 if (sp_turn != sp_dealer) {
573                         say("Bidding goes to " sp_player "!")
574                 } else {
575                         for (p in sp_players)
576                                 say(p, "You have: " sp_hand(p, p))
577                         sp_state = "play"
578                         for (i=0; i<2; i++) {
579                                 if (sp_nil[(i+0)%4] == 2 || sp_nil[(i+2)%4] == 2 ||
580                                     sp_nil[(i+1)%4] != 0 || sp_nil[(i+3)%4] != 0) {
581                                         say(sp_team(i) ": select a card to pass " \
582                                             "(/msg " NICK " .pass <card>)")
583                                         sp_state = "pass"
584                                 }
585                         }
586                         if (sp_state == "play")
587                                 say("Play starts with " sp_player "!")
588                 }
589         }
590 }
591
592 sp_state == "pass" &&
593 /^\.pass (\S+)$/ {
594         _card = $2
595         _team = sp_from in sp_players ? sp_players[sp_from] % 2 : 0
596
597         # check validity and pass
598         if (!(sp_from in sp_players)) {
599                 say(".slap " FROM ", you are not playing.")
600         }
601         else if (sp_nil[_team] == 1 && sp_nil[_team+2] == 1) {
602                 reply("Your team did not go blind")
603         }
604         else if (sp_pass[sp_players[sp_from]]) {
605                 reply("You have already passed a card")
606         }
607         else if (!(_card in sp_deck)) {
608                 reply("Invalid card")
609         }
610         else if (!(_card in sp_hands[sp_from])) {
611                 reply("You do not have that card")
612         }
613         else {
614                 sp_pass[sp_players[sp_from]] = $2
615                 say(sp_channel, FROM " passes a card")
616         }
617
618         # check for end of passing
619         if (((sp_nil[0] == 1 && sp_nil[2] == 1) || (sp_pass[0] && sp_pass[2])) &&
620             ((sp_nil[1] == 1 && sp_nil[3] == 1) || (sp_pass[1] && sp_pass[3]))) {
621                 for (i in sp_pass) {
622                         _partner = (i+2)%4
623                         _card    = sp_pass[i]
624                         delete sp_hands[sp_order[i]][_card]
625                         sp_hands[sp_order[_partner]][_card] = 1
626                 }
627                 say(sp_channel, "Cards have been passed, play starts with " sp_player "!")
628                 for (p in sp_players)
629                         say(p, "You have: " sp_hand(p, p))
630                 sp_state = "play"
631         }
632 }
633
634 sp_state ~ "(bid|pass|play)" &&
635 /^\.look$/ {
636         if (!(sp_from in sp_players)) {
637                 say(".slap " FROM ", you are not playing.")
638         } else {
639                 sp_looked[sp_players[sp_from]] = 1
640                 say(FROM, "You have: " sp_hand(FROM, sp_from))
641         }
642 }
643
644 sp_valid &&
645 sp_state == "play" &&
646 /^\.play (\S+)/ {
647         _card = $2
648         gsub(/[^A-Za-z0-9]/, "", _card);
649         if (!(_card in sp_deck)) {
650                 reply("Invalid card")
651         }
652         else if (!(_card in sp_hands[sp_from])) {
653                 reply("You do not have that card")
654         }
655         else if (sp_suit && _card !~ sp_suit && sp_hasa(sp_from, sp_suit)) {
656                 reply("You must follow suit (" sp_suit ")")
657         }
658         else if (_card ~ /s/ && length(sp_hands[sp_from]) == 13 && sp_hasa(sp_from, "[^s]$")) {
659                 reply("You cannot trump on the first hand")
660         }
661         else if (_card ~ /s/ && length(sp_pile) == 0 && sp_hasa(sp_from, "[^s]$") && !sp_broken) {
662                 reply("Spades have not been broken")
663         }
664         else {
665                 sp_play(_card)
666                 if (sp_state == "play") {
667                         if (length(sp_hands[sp_from]))
668                                 say(FROM, "You have: " sp_hand(FROM, sp_from))
669                         if (sp_piles)
670                                 say(sp_player ": it is your turn! " \
671                                     "(" sp_pretty(sp_piles, sp_player) ")")
672                         else
673                                 say(sp_player ": it is your turn!")
674                 }
675         }
676 }
677
678 /^\.bids/ && sp_state == "bid" ||
679 /^\.turn/ && sp_state ~ "(bid|pass|play)" {
680         _bids = sp_bidders()
681         _pile = sp_pretty(sp_piles, FROM)
682         if (sp_state == "bid" && !_bids)
683                 say("It is " sp_player "'s bid!")
684         if (sp_state == "bid" && _bids)
685                 say("It is " sp_player "'s bid! (" _bids ")")
686         if (sp_state == "play" && !_pile)
687                 say("It is " sp_player "'s turn!")
688         if (sp_state == "play" && _pile)
689                 say("It is " sp_player "'s turn! (" _pile ")")
690         for (_i=0; sp_state == "pass" && _i<4; _i++)
691                 if ((sp_nil[_i%2+0]!=1 || sp_nil[_i%2+2]!=1) && !sp_pass[_i])
692                         say("Waiting for " sp_order[_i] " to pass a card!")
693 }
694
695 /^\.bids$/ && sp_state ~ "(pass|play)" {
696         say(sp_order[0] " bid " sp_bid(0) ", " \
697             sp_order[2] " bid " sp_bid(2) ", " \
698             "total: " sp_bids[0] + sp_bids[2])
699         say(sp_order[1] " bid " sp_bid(1) ", " \
700             sp_order[3] " bid " sp_bid(3) ", " \
701             "total: " sp_bids[1] + sp_bids[3])
702 }
703
704 /^\.tricks$/ && sp_state == "play" {
705         say(sp_order[0] " took " int(sp_tricks[0]) "/" sp_bid(0) ", " \
706             sp_order[2] " took " int(sp_tricks[2]) "/" sp_bid(2))
707         say(sp_order[1] " took " int(sp_tricks[1]) "/" sp_bid(1) ", " \
708             sp_order[3] " took " int(sp_tricks[3]) "/" sp_bid(3))
709 }
710
711 (TO == NICK || DST == sp_channel) &&
712 /^\.(score|status)$/ {
713         if (sp_state == "new") {
714                 say("There is no game in progress")
715         }
716         if (sp_state ~ "join|bid|pass|play") {
717                 say("Playing to: " \
718                     sp_playto " points, " \
719                     sp_limit  " bags")
720         }
721         if (sp_state == "join") {
722                 say("Waiting for players: " \
723                     sp_order[0] " " sp_order[1] " " \
724                     sp_order[2] " " sp_order[3])
725         }
726         if (sp_state ~ "bid|pass|play") {
727                 say(sp_team(0) ": " \
728                     int(sp_scores[0]) " points, " \
729                     int(sp_bags(0))   " bags")
730                 say(sp_team(1) ": " \
731                     int(sp_scores[1]) " points, " \
732                     int(sp_bags(1))   " bags")
733         }
734 }
735
736 /^\.((new|end|load)game|join|look|bid|pass|play)/ {
737         sp_save("var/sp_cur.json");
738 }