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 /^\.order (\w+) ([0-4])/ {
433 say(sp_channel, FROM " is cheating for " $2)
436 sp_player = sp_order[sp_turn]
440 sp_state == "play" &&
441 /^\.force (\w+) (\S+)$/ {
442 say(sp_channel, FROM " is cheating for " $2)
450 match($0, /^\.newgame ?([0-9]+) *- *([0-9]+)$/, _arr) {
451 if (_arr[2] > _arr[1])
452 $0 = $1 " " int(rand() * (_arr[2]-_arr[1])+_arr[1])
455 /^\.newgame ?([0-9]+)?$/ {
456 if (sp_state != "new") {
457 reply("There is already a game in progress.")
461 sp_playto = $2 ? $2 : 200
462 sp_limit = sp_playto > 200 ? 10 : 5;
465 say(sp_owner " starts a game of Spades to " sp_playto " with " sp_limit " bags!")
469 (sp_from == sp_owner || AUTH == OWNER) &&
471 if (sp_state == "new") {
472 reply("There is no game in progress.")
474 say(FROM " ends the game")
480 if (sp_state == "new") {
481 reply("There is no game in progress")
483 else if (sp_state == "play") {
484 reply("The game has already started")
486 else if (sp_state == "join" && sp_from in sp_players) {
487 reply("You are already playing")
489 else if (sp_state == "join") {
493 sp_auths[AUTH] = FROM
495 say(FROM " joins the game!")
497 if (sp_state == "join" && sp_turn == 0) {
504 _who = $2 in USERS ? USERS[$2]["auth"] : ""
505 _str = _who && _who != $2 ? $2 " (" _who ")" : $2
506 if (sp_state ~ "new|join") {
507 reply("The game has not yet started")
509 else if (!(sp_from in sp_players)) {
510 reply("You are not playing")
513 reply(_str " is not logged in")
515 else if (_who in sp_players || _who in sp_auths) {
516 reply(_str " is a primary player")
518 else if (_who in sp_share) {
519 reply(_str " is already playing for " sp_share[_who])
522 reply(_str " can now play for " sp_from)
523 sp_share[_who] = sp_from
528 _who = $2 in USERS ? USERS[$2]["auth"] : $2
529 _str = _who && _who != $2 ? $2 " (" _who ")" : $2
530 if (sp_state ~ "new|join") {
531 reply("The game has not yet started")
533 else if (!(sp_from in sp_players)) {
534 reply("You are not playing")
536 else if (_who in sp_players || _who in sp_auths) {
537 reply(_str " is a primary player")
539 else if (!(_who in sp_share) || sp_share[_who] != sp_from) {
540 reply(_str " is not playing for " sp_from)
543 reply(_str " can no longer play for " sp_from)
544 delete sp_share[_who]
548 sp_state ~ "(bid|pass|play)" &&
552 _lines[sp_share[_i]] = _lines[sp_share[_i]] " " _i
554 say(_i " allowed:" _lines[_i])
558 (sp_state == "bid" || sp_state == "play") &&
560 if (sp_from in sp_players)
561 say(".slap " FROM ", it is not your turn.")
563 say(".slap " FROM ", you are not playing.")
569 if ($2 < 0 || $2 > 13) {
570 say("You can only bid from 0 to 13")
574 if ($2 == 0 && !sp_looked[i]) {
575 say(FROM " goes blind nil!")
577 } else if ($2 == 0) {
578 say(FROM " goes nil!")
583 if (sp_turn != sp_dealer) {
584 say("Bidding goes to " sp_player "!")
586 for (p in sp_players)
587 say(p, "You have: " sp_hand(p, p))
589 for (i=0; i<2; i++) {
590 if (sp_nil[(i+0)%4] == 2 || sp_nil[(i+2)%4] == 2 ||
591 sp_nil[(i+1)%4] != 0 || sp_nil[(i+3)%4] != 0) {
592 say(sp_team(i) ": select a card to pass " \
593 "(/msg " NICK " .pass <card>)")
597 if (sp_state == "play")
598 say("Play starts with " sp_player "!")
603 sp_state == "pass" &&
606 _team = sp_from in sp_players ? sp_players[sp_from] % 2 : 0
608 # check validity and pass
609 if (!(sp_from in sp_players)) {
610 say(".slap " FROM ", you are not playing.")
612 else if (sp_nil[_team] == 1 && sp_nil[_team+2] == 1) {
613 reply("Your team did not go blind")
615 else if (sp_pass[sp_players[sp_from]]) {
616 reply("You have already passed a card")
618 else if (!(_card in sp_deck)) {
619 reply("Invalid card")
621 else if (!(_card in sp_hands[sp_from])) {
622 reply("You do not have that card")
625 sp_pass[sp_players[sp_from]] = $2
626 say(sp_channel, FROM " passes a card")
629 # check for end of passing
630 if (((sp_nil[0] == 1 && sp_nil[2] == 1) || (sp_pass[0] && sp_pass[2])) &&
631 ((sp_nil[1] == 1 && sp_nil[3] == 1) || (sp_pass[1] && sp_pass[3]))) {
635 delete sp_hands[sp_order[i]][_card]
636 sp_hands[sp_order[_partner]][_card] = 1
638 say(sp_channel, "Cards have been passed, play starts with " sp_player "!")
639 for (p in sp_players)
640 say(p, "You have: " sp_hand(p, p))
645 sp_state ~ "(bid|pass|play)" &&
647 if (!(sp_from in sp_players)) {
648 say(".slap " FROM ", you are not playing.")
650 sp_looked[sp_players[sp_from]] = 1
651 say(FROM, "You have: " sp_hand(FROM, sp_from))
656 sp_state == "play" &&
659 gsub(/[^A-Za-z0-9]/, "", _card);
660 if (!(_card in sp_deck)) {
661 reply("Invalid card")
663 else if (!(_card in sp_hands[sp_from])) {
664 reply("You do not have that card")
666 else if (sp_suit && _card !~ sp_suit && sp_hasa(sp_from, sp_suit)) {
667 reply("You must follow suit (" sp_suit ")")
669 else if (_card ~ /s/ && length(sp_hands[sp_from]) == 13 && sp_hasa(sp_from, "[^s]$")) {
670 reply("You cannot trump on the first hand")
672 else if (_card ~ /s/ && length(sp_pile) == 0 && sp_hasa(sp_from, "[^s]$") && !sp_broken) {
673 reply("Spades have not been broken")
677 if (sp_state == "play") {
678 if (length(sp_hands[sp_from]))
679 say(FROM, "You have: " sp_hand(FROM, sp_from))
681 say(sp_player ": it is your turn! " \
682 "(" sp_pretty(sp_piles, sp_player) ")")
684 say(sp_player ": it is your turn!")
689 /^\.bids/ && sp_state == "bid" ||
690 /^\.turn/ && sp_state ~ "(bid|pass|play)" {
692 _pile = sp_pretty(sp_piles, FROM)
693 if (sp_state == "bid" && !_bids)
694 say("It is " sp_player "'s bid!")
695 if (sp_state == "bid" && _bids)
696 say("It is " sp_player "'s bid! (" _bids ")")
697 if (sp_state == "play" && !_pile)
698 say("It is " sp_player "'s turn!")
699 if (sp_state == "play" && _pile)
700 say("It is " sp_player "'s turn! (" _pile ")")
701 for (_i=0; sp_state == "pass" && _i<4; _i++)
702 if ((sp_nil[_i%2+0]!=1 || sp_nil[_i%2+2]!=1) && !sp_pass[_i])
703 say("Waiting for " sp_order[_i] " to pass a card!")
706 /^\.bids$/ && sp_state ~ "(pass|play)" {
707 say(sp_order[0] " bid " sp_bid(0) ", " \
708 sp_order[2] " bid " sp_bid(2) ", " \
709 "total: " sp_bids[0] + sp_bids[2])
710 say(sp_order[1] " bid " sp_bid(1) ", " \
711 sp_order[3] " bid " sp_bid(3) ", " \
712 "total: " sp_bids[1] + sp_bids[3])
715 /^\.tricks$/ && sp_state == "play" {
716 say(sp_order[0] " took " int(sp_tricks[0]) "/" sp_bid(0) ", " \
717 sp_order[2] " took " int(sp_tricks[2]) "/" sp_bid(2))
718 say(sp_order[1] " took " int(sp_tricks[1]) "/" sp_bid(1) ", " \
719 sp_order[3] " took " int(sp_tricks[3]) "/" sp_bid(3))
722 (TO == NICK || DST == sp_channel) &&
723 /^\.(score|status)$/ {
724 if (sp_state == "new") {
725 say("There is no game in progress")
727 if (sp_state ~ "join|bid|pass|play") {
729 sp_playto " points, " \
732 if (sp_state == "join") {
733 say("Waiting for players: " \
734 sp_order[0] " " sp_order[1] " " \
735 sp_order[2] " " sp_order[3])
737 if (sp_state ~ "bid|pass|play") {
738 say(sp_team(0) ": " \
739 int(sp_scores[0]) " points, " \
740 int(sp_bags(0)) " bags")
741 say(sp_team(1) ": " \
742 int(sp_scores[1]) " points, " \
743 int(sp_bags(1)) " bags")
747 /^\.((new|end|load)game|join|look|bid|pass|play)/ {
748 sp_save("var/sp_cur.json");