]> Pileus Git - ~andy/rhawk/blob - spades.awk
Swizzle .show
[~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         delete _lines
551         for (_i in sp_share)
552                 _lines[sp_share[_i]] = _lines[sp_share[_i]] " " _i
553         for (_i in _lines)
554                 say(_i " allowed:" _lines[_i])
555 }
556
557 !sp_valid &&
558 (sp_state == "bid" || sp_state == "play") &&
559 /^\.(bid|play)\>/ {
560         if (sp_from in sp_players)
561                 say(".slap " FROM ", it is not your turn.")
562         else
563                 say(".slap " FROM ", you are not playing.")
564 }
565
566 sp_valid &&
567 sp_state == "bid" &&
568 /^\.bid [0-9]+$/ {
569         if ($2 < 0 || $2 > 13) {
570                 say("You can only bid from 0 to 13")
571         } else {
572                 i = sp_next()
573                 sp_bids[i] = $2
574                 if ($2 == 0 && !sp_looked[i]) {
575                         say(FROM " goes blind nil!")
576                         sp_nil[i] = 2
577                 } else if ($2 == 0) {
578                         say(FROM " goes nil!")
579                         sp_nil[i] = 1
580                 } else {
581                         sp_nil[i] = 0
582                 }
583                 if (sp_turn != sp_dealer) {
584                         say("Bidding goes to " sp_player "!")
585                 } else {
586                         for (p in sp_players)
587                                 say(p, "You have: " sp_hand(p, p))
588                         sp_state = "play"
589                         for (i=0; i<2; i++) {
590                                 if (sp_nil[(i+0)%4] == 2 || sp_nil[(i+2)%4] == 2 ||
591                                     sp_nil[(i+1)%4] != 0 || sp_nil[(i+3)%4] != 0) {
592                                         say(sp_team(i) ": select a card to pass " \
593                                             "(/msg " NICK " .pass <card>)")
594                                         sp_state = "pass"
595                                 }
596                         }
597                         if (sp_state == "play")
598                                 say("Play starts with " sp_player "!")
599                 }
600         }
601 }
602
603 sp_state == "pass" &&
604 /^\.pass (\S+)$/ {
605         _card = $2
606         _team = sp_from in sp_players ? sp_players[sp_from] % 2 : 0
607
608         # check validity and pass
609         if (!(sp_from in sp_players)) {
610                 say(".slap " FROM ", you are not playing.")
611         }
612         else if (sp_nil[_team] == 1 && sp_nil[_team+2] == 1) {
613                 reply("Your team did not go blind")
614         }
615         else if (sp_pass[sp_players[sp_from]]) {
616                 reply("You have already passed a card")
617         }
618         else if (!(_card in sp_deck)) {
619                 reply("Invalid card")
620         }
621         else if (!(_card in sp_hands[sp_from])) {
622                 reply("You do not have that card")
623         }
624         else {
625                 sp_pass[sp_players[sp_from]] = $2
626                 say(sp_channel, FROM " passes a card")
627         }
628
629         # check for end of passing
630         if (((sp_nil[0] == 1 && sp_nil[2] == 1) || (sp_pass[0] && sp_pass[2])) &&
631             ((sp_nil[1] == 1 && sp_nil[3] == 1) || (sp_pass[1] && sp_pass[3]))) {
632                 for (i in sp_pass) {
633                         _partner = (i+2)%4
634                         _card    = sp_pass[i]
635                         delete sp_hands[sp_order[i]][_card]
636                         sp_hands[sp_order[_partner]][_card] = 1
637                 }
638                 say(sp_channel, "Cards have been passed, play starts with " sp_player "!")
639                 for (p in sp_players)
640                         say(p, "You have: " sp_hand(p, p))
641                 sp_state = "play"
642         }
643 }
644
645 sp_state ~ "(bid|pass|play)" &&
646 /^\.look$/ {
647         if (!(sp_from in sp_players)) {
648                 say(".slap " FROM ", you are not playing.")
649         } else {
650                 sp_looked[sp_players[sp_from]] = 1
651                 say(FROM, "You have: " sp_hand(FROM, sp_from))
652         }
653 }
654
655 sp_valid &&
656 sp_state == "play" &&
657 /^\.play (\S+)/ {
658         _card = $2
659         gsub(/[^A-Za-z0-9]/, "", _card);
660         if (!(_card in sp_deck)) {
661                 reply("Invalid card")
662         }
663         else if (!(_card in sp_hands[sp_from])) {
664                 reply("You do not have that card")
665         }
666         else if (sp_suit && _card !~ sp_suit && sp_hasa(sp_from, sp_suit)) {
667                 reply("You must follow suit (" sp_suit ")")
668         }
669         else if (_card ~ /s/ && length(sp_hands[sp_from]) == 13 && sp_hasa(sp_from, "[^s]$")) {
670                 reply("You cannot trump on the first hand")
671         }
672         else if (_card ~ /s/ && length(sp_pile) == 0 && sp_hasa(sp_from, "[^s]$") && !sp_broken) {
673                 reply("Spades have not been broken")
674         }
675         else {
676                 sp_play(_card)
677                 if (sp_state == "play") {
678                         if (length(sp_hands[sp_from]))
679                                 say(FROM, "You have: " sp_hand(FROM, sp_from))
680                         if (sp_piles)
681                                 say(sp_player ": it is your turn! " \
682                                     "(" sp_pretty(sp_piles, sp_player) ")")
683                         else
684                                 say(sp_player ": it is your turn!")
685                 }
686         }
687 }
688
689 /^\.bids/ && sp_state == "bid" ||
690 /^\.turn/ && sp_state ~ "(bid|pass|play)" {
691         _bids = sp_bidders()
692         _pile = sp_pretty(sp_piles, FROM)
693         if (sp_state == "bid" && !_bids)
694                 say("It is " sp_player "'s bid!")
695         if (sp_state == "bid" && _bids)
696                 say("It is " sp_player "'s bid! (" _bids ")")
697         if (sp_state == "play" && !_pile)
698                 say("It is " sp_player "'s turn!")
699         if (sp_state == "play" && _pile)
700                 say("It is " sp_player "'s turn! (" _pile ")")
701         for (_i=0; sp_state == "pass" && _i<4; _i++)
702                 if ((sp_nil[_i%2+0]!=1 || sp_nil[_i%2+2]!=1) && !sp_pass[_i])
703                         say("Waiting for " sp_order[_i] " to pass a card!")
704 }
705
706 /^\.bids$/ && sp_state ~ "(pass|play)" {
707         say(sp_order[0] " bid " sp_bid(0) ", " \
708             sp_order[2] " bid " sp_bid(2) ", " \
709             "total: " sp_bids[0] + sp_bids[2])
710         say(sp_order[1] " bid " sp_bid(1) ", " \
711             sp_order[3] " bid " sp_bid(3) ", " \
712             "total: " sp_bids[1] + sp_bids[3])
713 }
714
715 /^\.tricks$/ && sp_state == "play" {
716         say(sp_order[0] " took " int(sp_tricks[0]) "/" sp_bid(0) ", " \
717             sp_order[2] " took " int(sp_tricks[2]) "/" sp_bid(2))
718         say(sp_order[1] " took " int(sp_tricks[1]) "/" sp_bid(1) ", " \
719             sp_order[3] " took " int(sp_tricks[3]) "/" sp_bid(3))
720 }
721
722 (TO == NICK || DST == sp_channel) &&
723 /^\.(score|status)$/ {
724         if (sp_state == "new") {
725                 say("There is no game in progress")
726         }
727         if (sp_state ~ "join|bid|pass|play") {
728                 say("Playing to: " \
729                     sp_playto " points, " \
730                     sp_limit  " bags")
731         }
732         if (sp_state == "join") {
733                 say("Waiting for players: " \
734                     sp_order[0] " " sp_order[1] " " \
735                     sp_order[2] " " sp_order[3])
736         }
737         if (sp_state ~ "bid|pass|play") {
738                 say(sp_team(0) ": " \
739                     int(sp_scores[0]) " points, " \
740                     int(sp_bags(0))   " bags")
741                 say(sp_team(1) ": " \
742                     int(sp_scores[1]) " points, " \
743                     int(sp_bags(1))   " bags")
744         }
745 }
746
747 /^\.((new|end|load)game|join|look|bid|pass|play)/ {
748         sp_save("var/sp_cur.json");
749 }