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_shuf(i, mixed)
166 sp_usort(sp_players, mixed)
168 sp_order[i-1] = mixed[i]
169 sp_players[mixed[i]] = i-1
173 function sp_deal( shuf)
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
180 sp_dealer = (sp_dealer+1)%4
182 sp_player = sp_order[sp_turn]
183 say("Bidding starts with " sp_player "!")
186 function sp_hand(to, who, sort, str)
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)
195 function sp_hasa(who, expr)
197 for (c in sp_hands[who]) {
204 function sp_type(card)
206 return substr(card, length(card))
209 function sp_usort(list, out) {
212 asorti(out, out, "@val_num_asc")
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;
220 function sp_winner( card, tmp)
222 for (card in sp_pile)
223 if (card !~ sp_suit && card !~ /s/)
225 asorti(sp_pile, tmp, "sp_csort")
226 #print "pile: " tmp[1] ">" tmp[2] ">" tmp[3] ">" tmp[4]
232 #return "{" sp_order[i+0] "," sp_order[i+2] "}"
233 return sp_order[i+0] "/" sp_order[i+2]
236 function sp_bags(i, bags)
238 bags = sp_scores[i] % sp_limit
246 return sp_nil[who] == 0 ? sp_bids[who] :
247 sp_nil[who] == 1 ? "nil" :
248 sp_nil[who] == 2 ? "blind" : "n/a"
251 function sp_bidders( i, turn, bid, bids)
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
258 gsub(/^ +| +$/, "", bids)
262 function sp_score( bids, times, tricks)
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]
268 times = int((sp_bags(i) + bags) / sp_limit)
270 say(sp_team(i) " bag" (times>1?" way ":" ") "out")
271 sp_scores[i] -= sp_limit * 10 * times;
273 if (tricks >= bids) {
274 say(sp_team(i) " make their bid: " tricks "/" bids)
275 sp_scores[i] += bids*10 + bags;
277 say(sp_team(i) " go bust: " tricks "/" bids)
278 sp_scores[i] -= bids*10;
281 for (i=0; i<4; i++) {
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!" :
290 sp_scores[i%2] += sp_limit * 10 * sp_nil[i] * \
291 (sp_tricks[i] == 0 ? 1 : -1)
295 function sp_play(card, winner, pi)
297 delete sp_hands[sp_from][card]
298 sp_pile[card] = sp_player
299 sp_piles = sp_piles (sp_piles?",":"") card
306 if (length(sp_pile) == 1)
307 sp_suit = sp_type(card)
310 if (length(sp_pile) == 4) {
312 pi = sp_players[sp_pile[winner]]
314 say(sp_pile[winner] " wins with " sp_pretty(winner, FROM) \
315 " (" sp_pretty(sp_piles, FROM) ")")
316 sp_next(sp_pile[winner])
321 if (sp_tricks[0] + sp_tricks[1] + \
322 sp_tricks[2] + sp_tricks[3] == 13) {
325 if (sp_scores[0] >= sp_playto || sp_scores[1] >= sp_playto &&
326 sp_scores[0] != sp_scores[1]) {
328 winner = sp_scores[0] > sp_scores[1] ? 0 : 1
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] "++")
337 if (sp_scores[0] == sp_scores[1] &&
338 sp_scores[0] >= sp_playto)
339 say("It's tie! Playing an extra round!");
348 cmd = "od -An -N4 -td4 /dev/random"
354 sp_load("var/sp_cur.json");
356 # say(sp_channel, "Game restored.")
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
368 say("Spades! " sp_pretty("As,Ah,Ad,Ac", FROM))
373 sp_save("var/sp_save.json");
379 sp_load("var/sp_save.json");
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")
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")
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")
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")
423 /^\.deal (\w+) (.*)/ {
424 say(sp_channel, FROM " is cheating for " $2)
426 for (i=3; i<=NF; i++)
432 sp_state == "play" &&
433 /^\.force (\w+) (\S+)$/ {
434 say(sp_channel, FROM " is cheating for " $2)
442 match($0, /^\.newgame ?([0-9]+) *- *([0-9]+)$/, _arr) {
443 if (_arr[2] > _arr[1])
444 $0 = $1 " " int(rand() * (_arr[2]-_arr[1])+_arr[1])
447 /^\.newgame ?([0-9]+)?$/ {
448 if (sp_state != "new") {
449 reply("There is already a game in progress.")
453 sp_playto = $2 ? $2 : 200
454 sp_limit = sp_playto > 200 ? 10 : 5;
457 say(sp_owner " starts a game of Spades to " sp_playto " with " sp_limit " bags!")
461 (sp_from == sp_owner || AUTH == OWNER) &&
463 if (sp_state == "new") {
464 reply("There is no game in progress.")
466 say(FROM " ends the game")
472 if (sp_state == "new") {
473 reply("There is no game in progress")
475 else if (sp_state == "play") {
476 reply("The game has already started")
478 else if (sp_state == "join" && sp_from in sp_players) {
479 reply("You are already playing")
481 else if (sp_state == "join") {
485 sp_auths[AUTH] = FROM
487 say(FROM " joins the game!")
489 if (sp_state == "join" && sp_turn == 0) {
496 _who = $2 in USERS ? USERS[$2]["auth"] : ""
497 _str = _who && _who != $2 ? $2 " (" _who ")" : $2
498 if (sp_state ~ "new|join") {
499 reply("The game has not yet started")
501 else if (!(sp_from in sp_players)) {
502 reply("You are not playing")
505 reply(_str " is not logged in")
507 else if (_who in sp_players || _who in sp_auths) {
508 reply(_str " is a primary player")
510 else if (_who in sp_share) {
511 reply(_str " is already playing for " sp_share[_who])
514 reply(_str " can now play for " sp_from)
515 sp_share[_who] = sp_from
520 _who = $2 in USERS ? USERS[$2]["auth"] : $2
521 _str = _who && _who != $2 ? $2 " (" _who ")" : $2
522 if (sp_state ~ "new|join") {
523 reply("The game has not yet started")
525 else if (!(sp_from in sp_players)) {
526 reply("You are not playing")
528 else if (_who in sp_players || _who in sp_auths) {
529 reply(_str " is a primary player")
531 else if (!(_who in sp_share) || sp_share[_who] != sp_from) {
532 reply(_str " is not playing for " sp_from)
535 reply(_str " can no longer play for " sp_from)
536 delete sp_share[_who]
540 sp_state ~ "(bid|pass|play)" &&
543 say(_i " can play for " sp_share[_i]);
547 (sp_state == "bid" || sp_state == "play") &&
549 if (sp_from in sp_players)
550 say(".slap " FROM ", it is not your turn.")
552 say(".slap " FROM ", you are not playing.")
558 if ($2 < 0 || $2 > 13) {
559 say("You can only bid from 0 to 13")
563 if ($2 == 0 && !sp_looked[i]) {
564 say(FROM " goes blind nil!")
566 } else if ($2 == 0) {
567 say(FROM " goes nil!")
572 if (sp_turn != sp_dealer) {
573 say("Bidding goes to " sp_player "!")
575 for (p in sp_players)
576 say(p, "You have: " sp_hand(p, p))
578 for (i=0; i<2; i++) {
579 if (sp_nil[(i+0)%4] == 2 || sp_nil[(i+2)%4] == 2 ||
580 sp_nil[(i+1)%4] != 0 || sp_nil[(i+3)%4] != 0) {
581 say(sp_team(i) ": select a card to pass " \
582 "(/msg " NICK " .pass <card>)")
586 if (sp_state == "play")
587 say("Play starts with " sp_player "!")
592 sp_state == "pass" &&
595 _team = sp_from in sp_players ? sp_players[sp_from] % 2 : 0
597 # check validity and pass
598 if (!(sp_from in sp_players)) {
599 say(".slap " FROM ", you are not playing.")
601 else if (sp_nil[_team] == 1 && sp_nil[_team+2] == 1) {
602 reply("Your team did not go blind")
604 else if (sp_pass[sp_players[sp_from]]) {
605 reply("You have already passed a card")
607 else if (!(_card in sp_deck)) {
608 reply("Invalid card")
610 else if (!(_card in sp_hands[sp_from])) {
611 reply("You do not have that card")
614 sp_pass[sp_players[sp_from]] = $2
615 say(sp_channel, FROM " passes a card")
618 # check for end of passing
619 if (((sp_nil[0] == 1 && sp_nil[2] == 1) || (sp_pass[0] && sp_pass[2])) &&
620 ((sp_nil[1] == 1 && sp_nil[3] == 1) || (sp_pass[1] && sp_pass[3]))) {
624 delete sp_hands[sp_order[i]][_card]
625 sp_hands[sp_order[_partner]][_card] = 1
627 say(sp_channel, "Cards have been passed, play starts with " sp_player "!")
628 for (p in sp_players)
629 say(p, "You have: " sp_hand(p, p))
634 sp_state ~ "(bid|pass|play)" &&
636 if (!(sp_from in sp_players)) {
637 say(".slap " FROM ", you are not playing.")
639 sp_looked[sp_players[sp_from]] = 1
640 say(FROM, "You have: " sp_hand(FROM, sp_from))
645 sp_state == "play" &&
648 gsub(/[^A-Za-z0-9]/, "", _card);
649 if (!(_card in sp_deck)) {
650 reply("Invalid card")
652 else if (!(_card in sp_hands[sp_from])) {
653 reply("You do not have that card")
655 else if (sp_suit && _card !~ sp_suit && sp_hasa(sp_from, sp_suit)) {
656 reply("You must follow suit (" sp_suit ")")
658 else if (_card ~ /s/ && length(sp_hands[sp_from]) == 13 && sp_hasa(sp_from, "[^s]$")) {
659 reply("You cannot trump on the first hand")
661 else if (_card ~ /s/ && length(sp_pile) == 0 && sp_hasa(sp_from, "[^s]$") && !sp_broken) {
662 reply("Spades have not been broken")
666 if (sp_state == "play") {
667 if (length(sp_hands[sp_from]))
668 say(FROM, "You have: " sp_hand(FROM, sp_from))
670 say(sp_player ": it is your turn! " \
671 "(" sp_pretty(sp_piles, sp_player) ")")
673 say(sp_player ": it is your turn!")
678 /^\.bids/ && sp_state == "bid" ||
679 /^\.turn/ && sp_state ~ "(bid|pass|play)" {
681 _pile = sp_pretty(sp_piles, FROM)
682 if (sp_state == "bid" && !_bids)
683 say("It is " sp_player "'s bid!")
684 if (sp_state == "bid" && _bids)
685 say("It is " sp_player "'s bid! (" _bids ")")
686 if (sp_state == "play" && !_pile)
687 say("It is " sp_player "'s turn!")
688 if (sp_state == "play" && _pile)
689 say("It is " sp_player "'s turn! (" _pile ")")
690 for (_i=0; sp_state == "pass" && _i<4; _i++)
691 if ((sp_nil[_i%2+0]!=1 || sp_nil[_i%2+2]!=1) && !sp_pass[_i])
692 say("Waiting for " sp_order[_i] " to pass a card!")
695 /^\.bids$/ && sp_state ~ "(pass|play)" {
696 say(sp_order[0] " bid " sp_bid(0) ", " \
697 sp_order[2] " bid " sp_bid(2) ", " \
698 "total: " sp_bids[0] + sp_bids[2])
699 say(sp_order[1] " bid " sp_bid(1) ", " \
700 sp_order[3] " bid " sp_bid(3) ", " \
701 "total: " sp_bids[1] + sp_bids[3])
704 /^\.tricks$/ && sp_state == "play" {
705 say(sp_order[0] " took " int(sp_tricks[0]) "/" sp_bid(0) ", " \
706 sp_order[2] " took " int(sp_tricks[2]) "/" sp_bid(2))
707 say(sp_order[1] " took " int(sp_tricks[1]) "/" sp_bid(1) ", " \
708 sp_order[3] " took " int(sp_tricks[3]) "/" sp_bid(3))
711 (TO == NICK || DST == sp_channel) &&
712 /^\.(score|status)$/ {
713 if (sp_state == "new") {
714 say("There is no game in progress")
716 if (sp_state ~ "join|bid|pass|play") {
718 sp_playto " points, " \
721 if (sp_state == "join") {
722 say("Waiting for players: " \
723 sp_order[0] " " sp_order[1] " " \
724 sp_order[2] " " sp_order[3])
726 if (sp_state ~ "bid|pass|play") {
727 say(sp_team(0) ": " \
728 int(sp_scores[0]) " points, " \
729 int(sp_bags(0)) " bags")
730 say(sp_team(1) ": " \
731 int(sp_scores[1]) " points, " \
732 int(sp_bags(1)) " bags")
736 /^\.((new|end|load)game|join|look|bid|pass|play)/ {
737 sp_save("var/sp_cur.json");