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