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