]> Pileus Git - ~andy/rhawk/blob - spades.awk
Try to fix nil passing
[~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_passer(who)
252 {
253         return sp_nil[(who+0)%4] == 2 || sp_nil[(who+1)%4] != 0 ||
254                sp_nil[(who+2)%4] == 2 || sp_nil[(who+3)%4] != 0
255 }
256
257 function sp_bidders(    i, turn, bid, bids)
258 {
259         for (i = 0; i < 4; i++) {
260                 turn = (sp_dealer + i) % 4
261                 if (bid = sp_bid(turn))
262                         bids = bids " " sp_order[turn] ":" bid
263         }
264         gsub(/^ +| +$/, "", bids)
265         return bids
266 }
267
268 function sp_score(      bids, times, tricks)
269 {
270         for (i=0; i<2; i++) {
271                 bids   = sp_bids[i]   + sp_bids[i+2]
272                 tricks = sp_tricks[i] + sp_tricks[i+2]
273                 bags   = tricks - bids
274                 times  = int((sp_bags(i) + bags) / sp_limit)
275                 if (times > 0) {
276                         say(sp_team(i) " bag" (times>1?" way ":" ") "out")
277                         sp_scores[i] -= sp_limit * 10 * times;
278                 }
279                 if (tricks >= bids) {
280                         say(sp_team(i) " make their bid: " tricks "/" bids)
281                         sp_scores[i] += bids*10 + bags;
282                 } else {
283                         say(sp_team(i) " go bust: " tricks "/" bids)
284                         sp_scores[i] -= bids*10;
285                 }
286         }
287         for (i=0; i<4; i++) {
288                 if (!sp_nil[i])
289                         continue
290                 say(sp_order[i] " " \
291                     (sp_nil[i] == 1 && !sp_tricks[i] ? "makes nil!"       :
292                      sp_nil[i] == 1 &&  sp_tricks[i] ? "fails at nil!"    :
293                      sp_nil[i] == 2 && !sp_tricks[i] ? "makes blind nil!" :
294                      sp_nil[i] == 2 &&  sp_tricks[i] ? "fails miserably at blind nil!" :
295                                                        "unknown"))
296                 sp_scores[i%2] += sp_limit * 10 * sp_nil[i] * \
297                         (sp_tricks[i] == 0 ? 1 : -1)
298         }
299 }
300
301 function sp_play(card,  winner, pi)
302 {
303         delete sp_hands[sp_from][card]
304         sp_pile[card] = sp_player
305         sp_piles      = sp_piles (sp_piles?",":"") card
306         sp_next()
307
308         if (card ~ /s/)
309                 sp_broken = 1
310
311         # Start hand
312         if (length(sp_pile) == 1)
313                 sp_suit = sp_type(card)
314
315         # Finish hand
316         if (length(sp_pile) == 4) {
317                 winner = sp_winner()
318                 pi     = sp_players[sp_pile[winner]]
319                 sp_tricks[pi]++
320                 say(sp_pile[winner] " wins with " sp_pretty(winner, FROM) \
321                     " (" sp_pretty(sp_piles, FROM) ")")
322                 sp_next(sp_pile[winner])
323                 sp_reset(0)
324         }
325
326         # Finish round
327         if (sp_tricks[0] + sp_tricks[1] + \
328             sp_tricks[2] + sp_tricks[3] == 13) {
329                 say("Round over!")
330                 sp_score()
331                 if (sp_scores[0] >= sp_playto || sp_scores[1] >= sp_playto &&
332                     sp_scores[0]              != sp_scores[1]) {
333                         say("Game over!")
334                         winner = sp_scores[0] > sp_scores[1] ? 0 : 1
335                         looser = !winner
336                         say(CHANNEL, sp_team(winner) " wins the game " \
337                             sp_scores[winner] " to " sp_scores[looser])
338                         say(CHANNEL, sp_order[winner+0] "++")
339                         say(CHANNEL, sp_order[winner+2] "++")
340                         sp_reset(2)
341
342                 } else {
343                         if (sp_scores[0] == sp_scores[1] && 
344                             sp_scores[0] >= sp_playto)
345                                 say("It's tie! Playing an extra round!");
346                         sp_reset(1)
347                         sp_deal()
348                 }
349         }
350 }
351
352 # Misc
353 BEGIN {
354         cmd = "od -An -N4 -td4 /dev/random"
355         cmd | getline seed
356         close(cmd)
357         srand(seed)
358         sp_init()
359         sp_reset(2)
360         sp_load("var/sp_cur.json");
361         #if (sp_channel)
362         #       say(sp_channel, "Game restored.")
363 }
364
365 // {
366         sp_from  = AUTH in sp_auths ? sp_auths[AUTH] : \
367                    AUTH in sp_share ? sp_share[AUTH] : FROM
368         sp_valid = sp_from && sp_from == sp_player
369 }
370
371 CMD == "PRIVMSG" &&
372 ! /help/ &&
373 /[Ss]pades/ {
374         say("Spades! " sp_pretty("As,Ah,Ad,Ac", FROM))
375 }
376
377 AUTH == OWNER &&
378 /^\.savegame/ {
379         sp_save("var/sp_save.json");
380         say("Game saved.")
381 }
382
383 AUTH == OWNER &&
384 /^\.loadgame/ {
385         sp_load("var/sp_save.json");
386         say("Game loaded.")
387 }
388
389 # Help
390 /^\.help [Ss]pades$/ {
391         say("Spades -- play a game of spades")
392         say(".help game -- setup and administer the game")
393         say(".help play -- commands for playing spades")
394         say(".help auth -- control player authorization")
395         next
396 }
397
398 /^\.help game$/ {
399         say(".newgame [score] -- start a game to <score> points, default 500")
400         say(".endgame -- abort the current game")
401         say(".savegame -- save the current game to disk")
402         say(".loadgame -- load the previously saved game")
403         next
404 }
405
406 /^\.help play$/ {
407         say(".join -- join the current game")
408         say(".look -- look at your cards")
409         say(".bid [n] -- bid for <n> tricks")
410         say(".pass [card] -- pass a card to your partner")
411         say(".play [card] -- play a card")
412         say(".turn -- check whose turn it is")
413         say(".bids -- check what everyone bid")
414         say(".tricks -- check how many trick have been taken")
415         say(".score -- check the score")
416         next
417 }
418
419 /^\.help auth$/ {
420         say(".auth [who] -- display authentication info for a user")
421         say(".allow [who] -- allow another person to play on your behalf")
422         say(".deny [who] -- prevent a previously allowed user from playing")
423         say(".show -- display which users can play for which players")
424         next
425 }
426
427 # Debugging
428 AUTH == OWNER &&
429 /^\.deal (\w+) (.*)/ {
430         say(sp_channel, FROM " is cheating for " $2)
431         delete sp_hands[$2]
432         for (i=3; i<=NF; i++)
433                 sp_hands[$2][$i] = 1
434         next
435 }
436
437 AUTH == OWNER &&
438 /^\.order (\w+) ([0-4])/ {
439         say(sp_channel, FROM " is cheating for " $2)
440         sp_order[$3] = $2
441         sp_players[$2] = $3
442         sp_player = sp_order[sp_turn]
443 }
444
445 AUTH == OWNER &&
446 sp_state == "play" &&
447 /^\.force (\w+) (\S+)$/ {
448         say(sp_channel, FROM " is cheating for " $2)
449         sp_from = $2
450         sp_play($3)
451         next
452 }
453
454
455 # Setup
456 match($0, /^\.newgame ?([0-9]+) *- *([0-9]+)$/, _arr) {
457         if (_arr[2] > _arr[1])
458                 $0 = $1 " " int(rand() * (_arr[2]-_arr[1])+_arr[1])
459 }
460
461 /^\.newgame ?([0-9]+)?$/ {
462         if (sp_state != "new") {
463                 reply("There is already a game in progress.")
464         } else {
465                 $1         = ".join"
466                 sp_owner   = FROM
467                 sp_playto  = $2 ? $2 : 200
468                 sp_limit   = sp_playto > 200 ? 10 : 5;
469                 sp_state   = "join"
470                 sp_channel = DST
471                 say(sp_owner " starts a game of Spades to " sp_playto " with " sp_limit " bags!")
472         }
473 }
474
475 (sp_from == sp_owner || AUTH == OWNER) &&
476 /^\.endgame$/ {
477         if (sp_state == "new") {
478                 reply("There is no game in progress.")
479         } else {
480                 say(FROM " ends the game")
481                 sp_reset(2)
482         }
483 }
484
485 /^\.join$/ {
486         if (sp_state == "new") {
487                 reply("There is no game in progress")
488         }
489         else if (sp_state == "play") {
490                 reply("The game has already started")
491         }
492         else if (sp_state == "join" && sp_from in sp_players) {
493                 reply("You are already playing")
494         }
495         else if (sp_state == "join") {
496                 i = sp_next()
497                 sp_players[FROM] = i
498                 if (AUTH)
499                         sp_auths[AUTH] = FROM
500                 sp_order[i] = FROM
501                 say(FROM " joins the game!")
502         }
503         if (sp_state == "join" && sp_turn == 0) {
504                 sp_shuf()
505                 sp_deal()
506         }
507 }
508
509 /^\.allow \S+$/ {
510         _who = $2 in USERS ? USERS[$2]["auth"] : ""
511         _str = _who && _who != $2 ? $2 " (" _who ")" : $2
512         if (sp_state ~ "new|join") {
513                 reply("The game has not yet started")
514         }
515         else if (!(sp_from in sp_players)) {
516                 reply("You are not playing")
517         }
518         else if (!_who) {
519                 reply(_str " is not logged in")
520         }
521         else if (_who in sp_players || _who in sp_auths) {
522                 reply(_str " is a primary player")
523         }
524         else if (_who in sp_share) {
525                 reply(_str " is already playing for " sp_share[_who])
526         }
527         else {
528                 reply(_str " can now play for " sp_from)
529                 sp_share[_who] = sp_from
530         }
531 }
532
533 /^\.deny \S+$/ {
534         _who = $2 in USERS ? USERS[$2]["auth"] : $2
535         _str = _who && _who != $2 ? $2 " (" _who ")" : $2
536         if (sp_state ~ "new|join") {
537                 reply("The game has not yet started")
538         }
539         else if (!(sp_from in sp_players)) {
540                 reply("You are not playing")
541         }
542         else if (_who in sp_players || _who in sp_auths) {
543                 reply(_str " is a primary player")
544         }
545         else if (!(_who in sp_share) || sp_share[_who] != sp_from) {
546                 reply(_str " is not playing for " sp_from)
547         }
548         else {
549                 reply(_str " can no longer play for " sp_from)
550                 delete sp_share[_who]
551         }
552 }
553
554 sp_state ~ "(bid|pass|play)" &&
555 /^\.show/ {
556         delete _lines
557         for (_i in sp_share)
558                 _lines[sp_share[_i]] = _lines[sp_share[_i]] " " _i
559         for (_i in _lines)
560                 say(_i " allowed:" _lines[_i])
561 }
562
563 !sp_valid &&
564 (sp_state == "bid" || sp_state == "play") &&
565 /^\.(bid|play)\>/ {
566         if (sp_from in sp_players)
567                 say(".slap " FROM ", it is not your turn.")
568         else
569                 say(".slap " FROM ", you are not playing.")
570 }
571
572 sp_valid &&
573 sp_state == "bid" &&
574 /^\.bid [0-9]+$/ {
575         if ($2 < 0 || $2 > 13) {
576                 say("You can only bid from 0 to 13")
577         } else {
578                 i = sp_next()
579                 sp_bids[i] = $2
580                 if ($2 == 0 && !sp_looked[i]) {
581                         say(FROM " goes blind nil!")
582                         sp_nil[i] = 2
583                 } else if ($2 == 0) {
584                         say(FROM " goes nil!")
585                         sp_nil[i] = 1
586                 } else {
587                         sp_nil[i] = 0
588                 }
589                 if (sp_turn != sp_dealer) {
590                         say("Bidding goes to " sp_player "!")
591                 } else {
592                         for (p in sp_players)
593                                 say(p, "You have: " sp_hand(p, p))
594                         sp_state = "play"
595                         for (i=0; i<2; i++) {
596                                 if (sp_passer(i)) {
597                                         say(sp_team(i) ": select a card to pass " \
598                                             "(/msg " NICK " .pass <card>)")
599                                         sp_state = "pass"
600                                 }
601                         }
602                         if (sp_state == "play")
603                                 say("Play starts with " sp_player "!")
604                 }
605         }
606 }
607
608 sp_state == "pass" &&
609 /^\.pass (\S+)$/ {
610         _card = $2
611         _team = sp_from in sp_players ? sp_players[sp_from] % 2 : 0
612
613         # check validity and pass
614         if (!(sp_from in sp_players)) {
615                 say(".slap " FROM ", you are not playing.")
616         }
617         else if (!sp_passer(_team)) {
618                 reply("Your team did not go blind")
619         }
620         else if (sp_pass[sp_players[sp_from]]) {
621                 reply("You have already passed a card")
622         }
623         else if (!(_card in sp_deck)) {
624                 reply("Invalid card")
625         }
626         else if (!(_card in sp_hands[sp_from])) {
627                 reply("You do not have that card")
628         }
629         else {
630                 sp_pass[sp_players[sp_from]] = $2
631                 say(sp_channel, FROM " passes a card")
632         }
633
634         # check for end of passing
635         if ((!sp_passer(0) || (sp_pass[0] && sp_pass[2])) &&
636             (!sp_passer(1) || (sp_pass[1] && sp_pass[3]))) {
637                 for (i in sp_pass) {
638                         _partner = (i+2)%4
639                         _card    = sp_pass[i]
640                         delete sp_hands[sp_order[i]][_card]
641                         sp_hands[sp_order[_partner]][_card] = 1
642                 }
643                 say(sp_channel, "Cards have been passed, play starts with " sp_player "!")
644                 for (p in sp_players)
645                         say(p, "You have: " sp_hand(p, p))
646                 sp_state = "play"
647         }
648 }
649
650 sp_state ~ "(bid|pass|play)" &&
651 /^\.look$/ {
652         if (!(sp_from in sp_players)) {
653                 say(".slap " FROM ", you are not playing.")
654         } else {
655                 sp_looked[sp_players[sp_from]] = 1
656                 say(FROM, "You have: " sp_hand(FROM, sp_from))
657         }
658 }
659
660 sp_valid &&
661 sp_state == "play" &&
662 /^\.play (\S+)/ {
663         _card = $2
664         gsub(/[^A-Za-z0-9]/, "", _card);
665         if (!(_card in sp_deck)) {
666                 reply("Invalid card")
667         }
668         else if (!(_card in sp_hands[sp_from])) {
669                 reply("You do not have that card")
670         }
671         else if (sp_suit && _card !~ sp_suit && sp_hasa(sp_from, sp_suit)) {
672                 reply("You must follow suit (" sp_suit ")")
673         }
674         else if (_card ~ /s/ && length(sp_hands[sp_from]) == 13 && sp_hasa(sp_from, "[^s]$")) {
675                 reply("You cannot trump on the first hand")
676         }
677         else if (_card ~ /s/ && length(sp_pile) == 0 && sp_hasa(sp_from, "[^s]$") && !sp_broken) {
678                 reply("Spades have not been broken")
679         }
680         else {
681                 sp_play(_card)
682                 if (sp_state == "play") {
683                         if (length(sp_hands[sp_from]))
684                                 say(FROM, "You have: " sp_hand(FROM, sp_from))
685                         if (sp_piles)
686                                 say(sp_player ": it is your turn! " \
687                                     "(" sp_pretty(sp_piles, sp_player) ")")
688                         else
689                                 say(sp_player ": it is your turn!")
690                 }
691         }
692 }
693
694 /^\.bids/ && sp_state == "bid" ||
695 /^\.turn/ && sp_state ~ "(bid|pass|play)" {
696         _bids = sp_bidders()
697         _pile = sp_pretty(sp_piles, FROM)
698         if (sp_state == "bid" && !_bids)
699                 say("It is " sp_player "'s bid!")
700         if (sp_state == "bid" && _bids)
701                 say("It is " sp_player "'s bid! (" _bids ")")
702         if (sp_state == "play" && !_pile)
703                 say("It is " sp_player "'s turn!")
704         if (sp_state == "play" && _pile)
705                 say("It is " sp_player "'s turn! (" _pile ")")
706         for (_i=0; sp_state == "pass" && _i<4; _i++)
707                 if (sp_passer(_i) && !sp_pass[_i])
708                         say("Waiting for " sp_order[_i] " to pass a card!")
709 }
710
711 /^\.bids$/ && sp_state ~ "(pass|play)" {
712         say(sp_order[0] " bid " sp_bid(0) ", " \
713             sp_order[2] " bid " sp_bid(2) ", " \
714             "total: " sp_bids[0] + sp_bids[2])
715         say(sp_order[1] " bid " sp_bid(1) ", " \
716             sp_order[3] " bid " sp_bid(3) ", " \
717             "total: " sp_bids[1] + sp_bids[3])
718 }
719
720 /^\.tricks$/ && sp_state == "play" {
721         say(sp_order[0] " took " int(sp_tricks[0]) "/" sp_bid(0) ", " \
722             sp_order[2] " took " int(sp_tricks[2]) "/" sp_bid(2))
723         say(sp_order[1] " took " int(sp_tricks[1]) "/" sp_bid(1) ", " \
724             sp_order[3] " took " int(sp_tricks[3]) "/" sp_bid(3))
725 }
726
727 (TO == NICK || DST == sp_channel) &&
728 /^\.(score|status)$/ {
729         if (sp_state == "new") {
730                 say("There is no game in progress")
731         }
732         if (sp_state ~ "join|bid|pass|play") {
733                 say("Playing to: " \
734                     sp_playto " points, " \
735                     sp_limit  " bags")
736         }
737         if (sp_state == "join") {
738                 say("Waiting for players: " \
739                     sp_order[0] " " sp_order[1] " " \
740                     sp_order[2] " " sp_order[3])
741         }
742         if (sp_state ~ "bid|pass|play") {
743                 say(sp_team(0) ": " \
744                     int(sp_scores[0]) " points, " \
745                     int(sp_bags(0))   " bags")
746                 say(sp_team(1) ": " \
747                     int(sp_scores[1]) " points, " \
748                     int(sp_bags(1))   " bags")
749         }
750 }
751
752 /^\.((new|end|load)game|join|look|bid|pass|play)/ {
753         sp_save("var/sp_cur.json");
754 }