]> Pileus Git - ~andy/rhawk/blob - spades.awk
Split out bid/nil/blind code
[~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_deal(       shuf)
165 {
166         say("/me deals the cards")
167         asorti(sp_deck, shuf, "sp_usort")
168         for (i=1; i<=52; i++)
169                 sp_hands[sp_order[i%4]][shuf[i]] = 1
170         sp_state  = "bid"
171         sp_dealer = (sp_dealer+1)%4
172         sp_turn   =  sp_dealer
173         sp_player =  sp_order[sp_turn]
174         say("Bidding starts with " sp_player "!")
175 }
176
177 function sp_hand(to, who,       sort, str)
178 {
179         asorti(sp_hands[who], sort, "sp_csort")
180         for (i=0; i<length(sort); i++)
181                 str = str "" sprintf("%4s", sort[i])
182         gsub(/^ +| +$/, "", str)
183         return sp_pretty(str, to)
184 }
185
186 function sp_hasa(who, expr)
187 {
188         for (c in sp_hands[who]) {
189                 if (c ~ expr)
190                         return 1
191         }
192         return 0
193 }
194
195 function sp_type(card)
196 {
197         return substr(card, length(card))
198 }
199
200 function sp_usort(a,b,c,d) {
201         return rand() - 0.5
202 }
203
204 function sp_csort(i1,v1,i2,v2) {
205         return sp_deck[i1] > sp_deck[i2] ? +1 :
206                sp_deck[i1] < sp_deck[i2] ? -1 : 0;
207 }
208
209 function sp_winner(     card, tmp)
210 {
211         for (card in sp_pile)
212                 if (card !~ sp_suit && card !~ /s/)
213                         delete sp_pile[card]
214         asorti(sp_pile, tmp, "sp_csort")
215         #print "pile: " tmp[1] ">" tmp[2] ">" tmp[3] ">" tmp[4]
216         return tmp[1]
217 }
218
219 function sp_team(i)
220 {
221         #return "{" sp_order[i+0] "," sp_order[i+2] "}"
222         return sp_order[i+0] "/" sp_order[i+2]
223 }
224
225 function sp_bags(i,     bags)
226 {
227         bags = sp_scores[i] % sp_limit
228         if (bags < 0)
229                 bags += sp_limit
230         return bags
231 }
232
233 function sp_bid(who)
234 {
235         return sp_nil[who] == 0 ? sp_bids[who] :
236                sp_nil[who] == 1 ? "nil"        :
237                sp_nil[who] == 2 ? "blind"      : "n/a"
238 }
239
240 function sp_bidders(    i, turn, bid, bids)
241 {
242         for (i = 0; i < 4; i++)
243                 if (bid = sp_bid((sp_dealer + i) % 4))
244                         bids = bids " " bid
245         gsub(/^ +| +$/, "", bids)
246         return bids
247 }
248
249 function sp_score(      bids, times, tricks)
250 {
251         for (i=0; i<2; i++) {
252                 bids   = sp_bids[i]   + sp_bids[i+2]
253                 tricks = sp_tricks[i] + sp_tricks[i+2]
254                 bags   = tricks - bids
255                 times  = int((sp_bags(i) + bags) / sp_limit)
256                 if (times > 0) {
257                         say(sp_team(i) " bag" (times>1?" way ":" ") "out")
258                         sp_scores[i] -= sp_limit * 10 * times;
259                 }
260                 if (tricks >= bids) {
261                         say(sp_team(i) " make their bid: " tricks "/" bids)
262                         sp_scores[i] += bids*10 + bags;
263                 } else {
264                         say(sp_team(i) " go bust: " tricks "/" bids)
265                         sp_scores[i] -= bids*10;
266                 }
267         }
268         for (i=0; i<4; i++) {
269                 if (!sp_nil[i])
270                         continue
271                 say(sp_order[i] " " \
272                     (sp_nil[i] == 1 && !sp_tricks[i] ? "makes nil!"       :
273                      sp_nil[i] == 1 &&  sp_tricks[i] ? "fails at nil!"    :
274                      sp_nil[i] == 2 && !sp_tricks[i] ? "makes blind nil!" :
275                      sp_nil[i] == 2 &&  sp_tricks[i] ? "fails miserably at blind nil!" :
276                                                        "unknown"))
277                 sp_scores[i%2] += sp_limit * 10 * sp_nil[i] * \
278                         (sp_tricks[i] == 0 ? 1 : -1)
279         }
280 }
281
282 function sp_play(card,  winner, pi)
283 {
284         delete sp_hands[sp_from][card]
285         sp_pile[card] = sp_player
286         sp_piles      = sp_piles (sp_piles?",":"") card
287         sp_next()
288
289         if (card ~ /s/)
290                 sp_broken = 1
291
292         # Start hand
293         if (length(sp_pile) == 1)
294                 sp_suit = sp_type(card)
295
296         # Finish hand
297         if (length(sp_pile) == 4) {
298                 winner = sp_winner()
299                 pi     = sp_players[sp_pile[winner]]
300                 sp_tricks[pi]++
301                 say(sp_pile[winner] " wins with " sp_pretty(winner, FROM) \
302                     " (" sp_pretty(sp_piles, FROM) ")")
303                 sp_next(sp_pile[winner])
304                 sp_reset(0)
305         }
306
307         # Finish round
308         if (sp_tricks[0] + sp_tricks[1] + \
309             sp_tricks[2] + sp_tricks[3] == 13) {
310                 say("Round over!")
311                 sp_score()
312                 if (sp_scores[0] >= sp_playto || sp_scores[1] >= sp_playto &&
313                     sp_scores[0]              != sp_scores[1]) {
314                         say("Game over!")
315                         winner = sp_scores[0] > sp_scores[1] ? 0 : 1
316                         looser = !winner
317                         say(CHANNEL, sp_team(winner) " wins the game " \
318                             sp_scores[winner] " to " sp_scores[looser])
319                         say(CHANNEL, sp_order[winner+0] "++")
320                         say(CHANNEL, sp_order[winner+2] "++")
321                         sp_reset(2)
322
323                 } else {
324                         if (sp_scores[0] == sp_scores[1] && 
325                             sp_scores[0] >= sp_playto)
326                                 say("It's tie! Playing an extra round!");
327                         sp_reset(1)
328                         sp_deal()
329                 }
330         }
331 }
332
333 # Misc
334 BEGIN {
335         cmd = "od -An -N4 -td4 /dev/random"
336         cmd | getline seed
337         close(cmd)
338         srand(seed)
339         sp_init()
340         sp_reset(2)
341         sp_load("var/sp_cur.json");
342         #if (sp_channel)
343         #       say(sp_channel, "Game restored.")
344 }
345
346 // {
347         sp_from  = AUTH in sp_auths ? sp_auths[AUTH] : \
348                    AUTH in sp_share ? sp_share[AUTH] : FROM
349         sp_valid = sp_from && sp_from == sp_player
350 }
351
352 CMD == "PRIVMSG" &&
353 ! /help/ &&
354 /[Ss]pades/ {
355         say("Spades! " sp_pretty("As,Ah,Ad,Ac", FROM))
356 }
357
358 AUTH == OWNER &&
359 /^\.savegame/ {
360         sp_save("var/sp_save.json");
361         say("Game saved.")
362 }
363
364 AUTH == OWNER &&
365 /^\.loadgame/ {
366         sp_load("var/sp_save.json");
367         say("Game loaded.")
368 }
369
370 # Help
371 /^\.help [Ss]pades$/ {
372         say("Spades -- play a game of spades")
373         say(".help game -- setup and administer the game")
374         say(".help play -- commands for playing spades")
375         say(".help auth -- control player authorization")
376         next
377 }
378
379 /^\.help game$/ {
380         say(".newgame [score] -- start a game to <score> points, default 500")
381         say(".endgame -- abort the current game")
382         say(".savegame -- save the current game to disk")
383         say(".loadgame -- load the previously saved game")
384         next
385 }
386
387 /^\.help play$/ {
388         say(".join -- join the current game")
389         say(".look -- look at your cards")
390         say(".bid [n] -- bid for <n> tricks")
391         say(".pass [card] -- pass a card to your partner")
392         say(".play [card] -- play a card")
393         say(".turn -- check whose turn it is")
394         say(".bids -- check what everyone bid")
395         say(".tricks -- check how many trick have been taken")
396         say(".score -- check the score")
397         next
398 }
399
400 /^\.help auth$/ {
401         say(".auth [who] -- display authentication info for a user")
402         say(".allow [who] -- allow another person to play on your behalf")
403         say(".deny [who] -- prevent a previously allowed user from playing")
404         say(".show -- display which users can play for which players")
405         next
406 }
407
408 # Debugging
409 AUTH == OWNER &&
410 /^\.deal (\w+) (.*)/ {
411         say(sp_channel, FROM " is cheating for " $2)
412         delete sp_hands[$2]
413         for (i=3; i<=NF; i++)
414                 sp_hands[$2][$i] = 1
415         next
416 }
417
418 AUTH == OWNER &&
419 sp_state == "play" &&
420 /^\.force (\w+) (\S+)$/ {
421         say(sp_channel, FROM " is cheating for " $2)
422         sp_from = $2
423         sp_play($3)
424         next
425 }
426
427
428 # Setup
429 /^\.newgame ?([0-9]+)?$/ {
430         if (sp_state != "new") {
431                 reply("There is already a game in progress.")
432         } else {
433                 $1         = ".join"
434                 sp_owner   = FROM
435                 sp_playto  = $2 ? $2 : 200
436                 sp_limit   = sp_playto > 200 ? 10 : 5;
437                 sp_state   = "join"
438                 sp_channel = DST
439                 say(sp_owner " starts a game of Spades to " sp_playto " with " sp_limit " bags!")
440         }
441 }
442
443 (sp_from == sp_owner || AUTH == OWNER) &&
444 /^\.endgame$/ {
445         if (sp_state == "new") {
446                 reply("There is no game in progress.")
447         } else {
448                 say(FROM " ends the game")
449                 sp_reset(2)
450         }
451 }
452
453 /^\.join$/ {
454         if (sp_state == "new") {
455                 reply("There is no game in progress")
456         }
457         else if (sp_state == "play") {
458                 reply("The game has already started")
459         }
460         else if (sp_state == "join" && sp_from in sp_players) {
461                 reply("You are already playing")
462         }
463         else if (sp_state == "join") {
464                 i = sp_next()
465                 sp_players[FROM] = i
466                 if (AUTH)
467                         sp_auths[AUTH] = FROM
468                 sp_order[i] = FROM
469                 say(FROM " joins the game!")
470         }
471         if (sp_state == "join" && sp_turn == 0)
472                 sp_deal()
473 }
474
475 /^\.allow \S+$/ {
476         _who = $2 in USERS ? USERS[$2]["auth"] : ""
477         _str = _who && _who != $2 ? $2 " (" _who ")" : $2
478         if (sp_state ~ "new|join") {
479                 reply("The game has not yet started")
480         }
481         else if (!(sp_from in sp_players)) {
482                 reply("You are not playing")
483         }
484         else if (!_who) {
485                 reply(_str " is not logged in")
486         }
487         else if (_who in sp_players || _who in sp_auths) {
488                 reply(_str " is a primary player")
489         }
490         else if (_who in sp_share) {
491                 reply(_str " is already playing for " sp_share[_who])
492         }
493         else {
494                 reply(_str " can now play for " sp_from)
495                 sp_share[_who] = sp_from
496         }
497 }
498
499 /^\.deny \S+$/ {
500         _who = $2 in USERS ? USERS[$2]["auth"] : $2
501         _str = _who && _who != $2 ? $2 " (" _who ")" : $2
502         if (sp_state ~ "new|join") {
503                 reply("The game has not yet started")
504         }
505         else if (!(sp_from in sp_players)) {
506                 reply("You are not playing")
507         }
508         else if (_who in sp_players || _who in sp_auths) {
509                 reply(_str " is a primary player")
510         }
511         else if (!(_who in sp_share) || sp_share[_who] != sp_from) {
512                 reply(_str " is not playing for " sp_from)
513         }
514         else {
515                 reply(_str " can no longer play for " sp_from)
516                 delete sp_share[_who]
517         }
518 }
519
520 sp_state ~ "(bid|pass|play)" &&
521 /^\.show/ {
522         for (_i in sp_share)
523                 say(_i " can play for " sp_share[_i]);
524 }
525
526 !sp_valid &&
527 (sp_state == "bid" || sp_state == "play") &&
528 /^\.(bid|play)\>/ {
529         if (sp_from in sp_players)
530                 say(".slap " FROM ", it is not your turn.")
531         else
532                 say(".slap " FROM ", you are not playing.")
533 }
534
535 sp_valid &&
536 sp_state == "bid" &&
537 /^\.bid [0-9]+$/ {
538         if ($2 < 0 || $2 > 13) {
539                 say("You can only bid from 0 to 13")
540         } else {
541                 i = sp_next()
542                 sp_bids[i] = $2
543                 if ($2 == 0 && !sp_looked[i]) {
544                         say(FROM " goes blind nil!")
545                         sp_nil[i] = 2
546                 } else if ($2 == 0) {
547                         say(FROM " goes nil!")
548                         sp_nil[i] = 1
549                 } else {
550                         sp_nil[i] = 0
551                 }
552                 if (sp_turn != sp_dealer) {
553                         say("Bidding goes to " sp_player "!")
554                 } else {
555                         for (p in sp_players)
556                                 say(p, "You have: " sp_hand(p, p))
557                         sp_state = "play"
558                         for (i=0; i<2; i++) {
559                                 if (sp_nil[i] == 2 || sp_nil[i+2] == 2) {
560                                         say(sp_team(i) ": select a card to pass " \
561                                             "(/msg " NICK " .pass <card>)")
562                                         sp_state = "pass"
563                                 }
564                         }
565                         if (sp_state == "play")
566                                 say("Play starts with " sp_player "!")
567                 }
568         }
569 }
570
571 sp_state == "pass" &&
572 /^\.pass (\S+)$/ {
573         _card = $2
574         _team = sp_from in sp_players ? sp_players[sp_from] % 2 : 0
575
576         # check validity and pass
577         if (!(sp_from in sp_players)) {
578                 say(".slap " FROM ", you are not playing.")
579         }
580         else if (sp_nil[_team] != 2 && sp_nil[_team+2] != 2) {
581                 reply("Your team did not go blind")
582         }
583         else if (sp_pass[sp_players[sp_from]]) {
584                 reply("You have already passed a card")
585         }
586         else if (!(_card in sp_deck)) {
587                 reply("Invalid card")
588         }
589         else if (!(_card in sp_hands[sp_from])) {
590                 reply("You do not have that card")
591         }
592         else {
593                 sp_pass[sp_players[sp_from]] = $2
594                 say(sp_channel, FROM " passes a card")
595         }
596
597         # check for end of passing
598         if (((sp_nil[0] != 2 && sp_nil[2] != 2) || (sp_pass[0] && sp_pass[2])) &&
599             ((sp_nil[1] != 2 && sp_nil[3] != 2) || (sp_pass[1] && sp_pass[3]))) {
600                 for (i in sp_pass) {
601                         _partner = (i+2)%4
602                         _card    = sp_pass[i]
603                         delete sp_hands[sp_order[i]][_card]
604                         sp_hands[sp_order[_partner]][_card] = 1
605                 }
606                 say(sp_channel, "Cards have been passed, play starts with " sp_player "!")
607                 for (p in sp_players)
608                         say(p, "You have: " sp_hand(p, p))
609                 sp_state = "play"
610         }
611 }
612
613 sp_state ~ "(bid|pass|play)" &&
614 /^\.look$/ {
615         if (!(sp_from in sp_players)) {
616                 say(".slap " FROM ", you are not playing.")
617         } else {
618                 sp_looked[sp_players[sp_from]] = 1
619                 say(FROM, "You have: " sp_hand(FROM, sp_from))
620         }
621 }
622
623 sp_valid &&
624 sp_state == "play" &&
625 /^\.play (\S+)/ {
626         _card = $2
627         gsub(/[^A-Za-z0-9]/, "", _card);
628         if (!(_card in sp_deck)) {
629                 reply("Invalid card")
630         }
631         else if (!(_card in sp_hands[sp_from])) {
632                 reply("You do not have that card")
633         }
634         else if (sp_suit && _card !~ sp_suit && sp_hasa(sp_from, sp_suit)) {
635                 reply("You must follow suit (" sp_suit ")")
636         }
637         else if (_card ~ /s/ && length(sp_hands[sp_from]) == 13 && sp_hasa(sp_from, "[^s]$")) {
638                 reply("You cannot trump on the first hand")
639         }
640         else if (_card ~ /s/ && length(sp_pile) == 0 && sp_hasa(sp_from, "[^s]$") && !sp_broken) {
641                 reply("Spades have not been broken")
642         }
643         else {
644                 sp_play(_card)
645                 if (sp_state == "play") {
646                         if (length(sp_hands[sp_from]))
647                                 say(FROM, "You have: " sp_hand(FROM, sp_from))
648                         if (sp_piles)
649                                 say(sp_player ": it is your turn! " \
650                                     "(" sp_pretty(sp_piles, sp_player) ")")
651                         else
652                                 say(sp_player ": it is your turn!")
653                 }
654         }
655 }
656
657 /^\.turn/ && sp_state ~ "(bid|pass|play)" {
658         _bids = sp_bidders()
659         _pile = sp_pretty(sp_piles, FROM)
660         if (sp_state == "bid" && !_bids)
661                 say("It is " sp_player "'s bid!")
662         if (sp_state == "bid" && _bids)
663                 say("It is " sp_player "'s bid! (" _bids ")")
664         if (sp_state == "play" && !_pile)
665                 say("It is " sp_player "'s turn!")
666         if (sp_state == "play" && _pile)
667                 say("It is " sp_player "'s turn! (" _pile ")")
668         for (_i=0; sp_state == "pass" && _i<4; _i++)
669                 if ((sp_nil[_i%2+0]==2 || sp_nil[_i%2+2]==2) && !sp_pass[_i])
670                         say("Waiting for " sp_order[_i] " to pass a card!")
671 }
672
673 /^\.bids$/ && sp_state ~ "(pass|play)" {
674         say(sp_order[0] " bid " sp_bid(0) ", " \
675             sp_order[2] " bid " sp_bid(2) ", " \
676             "total: " sp_bids[0] + sp_bids[2])
677         say(sp_order[1] " bid " sp_bid(1) ", " \
678             sp_order[3] " bid " sp_bid(3) ", " \
679             "total: " sp_bids[1] + sp_bids[3])
680 }
681
682 /^\.tricks$/ && sp_state == "play" {
683         say(sp_order[0] " took " int(sp_tricks[0]) "/" sp_bid(0) ", " \
684             sp_order[2] " took " int(sp_tricks[2]) "/" sp_bid(2))
685         say(sp_order[1] " took " int(sp_tricks[1]) "/" sp_bid(1) ", " \
686             sp_order[3] " took " int(sp_tricks[3]) "/" sp_bid(3))
687 }
688
689 (TO == NICK || DST == sp_channel) &&
690 /^\.(score|status)$/ {
691         if (sp_state == "new") {
692                 say("There is no game in progress")
693         }
694         if (sp_state ~ "join|bid|pass|play") {
695                 say("Playing to: " \
696                     sp_playto " points, " \
697                     sp_limit  " bags")
698         }
699         if (sp_state == "join") {
700                 say("Waiting for players: " \
701                     sp_order[0] " " sp_order[1] " " \
702                     sp_order[2] " " sp_order[3])
703         }
704         if (sp_state ~ "bid|pass|play") {
705                 say(sp_team(0) ": " \
706                     int(sp_scores[0]) " points, " \
707                     int(sp_bags(0))   " bags")
708                 say(sp_team(1) ": " \
709                     int(sp_scores[1]) " points, " \
710                     int(sp_bags(1))   " bags")
711         }
712 }
713
714 /^\.((new|end|load)game|join|look|bid|pass|play)/ {
715         sp_save("var/sp_cur.json");
716 }