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