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_hands # [p] Each players cards
37 delete sp_looked # [i] Whether a player has looked a their cards
38 delete sp_bids # [i] Each players bid
39 delete sp_nil # [i] Nil multiplier 0=regular, 1=nil, 2=blind
40 delete sp_pass # [i] Cards to pass
41 delete sp_tricks # [i] Tricks this round
46 sp_channel = "" # channel to play in
47 sp_state = "new" # {new,join,bid,pass,play}
48 sp_owner = "" # Who started the game
49 sp_playto = 0 # Score the game will go to
50 sp_dealer =-1 # Who is dealing this round
51 sp_turn = 0 # Index of who's turn it is
52 sp_player = "" # Who's turn it is
53 sp_limit = 10 # Bag out limit / nil bonus
54 delete sp_players # [p] Player names players["name"] -> i
55 delete sp_auths # [c] Player auth names auths["auth"] -> "name"
56 delete sp_share # [c] Player teammates share["friend"] -> "name"
57 delete sp_order # [i] Player order order[i] -> "name"
58 delete sp_scores # [i] Teams score
62 function sp_acopy(dst, src, key)
67 json_copy(dst, key, src[key])
71 function sp_save(file, game)
74 game["suit"] = sp_suit;
75 game["piles"] = sp_piles;
76 json_copy(game, "pile", sp_pile);
79 game["state"] = sp_state;
80 game["broken"] = sp_broken;
81 json_copy(game, "looked", sp_looked);
82 json_copy(game, "bids", sp_bids);
83 json_copy(game, "nil", sp_nil);
84 json_copy(game, "pass", sp_pass);
85 json_copy(game, "tricks", sp_tricks);
88 game["channel"] = sp_channel;
89 game["owner"] = sp_owner;
90 game["playto"] = sp_playto;
91 game["dealer"] = sp_dealer;
92 game["turn"] = sp_turn;
93 game["player"] = sp_player;
94 game["limit"] = sp_limit;
95 json_copy(game, "hands", sp_hands);
96 json_copy(game, "players", sp_players);
97 json_copy(game, "auths", sp_auths);
98 json_copy(game, "share", sp_share);
99 json_copy(game, "order", sp_order);
100 json_copy(game, "scores", sp_scores);
103 json_save(file, game);
106 function sp_load(file, game)
109 if (!json_load(file, game))
113 sp_suit = game["suit"];
114 sp_piles = game["piles"];
115 sp_acopy(sp_pile, game["pile"]);
118 sp_state = game["state"];
119 sp_broken = game["broken"];
120 sp_acopy(sp_looked, game["looked"]);
121 sp_acopy(sp_bids, game["bids"]);
122 sp_acopy(sp_nil, game["nil"]);
123 sp_acopy(sp_pass, game["pass"]);
124 sp_acopy(sp_tricks, game["tricks"]);
127 sp_channel = game["channel"];
128 sp_owner = game["owner"];
129 sp_playto = game["playto"];
130 sp_dealer = game["dealer"];
131 sp_turn = game["turn"];
132 sp_player = game["player"];
133 sp_limit = game["limit"];
134 sp_acopy(sp_hands, game["hands"]);
135 sp_acopy(sp_players, game["players"]);
136 sp_acopy(sp_auths, game["auths"]);
137 sp_acopy(sp_share, game["share"]);
138 sp_acopy(sp_order, game["order"]);
139 sp_acopy(sp_scores, game["scores"]);
142 function sp_pretty(cards, who)
145 gsub(/[0-9JQKA]*[sc]/, "\0031,00\002&\017", cards) # black
146 gsub(/[0-9JQKA]*[hd]/, "\0034,00\002&\017", cards) # red
148 if (!nounicode[who]) {
149 gsub(/s/, "\002♠", cards)
150 gsub(/h/, "\002♥", cards)
151 gsub(/d/, "\002♦", cards)
152 gsub(/c/, "\002♣", cards)
157 function sp_next(who, prev)
160 sp_turn = who ? sp_players[who] : (sp_turn + 1) % 4
161 if (length(sp_order) == 4)
162 sp_player = sp_order[sp_turn]
166 function sp_shuf(i, mixed)
168 sp_usort(sp_players, mixed)
170 sp_order[i-1] = mixed[i]
171 sp_players[mixed[i]] = i-1
175 function sp_deal( shuf)
177 say("/me deals the cards")
178 sp_usort(sp_deck, shuf)
179 for (i=1; i<=52; i++)
180 sp_hands[sp_order[i%4]][shuf[i]] = 1
182 sp_dealer = (sp_dealer+1)%4
184 sp_player = sp_order[sp_turn]
185 say("Bidding starts with " sp_player "!")
188 function sp_hand(to, who, sort, str)
190 asorti(sp_hands[who], sort, "sp_csort")
191 for (i=0; i<length(sort); i++)
192 str = str "" sprintf("%4s", sort[i])
193 gsub(/^ +| +$/, "", str)
194 return sp_pretty(str, to)
197 function sp_hasa(who, expr)
199 for (c in sp_hands[who]) {
206 function sp_type(card)
208 return substr(card, length(card))
211 function sp_usort(list, out) {
214 asorti(out, out, "@val_num_asc")
217 function sp_csort(i1,v1,i2,v2) {
218 return sp_deck[i1] > sp_deck[i2] ? +1 :
219 sp_deck[i1] < sp_deck[i2] ? -1 : 0;
222 function sp_winner( card, tmp)
224 for (card in sp_pile)
225 if (card !~ sp_suit && card !~ /s/)
227 asorti(sp_pile, tmp, "sp_csort")
228 #print "pile: " tmp[1] ">" tmp[2] ">" tmp[3] ">" tmp[4]
234 #return "{" sp_order[i+0] "," sp_order[i+2] "}"
235 return sp_order[i+0] "/" sp_order[i+2]
238 function sp_bags(i, bags)
240 bags = sp_scores[i] % sp_limit
248 return sp_nil[who] == 0 ? sp_bids[who] :
249 sp_nil[who] == 1 ? "nil" :
250 sp_nil[who] == 2 ? "blind" : "n/a"
253 function sp_passer(who)
255 return sp_nil[(who+0)%4] == 2 || sp_nil[(who+1)%4] != 0 ||
256 sp_nil[(who+2)%4] == 2 || sp_nil[(who+3)%4] != 0
259 function sp_bidders( i, turn, bid, bids)
261 for (i = 0; i < 4; i++) {
262 turn = (sp_dealer + i) % 4
263 if (bid = sp_bid(turn))
264 bids = bids " " sp_order[turn] ":" bid
266 gsub(/^ +| +$/, "", bids)
270 function sp_score( bids, times, tricks)
272 for (i=0; i<2; i++) {
273 bids = sp_bids[i] + sp_bids[i+2]
274 tricks = sp_tricks[i] + sp_tricks[i+2]
276 times = int((sp_bags(i) + bags) / sp_limit)
278 say(sp_team(i) " bag" (times>1?" way ":" ") "out")
279 sp_scores[i] -= sp_limit * 10 * times;
281 if (tricks >= bids) {
282 say(sp_team(i) " make their bid: " tricks "/" bids)
283 sp_scores[i] += bids*10 + bags;
285 say(sp_team(i) " go bust: " tricks "/" bids)
286 sp_scores[i] -= bids*10;
289 for (i=0; i<4; i++) {
292 say(sp_order[i] " " \
293 (sp_nil[i] == 1 && !sp_tricks[i] ? "makes nil!" :
294 sp_nil[i] == 1 && sp_tricks[i] ? "fails at nil!" :
295 sp_nil[i] == 2 && !sp_tricks[i] ? "makes blind nil!" :
296 sp_nil[i] == 2 && sp_tricks[i] ? "fails miserably at blind nil!" :
298 sp_scores[i%2] += sp_limit * 10 * sp_nil[i] * \
299 (sp_tricks[i] == 0 ? 1 : -1)
301 if (sp_scores[0] > sp_scores[1])
302 say(sp_team(0) " lead " sp_scores[0] " to " sp_scores[1] " of " sp_playto)
303 else if (sp_scores[1] > sp_scores[0])
304 say(sp_team(1) " lead " sp_scores[1] " to " sp_scores[0] " of " sp_playto)
306 say("tied at " sp_scores[0])
309 function sp_play(card, winner, pi)
311 delete sp_hands[sp_from][card]
312 sp_pile[card] = sp_player
313 sp_piles = sp_piles (sp_piles?",":"") card
320 if (length(sp_pile) == 1)
321 sp_suit = sp_type(card)
324 if (length(sp_pile) == 4) {
326 pi = sp_players[sp_pile[winner]]
328 say(sp_pile[winner] " wins with " sp_pretty(winner, FROM) \
329 " (" sp_pretty(sp_piles, FROM) ")")
330 sp_next(sp_pile[winner])
335 if (sp_tricks[0] + sp_tricks[1] + \
336 sp_tricks[2] + sp_tricks[3] == 13) {
339 if (sp_scores[0] >= sp_playto || sp_scores[1] >= sp_playto &&
340 sp_scores[0] != sp_scores[1]) {
342 winner = sp_scores[0] > sp_scores[1] ? 0 : 1
344 say(CHANNEL, sp_team(winner) " wins the game " \
345 sp_scores[winner] " to " sp_scores[looser])
346 say(CHANNEL, sp_order[winner+0] "++")
347 say(CHANNEL, sp_order[winner+2] "++")
351 if (sp_scores[0] == sp_scores[1] &&
352 sp_scores[0] >= sp_playto)
353 say("It's tie! Playing an extra round!");
362 cmd = "od -An -N4 -td4 /dev/random"
368 sp_load("var/sp_cur.json");
370 # say(sp_channel, "Game restored.")
374 sp_from = AUTH in sp_auths ? sp_auths[AUTH] : \
375 AUTH in sp_share ? sp_share[AUTH] : FROM
376 sp_valid = sp_from && sp_from == sp_player
382 say("Spades! " sp_pretty("As,Ah,Ad,Ac", FROM))
387 sp_save("var/sp_save.json");
393 sp_load("var/sp_save.json");
398 /^\.help [Ss]pades$/ {
399 say("Spades -- play a game of spades")
400 say(".help game -- setup and administer the game")
401 say(".help play -- commands for playing spades")
402 say(".help auth -- control player authorization")
407 say(".newgame [score] -- start a game to <score> points, default 500")
408 say(".endgame -- abort the current game")
409 say(".savegame -- save the current game to disk")
410 say(".loadgame -- load the previously saved game")
415 say(".join -- join the current game")
416 say(".look -- look at your cards")
417 say(".bid [n] -- bid for <n> tricks")
418 say(".pass [card] -- pass a card to your partner")
419 say(".play [card] -- play a card")
420 say(".turn -- check whose turn it is")
421 say(".bids -- check what everyone bid")
422 say(".tricks -- check how many trick have been taken")
423 say(".score -- check the score")
428 say(".auth [who] -- display authentication info for a user")
429 say(".allow [who] -- allow another person to play on your behalf")
430 say(".deny [who] -- prevent a previously allowed user from playing")
431 say(".show -- display which users can play for which players")
437 /^\.deal (\w+) (.*)/ {
438 say(sp_channel, FROM " is cheating for " $2)
440 for (i=3; i<=NF; i++)
446 /^\.order (\w+) ([0-4])/ {
447 say(sp_channel, FROM " is cheating for " $2)
450 sp_player = sp_order[sp_turn]
454 sp_state == "play" &&
455 /^\.force (\w+) (\S+)$/ {
456 say(sp_channel, FROM " is cheating for " $2)
464 match($0, /^\.newgame ?([1-9][0-9]*) *- *([1-9][0-9]*)$/, _arr) {
465 if (_arr[2] > _arr[1])
466 $0 = $1 " " int(rand() * (_arr[2]-_arr[1])+_arr[1])
469 /^\.newgame ?([1-9][0-9]*)?$/ {
470 if (sp_state != "new") {
471 reply("There is already a game in progress.")
475 sp_playto = $2 ? $2 : 200
476 sp_limit = sp_playto > 200 ? 10 : 5;
479 say(sp_owner " starts a game of Spades to " sp_playto " with " sp_limit " bags!")
483 (sp_from == sp_owner || AUTH == OWNER) &&
485 if (sp_state == "new") {
486 reply("There is no game in progress.")
488 say(FROM " ends the game")
494 if (sp_state == "new") {
495 reply("There is no game in progress")
497 else if (sp_state == "play") {
498 reply("The game has already started")
500 else if (sp_state == "join" && sp_from in sp_players) {
501 reply("You are already playing")
503 else if (sp_state == "join") {
507 sp_auths[AUTH] = FROM
509 say(FROM " joins the game!")
511 if (sp_state == "join" && sp_turn == 0) {
518 _who = $2 in USERS ? USERS[$2]["auth"] : ""
519 _str = _who && _who != $2 ? $2 " (" _who ")" : $2
520 if (sp_state ~ "new|join") {
521 reply("The game has not yet started")
523 else if (!(sp_from in sp_players)) {
524 reply("You are not playing")
527 reply(_str " is not logged in")
529 else if (_who in sp_players || _who in sp_auths) {
530 reply(_str " is a primary player")
532 else if (_who in sp_share) {
533 reply(_str " is already playing for " sp_share[_who])
536 reply(_str " can now play for " sp_from)
537 sp_share[_who] = sp_from
542 _who = $2 in USERS ? USERS[$2]["auth"] : $2
543 _str = _who && _who != $2 ? $2 " (" _who ")" : $2
544 if (sp_state ~ "new|join") {
545 reply("The game has not yet started")
547 else if (!(sp_from in sp_players)) {
548 reply("You are not playing")
550 else if (_who in sp_players || _who in sp_auths) {
551 reply(_str " is a primary player")
553 else if (!(_who in sp_share) || sp_share[_who] != sp_from) {
554 reply(_str " is not playing for " sp_from)
557 reply(_str " can no longer play for " sp_from)
558 delete sp_share[_who]
562 sp_state ~ "(bid|pass|play)" &&
566 _lines[sp_share[_i]] = _lines[sp_share[_i]] " " _i
568 say(_i " allowed:" _lines[_i])
572 (sp_state == "bid" || sp_state == "play") &&
574 if (sp_from in sp_players)
575 say(".slap " FROM ", it is not your turn.")
577 say(".slap " FROM ", you are not playing.")
582 /^\.bid [1-9][0-9]*$/ {
583 if ($2 < 0 || $2 > 13) {
584 say("You can only bid from 0 to 13")
588 if ($2 == 0 && !sp_looked[i]) {
589 say(FROM " goes blind nil!")
591 } else if ($2 == 0) {
592 say(FROM " goes nil!")
597 if (sp_turn != sp_dealer) {
598 say("Bidding goes to " sp_player "!")
600 for (p in sp_players)
601 say(p, "You have: " sp_hand(p, p))
603 for (i=0; i<2; i++) {
605 say(sp_team(i) ": select a card to pass " \
606 "(/msg " NICK " .pass <card>)")
610 if (sp_state == "play")
611 say("Play starts with " sp_player "!")
616 sp_state == "pass" &&
619 _team = sp_from in sp_players ? sp_players[sp_from] % 2 : 0
621 # check validity and pass
622 if (!(sp_from in sp_players)) {
623 say(".slap " FROM ", you are not playing.")
625 else if (!sp_passer(_team)) {
626 reply("Your team did not go blind")
628 else if (sp_pass[sp_players[sp_from]]) {
629 reply("You have already passed a card")
631 else if (!(_card in sp_deck)) {
632 reply("Invalid card")
634 else if (!(_card in sp_hands[sp_from])) {
635 reply("You do not have that card")
638 sp_pass[sp_players[sp_from]] = $2
639 say(sp_channel, FROM " passes a card")
642 # check for end of passing
643 if ((!sp_passer(0) || (sp_pass[0] && sp_pass[2])) &&
644 (!sp_passer(1) || (sp_pass[1] && sp_pass[3]))) {
648 delete sp_hands[sp_order[i]][_card]
649 sp_hands[sp_order[_partner]][_card] = 1
651 say(sp_channel, "Cards have been passed, play starts with " sp_player "!")
652 for (p in sp_players)
653 say(p, "You have: " sp_hand(p, p))
658 sp_state ~ "(bid|pass|play)" &&
660 if (!(sp_from in sp_players)) {
661 say(".slap " FROM ", you are not playing.")
663 sp_looked[sp_players[sp_from]] = 1
664 say(FROM, "You have: " sp_hand(FROM, sp_from))
669 sp_state == "play" &&
672 gsub(/[^A-Za-z0-9]/, "", _card);
673 if (!(_card in sp_deck)) {
674 reply("Invalid card")
676 else if (!(_card in sp_hands[sp_from])) {
677 reply("You do not have that card")
679 else if (sp_suit && _card !~ sp_suit && sp_hasa(sp_from, sp_suit)) {
680 reply("You must follow suit (" sp_suit ")")
682 else if (_card ~ /s/ && length(sp_hands[sp_from]) == 13 && sp_hasa(sp_from, "[^s]$")) {
683 reply("You cannot trump on the first hand")
685 else if (_card ~ /s/ && length(sp_pile) == 0 && sp_hasa(sp_from, "[^s]$") && !sp_broken) {
686 reply("Spades have not been broken")
690 if (sp_state == "play") {
691 if (length(sp_hands[sp_from]))
692 say(FROM, "You have: " sp_hand(FROM, sp_from))
694 say(sp_player ": it is your turn! " \
695 "(" sp_pretty(sp_piles, sp_player) ")")
697 say(sp_player ": it is your turn!")
702 /^\.bids/ && sp_state == "bid" ||
703 /^\.turn/ && sp_state ~ "(bid|pass|play)" {
705 _pile = sp_pretty(sp_piles, FROM)
709 if (/!/ && sp_share[_i] == sp_player)
710 _extra = _extra " " _i "!"
712 if (sp_state == "bid" && !_bids)
713 say("It is " sp_player "'s bid!" _extra)
714 if (sp_state == "bid" && _bids)
715 say("It is " sp_player "'s bid!" _extra " (" _bids ")")
716 if (sp_state == "play" && !_pile)
717 say("It is " sp_player "'s turn!" _extra)
718 if (sp_state == "play" && _pile)
719 say("It is " sp_player "'s turn!" _extra " (" _pile ")")
721 for (_i=0; sp_state == "pass" && _i<4; _i++)
722 if (sp_passer(_i) && !sp_pass[_i])
723 say("Waiting for " sp_order[_i] " to pass a card!")
726 /^\.bids$/ && sp_state ~ "(pass|play)" {
727 say(sp_order[0] " bid " sp_bid(0) ", " \
728 sp_order[2] " bid " sp_bid(2) ", " \
729 "total: " sp_bids[0] + sp_bids[2])
730 say(sp_order[1] " bid " sp_bid(1) ", " \
731 sp_order[3] " bid " sp_bid(3) ", " \
732 "total: " sp_bids[1] + sp_bids[3])
735 /^\.tricks$/ && sp_state == "play" {
736 say(sp_order[0] " took " int(sp_tricks[0]) "/" sp_bid(0) ", " \
737 sp_order[2] " took " int(sp_tricks[2]) "/" sp_bid(2))
738 say(sp_order[1] " took " int(sp_tricks[1]) "/" sp_bid(1) ", " \
739 sp_order[3] " took " int(sp_tricks[3]) "/" sp_bid(3))
742 (TO == NICK || DST == sp_channel) &&
743 /^\.(score|status)$/ {
744 if (sp_state == "new") {
745 say("There is no game in progress")
747 if (sp_state ~ "join|bid|pass|play") {
749 sp_playto " points, " \
752 if (sp_state == "join") {
753 say("Waiting for players: " \
754 sp_order[0] " " sp_order[1] " " \
755 sp_order[2] " " sp_order[3])
757 if (sp_state ~ "bid|pass|play") {
758 say(sp_team(0) ": " \
759 int(sp_scores[0]) " points, " \
760 int(sp_bags(0)) " bags")
761 say(sp_team(1) ": " \
762 int(sp_scores[1]) " points, " \
763 int(sp_bags(1)) " bags")
767 /^\.((new|end|load)game|join|look|bid|pass|play)/ {
768 sp_save("var/sp_cur.json");