]> Pileus Git - ~andy/rhawk/blob - spades.awk
Add more testing
[~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 /^\.order (\w+) ([0-4])/ {
433         say(sp_channel, FROM " is cheating for " $2)
434         sp_order[$3] = $2
435         sp_players[$2] = $3
436         sp_player = sp_order[sp_turn]
437 }
438
439 AUTH == OWNER &&
440 sp_state == "play" &&
441 /^\.force (\w+) (\S+)$/ {
442         say(sp_channel, FROM " is cheating for " $2)
443         sp_from = $2
444         sp_play($3)
445         next
446 }
447
448
449 # Setup
450 match($0, /^\.newgame ?([0-9]+) *- *([0-9]+)$/, _arr) {
451         if (_arr[2] > _arr[1])
452                 $0 = $1 " " int(rand() * (_arr[2]-_arr[1])+_arr[1])
453 }
454
455 /^\.newgame ?([0-9]+)?$/ {
456         if (sp_state != "new") {
457                 reply("There is already a game in progress.")
458         } else {
459                 $1         = ".join"
460                 sp_owner   = FROM
461                 sp_playto  = $2 ? $2 : 200
462                 sp_limit   = sp_playto > 200 ? 10 : 5;
463                 sp_state   = "join"
464                 sp_channel = DST
465                 say(sp_owner " starts a game of Spades to " sp_playto " with " sp_limit " bags!")
466         }
467 }
468
469 (sp_from == sp_owner || AUTH == OWNER) &&
470 /^\.endgame$/ {
471         if (sp_state == "new") {
472                 reply("There is no game in progress.")
473         } else {
474                 say(FROM " ends the game")
475                 sp_reset(2)
476         }
477 }
478
479 /^\.join$/ {
480         if (sp_state == "new") {
481                 reply("There is no game in progress")
482         }
483         else if (sp_state == "play") {
484                 reply("The game has already started")
485         }
486         else if (sp_state == "join" && sp_from in sp_players) {
487                 reply("You are already playing")
488         }
489         else if (sp_state == "join") {
490                 i = sp_next()
491                 sp_players[FROM] = i
492                 if (AUTH)
493                         sp_auths[AUTH] = FROM
494                 sp_order[i] = FROM
495                 say(FROM " joins the game!")
496         }
497         if (sp_state == "join" && sp_turn == 0) {
498                 sp_shuf()
499                 sp_deal()
500         }
501 }
502
503 /^\.allow \S+$/ {
504         _who = $2 in USERS ? USERS[$2]["auth"] : ""
505         _str = _who && _who != $2 ? $2 " (" _who ")" : $2
506         if (sp_state ~ "new|join") {
507                 reply("The game has not yet started")
508         }
509         else if (!(sp_from in sp_players)) {
510                 reply("You are not playing")
511         }
512         else if (!_who) {
513                 reply(_str " is not logged in")
514         }
515         else if (_who in sp_players || _who in sp_auths) {
516                 reply(_str " is a primary player")
517         }
518         else if (_who in sp_share) {
519                 reply(_str " is already playing for " sp_share[_who])
520         }
521         else {
522                 reply(_str " can now play for " sp_from)
523                 sp_share[_who] = sp_from
524         }
525 }
526
527 /^\.deny \S+$/ {
528         _who = $2 in USERS ? USERS[$2]["auth"] : $2
529         _str = _who && _who != $2 ? $2 " (" _who ")" : $2
530         if (sp_state ~ "new|join") {
531                 reply("The game has not yet started")
532         }
533         else if (!(sp_from in sp_players)) {
534                 reply("You are not playing")
535         }
536         else if (_who in sp_players || _who in sp_auths) {
537                 reply(_str " is a primary player")
538         }
539         else if (!(_who in sp_share) || sp_share[_who] != sp_from) {
540                 reply(_str " is not playing for " sp_from)
541         }
542         else {
543                 reply(_str " can no longer play for " sp_from)
544                 delete sp_share[_who]
545         }
546 }
547
548 sp_state ~ "(bid|pass|play)" &&
549 /^\.show/ {
550         for (_i in sp_share)
551                 say(_i " can play for " sp_share[_i]);
552 }
553
554 !sp_valid &&
555 (sp_state == "bid" || sp_state == "play") &&
556 /^\.(bid|play)\>/ {
557         if (sp_from in sp_players)
558                 say(".slap " FROM ", it is not your turn.")
559         else
560                 say(".slap " FROM ", you are not playing.")
561 }
562
563 sp_valid &&
564 sp_state == "bid" &&
565 /^\.bid [0-9]+$/ {
566         if ($2 < 0 || $2 > 13) {
567                 say("You can only bid from 0 to 13")
568         } else {
569                 i = sp_next()
570                 sp_bids[i] = $2
571                 if ($2 == 0 && !sp_looked[i]) {
572                         say(FROM " goes blind nil!")
573                         sp_nil[i] = 2
574                 } else if ($2 == 0) {
575                         say(FROM " goes nil!")
576                         sp_nil[i] = 1
577                 } else {
578                         sp_nil[i] = 0
579                 }
580                 if (sp_turn != sp_dealer) {
581                         say("Bidding goes to " sp_player "!")
582                 } else {
583                         for (p in sp_players)
584                                 say(p, "You have: " sp_hand(p, p))
585                         sp_state = "play"
586                         for (i=0; i<2; i++) {
587                                 if (sp_nil[(i+0)%4] == 2 || sp_nil[(i+2)%4] == 2 ||
588                                     sp_nil[(i+1)%4] != 0 || sp_nil[(i+3)%4] != 0) {
589                                         say(sp_team(i) ": select a card to pass " \
590                                             "(/msg " NICK " .pass <card>)")
591                                         sp_state = "pass"
592                                 }
593                         }
594                         if (sp_state == "play")
595                                 say("Play starts with " sp_player "!")
596                 }
597         }
598 }
599
600 sp_state == "pass" &&
601 /^\.pass (\S+)$/ {
602         _card = $2
603         _team = sp_from in sp_players ? sp_players[sp_from] % 2 : 0
604
605         # check validity and pass
606         if (!(sp_from in sp_players)) {
607                 say(".slap " FROM ", you are not playing.")
608         }
609         else if (sp_nil[_team] == 1 && sp_nil[_team+2] == 1) {
610                 reply("Your team did not go blind")
611         }
612         else if (sp_pass[sp_players[sp_from]]) {
613                 reply("You have already passed a card")
614         }
615         else if (!(_card in sp_deck)) {
616                 reply("Invalid card")
617         }
618         else if (!(_card in sp_hands[sp_from])) {
619                 reply("You do not have that card")
620         }
621         else {
622                 sp_pass[sp_players[sp_from]] = $2
623                 say(sp_channel, FROM " passes a card")
624         }
625
626         # check for end of passing
627         if (((sp_nil[0] == 1 && sp_nil[2] == 1) || (sp_pass[0] && sp_pass[2])) &&
628             ((sp_nil[1] == 1 && sp_nil[3] == 1) || (sp_pass[1] && sp_pass[3]))) {
629                 for (i in sp_pass) {
630                         _partner = (i+2)%4
631                         _card    = sp_pass[i]
632                         delete sp_hands[sp_order[i]][_card]
633                         sp_hands[sp_order[_partner]][_card] = 1
634                 }
635                 say(sp_channel, "Cards have been passed, play starts with " sp_player "!")
636                 for (p in sp_players)
637                         say(p, "You have: " sp_hand(p, p))
638                 sp_state = "play"
639         }
640 }
641
642 sp_state ~ "(bid|pass|play)" &&
643 /^\.look$/ {
644         if (!(sp_from in sp_players)) {
645                 say(".slap " FROM ", you are not playing.")
646         } else {
647                 sp_looked[sp_players[sp_from]] = 1
648                 say(FROM, "You have: " sp_hand(FROM, sp_from))
649         }
650 }
651
652 sp_valid &&
653 sp_state == "play" &&
654 /^\.play (\S+)/ {
655         _card = $2
656         gsub(/[^A-Za-z0-9]/, "", _card);
657         if (!(_card in sp_deck)) {
658                 reply("Invalid card")
659         }
660         else if (!(_card in sp_hands[sp_from])) {
661                 reply("You do not have that card")
662         }
663         else if (sp_suit && _card !~ sp_suit && sp_hasa(sp_from, sp_suit)) {
664                 reply("You must follow suit (" sp_suit ")")
665         }
666         else if (_card ~ /s/ && length(sp_hands[sp_from]) == 13 && sp_hasa(sp_from, "[^s]$")) {
667                 reply("You cannot trump on the first hand")
668         }
669         else if (_card ~ /s/ && length(sp_pile) == 0 && sp_hasa(sp_from, "[^s]$") && !sp_broken) {
670                 reply("Spades have not been broken")
671         }
672         else {
673                 sp_play(_card)
674                 if (sp_state == "play") {
675                         if (length(sp_hands[sp_from]))
676                                 say(FROM, "You have: " sp_hand(FROM, sp_from))
677                         if (sp_piles)
678                                 say(sp_player ": it is your turn! " \
679                                     "(" sp_pretty(sp_piles, sp_player) ")")
680                         else
681                                 say(sp_player ": it is your turn!")
682                 }
683         }
684 }
685
686 /^\.bids/ && sp_state == "bid" ||
687 /^\.turn/ && sp_state ~ "(bid|pass|play)" {
688         _bids = sp_bidders()
689         _pile = sp_pretty(sp_piles, FROM)
690         if (sp_state == "bid" && !_bids)
691                 say("It is " sp_player "'s bid!")
692         if (sp_state == "bid" && _bids)
693                 say("It is " sp_player "'s bid! (" _bids ")")
694         if (sp_state == "play" && !_pile)
695                 say("It is " sp_player "'s turn!")
696         if (sp_state == "play" && _pile)
697                 say("It is " sp_player "'s turn! (" _pile ")")
698         for (_i=0; sp_state == "pass" && _i<4; _i++)
699                 if ((sp_nil[_i%2+0]!=1 || sp_nil[_i%2+2]!=1) && !sp_pass[_i])
700                         say("Waiting for " sp_order[_i] " to pass a card!")
701 }
702
703 /^\.bids$/ && sp_state ~ "(pass|play)" {
704         say(sp_order[0] " bid " sp_bid(0) ", " \
705             sp_order[2] " bid " sp_bid(2) ", " \
706             "total: " sp_bids[0] + sp_bids[2])
707         say(sp_order[1] " bid " sp_bid(1) ", " \
708             sp_order[3] " bid " sp_bid(3) ", " \
709             "total: " sp_bids[1] + sp_bids[3])
710 }
711
712 /^\.tricks$/ && sp_state == "play" {
713         say(sp_order[0] " took " int(sp_tricks[0]) "/" sp_bid(0) ", " \
714             sp_order[2] " took " int(sp_tricks[2]) "/" sp_bid(2))
715         say(sp_order[1] " took " int(sp_tricks[1]) "/" sp_bid(1) ", " \
716             sp_order[3] " took " int(sp_tricks[3]) "/" sp_bid(3))
717 }
718
719 (TO == NICK || DST == sp_channel) &&
720 /^\.(score|status)$/ {
721         if (sp_state == "new") {
722                 say("There is no game in progress")
723         }
724         if (sp_state ~ "join|bid|pass|play") {
725                 say("Playing to: " \
726                     sp_playto " points, " \
727                     sp_limit  " bags")
728         }
729         if (sp_state == "join") {
730                 say("Waiting for players: " \
731                     sp_order[0] " " sp_order[1] " " \
732                     sp_order[2] " " sp_order[3])
733         }
734         if (sp_state ~ "bid|pass|play") {
735                 say(sp_team(0) ": " \
736                     int(sp_scores[0]) " points, " \
737                     int(sp_bags(0))   " bags")
738                 say(sp_team(1) ": " \
739                     int(sp_scores[1]) " points, " \
740                     int(sp_bags(1))   " bags")
741         }
742 }
743
744 /^\.((new|end|load)game|join|look|bid|pass|play)/ {
745         sp_save("var/sp_cur.json");
746 }