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