5 function sp_init(cards, tmp0, tmp1)
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"
13 for (i=1; i<=length(tmp0); i++)
17 function sp_reset(type)
21 sp_from = "" # The speakers player name
22 sp_valid = "" # It is the speaker turn
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
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
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
62 function sp_acopy(dst, src, key)
66 json_copy(dst, key, src[key])
69 function sp_save(file, game)
72 game["suit"] = sp_suit;
73 game["piles"] = sp_piles;
74 json_copy(game, "pile", sp_pile);
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);
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);
101 json_save(file, game);
104 function sp_load(file, game)
107 if (!json_load(file, game))
111 sp_suit = game["suit"];
112 sp_piles = game["piles"];
113 sp_acopy(sp_pile, game["pile"]);
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"]);
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"]);
140 function sp_pretty(cards, who)
143 gsub(/[0-9JQKA]*[sc]/, "\0031,00\002&\017", cards) # black
144 gsub(/[0-9JQKA]*[hd]/, "\0034,00\002&\017", cards) # red
146 if (!nounicode[who]) {
147 gsub(/s/, "\002♠", cards)
148 gsub(/h/, "\002♥", cards)
149 gsub(/d/, "\002♦", cards)
150 gsub(/c/, "\002♣", cards)
155 function sp_next(who, prev)
158 sp_turn = who ? sp_players[who] : (sp_turn + 1) % 4
159 if (length(sp_order) == 4)
160 sp_player = sp_order[sp_turn]
164 function sp_deal( shuf)
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
171 sp_dealer = (sp_dealer+1)%4
173 sp_player = sp_order[sp_turn]
174 say("Bidding starts with " sp_player "!")
177 function sp_hand(to, who, sort, str)
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)
186 function sp_hasa(who, expr)
188 for (c in sp_hands[who]) {
195 function sp_type(card)
197 return substr(card, length(card))
200 function sp_usort(a,b,c,d) {
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;
209 function sp_winner( card, tmp)
211 for (card in sp_pile)
212 if (card !~ sp_suit && card !~ /s/)
214 asorti(sp_pile, tmp, "sp_csort")
215 #print "pile: " tmp[1] ">" tmp[2] ">" tmp[3] ">" tmp[4]
221 #return "{" sp_order[i+0] "," sp_order[i+2] "}"
222 return sp_order[i+0] "/" sp_order[i+2]
225 function sp_bags(i, bags)
227 bags = sp_scores[i] % sp_limit
235 return sp_nil[who] == 0 ? sp_bids[who] :
236 sp_nil[who] == 1 ? "nil" :
237 sp_nil[who] == 2 ? "blind" : "n/a"
240 function sp_bidders( i, turn, bid, bids)
242 for (i = 0; i < 4; i++)
243 if (bid = sp_bid((sp_dealer + i) % 4))
245 gsub(/^ +| +$/, "", bids)
249 function sp_score( bids, times, tricks)
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]
255 times = int((sp_bags(i) + bags) / sp_limit)
257 say(sp_team(i) " bag" (times>1?" way ":" ") "out")
258 sp_scores[i] -= sp_limit * 10 * times;
260 if (tricks >= bids) {
261 say(sp_team(i) " make their bid: " tricks "/" bids)
262 sp_scores[i] += bids*10 + bags;
264 say(sp_team(i) " go bust: " tricks "/" bids)
265 sp_scores[i] -= bids*10;
268 for (i=0; i<4; i++) {
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!" :
277 sp_scores[i%2] += sp_limit * 10 * sp_nil[i] * \
278 (sp_tricks[i] == 0 ? 1 : -1)
282 function sp_play(card, winner, pi)
284 delete sp_hands[sp_from][card]
285 sp_pile[card] = sp_player
286 sp_piles = sp_piles (sp_piles?",":"") card
293 if (length(sp_pile) == 1)
294 sp_suit = sp_type(card)
297 if (length(sp_pile) == 4) {
299 pi = sp_players[sp_pile[winner]]
301 say(sp_pile[winner] " wins with " sp_pretty(winner, FROM) \
302 " (" sp_pretty(sp_piles, FROM) ")")
303 sp_next(sp_pile[winner])
308 if (sp_tricks[0] + sp_tricks[1] + \
309 sp_tricks[2] + sp_tricks[3] == 13) {
312 if (sp_scores[0] >= sp_playto || sp_scores[1] >= sp_playto &&
313 sp_scores[0] != sp_scores[1]) {
315 winner = sp_scores[0] > sp_scores[1] ? 0 : 1
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] "++")
324 if (sp_scores[0] == sp_scores[1] &&
325 sp_scores[0] >= sp_playto)
326 say("It's tie! Playing an extra round!");
335 cmd = "od -An -N4 -td4 /dev/random"
341 sp_load("var/sp_cur.json");
343 # say(sp_channel, "Game restored.")
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
355 say("Spades! " sp_pretty("As,Ah,Ad,Ac", FROM))
360 sp_save("var/sp_save.json");
366 sp_load("var/sp_save.json");
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")
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")
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")
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")
410 /^\.deal (\w+) (.*)/ {
411 say(sp_channel, FROM " is cheating for " $2)
413 for (i=3; i<=NF; i++)
419 sp_state == "play" &&
420 /^\.force (\w+) (\S+)$/ {
421 say(sp_channel, FROM " is cheating for " $2)
429 /^\.newgame ?([0-9]+)?$/ {
430 if (sp_state != "new") {
431 reply("There is already a game in progress.")
435 sp_playto = $2 ? $2 : 200
436 sp_limit = sp_playto > 200 ? 10 : 5;
439 say(sp_owner " starts a game of Spades to " sp_playto " with " sp_limit " bags!")
443 (sp_from == sp_owner || AUTH == OWNER) &&
445 if (sp_state == "new") {
446 reply("There is no game in progress.")
448 say(FROM " ends the game")
454 if (sp_state == "new") {
455 reply("There is no game in progress")
457 else if (sp_state == "play") {
458 reply("The game has already started")
460 else if (sp_state == "join" && sp_from in sp_players) {
461 reply("You are already playing")
463 else if (sp_state == "join") {
467 sp_auths[AUTH] = FROM
469 say(FROM " joins the game!")
471 if (sp_state == "join" && sp_turn == 0)
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")
481 else if (!(sp_from in sp_players)) {
482 reply("You are not playing")
485 reply(_str " is not logged in")
487 else if (_who in sp_players || _who in sp_auths) {
488 reply(_str " is a primary player")
490 else if (_who in sp_share) {
491 reply(_str " is already playing for " sp_share[_who])
494 reply(_str " can now play for " sp_from)
495 sp_share[_who] = sp_from
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")
505 else if (!(sp_from in sp_players)) {
506 reply("You are not playing")
508 else if (_who in sp_players || _who in sp_auths) {
509 reply(_str " is a primary player")
511 else if (!(_who in sp_share) || sp_share[_who] != sp_from) {
512 reply(_str " is not playing for " sp_from)
515 reply(_str " can no longer play for " sp_from)
516 delete sp_share[_who]
520 sp_state ~ "(bid|pass|play)" &&
523 say(_i " can play for " sp_share[_i]);
527 (sp_state == "bid" || sp_state == "play") &&
529 if (sp_from in sp_players)
530 say(".slap " FROM ", it is not your turn.")
532 say(".slap " FROM ", you are not playing.")
538 if ($2 < 0 || $2 > 13) {
539 say("You can only bid from 0 to 13")
543 if ($2 == 0 && !sp_looked[i]) {
544 say(FROM " goes blind nil!")
546 } else if ($2 == 0) {
547 say(FROM " goes nil!")
552 if (sp_turn != sp_dealer) {
553 say("Bidding goes to " sp_player "!")
555 for (p in sp_players)
556 say(p, "You have: " sp_hand(p, p))
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>)")
565 if (sp_state == "play")
566 say("Play starts with " sp_player "!")
571 sp_state == "pass" &&
574 _team = sp_from in sp_players ? sp_players[sp_from] % 2 : 0
576 # check validity and pass
577 if (!(sp_from in sp_players)) {
578 say(".slap " FROM ", you are not playing.")
580 else if (sp_nil[_team] != 2 && sp_nil[_team+2] != 2) {
581 reply("Your team did not go blind")
583 else if (sp_pass[sp_players[sp_from]]) {
584 reply("You have already passed a card")
586 else if (!(_card in sp_deck)) {
587 reply("Invalid card")
589 else if (!(_card in sp_hands[sp_from])) {
590 reply("You do not have that card")
593 sp_pass[sp_players[sp_from]] = $2
594 say(sp_channel, FROM " passes a card")
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]))) {
603 delete sp_hands[sp_order[i]][_card]
604 sp_hands[sp_order[_partner]][_card] = 1
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))
613 sp_state ~ "(bid|pass|play)" &&
615 if (!(sp_from in sp_players)) {
616 say(".slap " FROM ", you are not playing.")
618 sp_looked[sp_players[sp_from]] = 1
619 say(FROM, "You have: " sp_hand(FROM, sp_from))
624 sp_state == "play" &&
627 gsub(/[^A-Za-z0-9]/, "", _card);
628 if (!(_card in sp_deck)) {
629 reply("Invalid card")
631 else if (!(_card in sp_hands[sp_from])) {
632 reply("You do not have that card")
634 else if (sp_suit && _card !~ sp_suit && sp_hasa(sp_from, sp_suit)) {
635 reply("You must follow suit (" sp_suit ")")
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")
640 else if (_card ~ /s/ && length(sp_pile) == 0 && sp_hasa(sp_from, "[^s]$") && !sp_broken) {
641 reply("Spades have not been broken")
645 if (sp_state == "play") {
646 if (length(sp_hands[sp_from]))
647 say(FROM, "You have: " sp_hand(FROM, sp_from))
649 say(sp_player ": it is your turn! " \
650 "(" sp_pretty(sp_piles, sp_player) ")")
652 say(sp_player ": it is your turn!")
657 /^\.turn/ && sp_state ~ "(bid|pass|play)" {
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!")
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])
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))
689 (TO == NICK || DST == sp_channel) &&
690 /^\.(score|status)$/ {
691 if (sp_state == "new") {
692 say("There is no game in progress")
694 if (sp_state ~ "join|bid|pass|play") {
696 sp_playto " points, " \
699 if (sp_state == "join") {
700 say("Waiting for players: " \
701 sp_order[0] " " sp_order[1] " " \
702 sp_order[2] " " sp_order[3])
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")
714 /^\.((new|end|load)game|join|look|bid|pass|play)/ {
715 sp_save("var/sp_cur.json");