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)" &&
551 say(_i " can play for " sp_share[_i]);
555 (sp_state == "bid" || sp_state == "play") &&
557 if (sp_from in sp_players)
558 say(".slap " FROM ", it is not your turn.")
560 say(".slap " FROM ", you are not playing.")
566 if ($2 < 0 || $2 > 13) {
567 say("You can only bid from 0 to 13")
571 if ($2 == 0 && !sp_looked[i]) {
572 say(FROM " goes blind nil!")
574 } else if ($2 == 0) {
575 say(FROM " goes nil!")
580 if (sp_turn != sp_dealer) {
581 say("Bidding goes to " sp_player "!")
583 for (p in sp_players)
584 say(p, "You have: " sp_hand(p, p))
586 for (i=0; i<2; i++) {
587 if (sp_nil[(i+0)%4] == 2 || sp_nil[(i+2)%4] == 2 ||
588 sp_nil[(i+1)%4] != 0 || sp_nil[(i+3)%4] != 0) {
589 say(sp_team(i) ": select a card to pass " \
590 "(/msg " NICK " .pass <card>)")
594 if (sp_state == "play")
595 say("Play starts with " sp_player "!")
600 sp_state == "pass" &&
603 _team = sp_from in sp_players ? sp_players[sp_from] % 2 : 0
605 # check validity and pass
606 if (!(sp_from in sp_players)) {
607 say(".slap " FROM ", you are not playing.")
609 else if (sp_nil[_team] == 1 && sp_nil[_team+2] == 1) {
610 reply("Your team did not go blind")
612 else if (sp_pass[sp_players[sp_from]]) {
613 reply("You have already passed a card")
615 else if (!(_card in sp_deck)) {
616 reply("Invalid card")
618 else if (!(_card in sp_hands[sp_from])) {
619 reply("You do not have that card")
622 sp_pass[sp_players[sp_from]] = $2
623 say(sp_channel, FROM " passes a card")
626 # check for end of passing
627 if (((sp_nil[0] == 1 && sp_nil[2] == 1) || (sp_pass[0] && sp_pass[2])) &&
628 ((sp_nil[1] == 1 && sp_nil[3] == 1) || (sp_pass[1] && sp_pass[3]))) {
632 delete sp_hands[sp_order[i]][_card]
633 sp_hands[sp_order[_partner]][_card] = 1
635 say(sp_channel, "Cards have been passed, play starts with " sp_player "!")
636 for (p in sp_players)
637 say(p, "You have: " sp_hand(p, p))
642 sp_state ~ "(bid|pass|play)" &&
644 if (!(sp_from in sp_players)) {
645 say(".slap " FROM ", you are not playing.")
647 sp_looked[sp_players[sp_from]] = 1
648 say(FROM, "You have: " sp_hand(FROM, sp_from))
653 sp_state == "play" &&
656 gsub(/[^A-Za-z0-9]/, "", _card);
657 if (!(_card in sp_deck)) {
658 reply("Invalid card")
660 else if (!(_card in sp_hands[sp_from])) {
661 reply("You do not have that card")
663 else if (sp_suit && _card !~ sp_suit && sp_hasa(sp_from, sp_suit)) {
664 reply("You must follow suit (" sp_suit ")")
666 else if (_card ~ /s/ && length(sp_hands[sp_from]) == 13 && sp_hasa(sp_from, "[^s]$")) {
667 reply("You cannot trump on the first hand")
669 else if (_card ~ /s/ && length(sp_pile) == 0 && sp_hasa(sp_from, "[^s]$") && !sp_broken) {
670 reply("Spades have not been broken")
674 if (sp_state == "play") {
675 if (length(sp_hands[sp_from]))
676 say(FROM, "You have: " sp_hand(FROM, sp_from))
678 say(sp_player ": it is your turn! " \
679 "(" sp_pretty(sp_piles, sp_player) ")")
681 say(sp_player ": it is your turn!")
686 /^\.bids/ && sp_state == "bid" ||
687 /^\.turn/ && sp_state ~ "(bid|pass|play)" {
689 _pile = sp_pretty(sp_piles, FROM)
690 if (sp_state == "bid" && !_bids)
691 say("It is " sp_player "'s bid!")
692 if (sp_state == "bid" && _bids)
693 say("It is " sp_player "'s bid! (" _bids ")")
694 if (sp_state == "play" && !_pile)
695 say("It is " sp_player "'s turn!")
696 if (sp_state == "play" && _pile)
697 say("It is " sp_player "'s turn! (" _pile ")")
698 for (_i=0; sp_state == "pass" && _i<4; _i++)
699 if ((sp_nil[_i%2+0]!=1 || sp_nil[_i%2+2]!=1) && !sp_pass[_i])
700 say("Waiting for " sp_order[_i] " to pass a card!")
703 /^\.bids$/ && sp_state ~ "(pass|play)" {
704 say(sp_order[0] " bid " sp_bid(0) ", " \
705 sp_order[2] " bid " sp_bid(2) ", " \
706 "total: " sp_bids[0] + sp_bids[2])
707 say(sp_order[1] " bid " sp_bid(1) ", " \
708 sp_order[3] " bid " sp_bid(3) ", " \
709 "total: " sp_bids[1] + sp_bids[3])
712 /^\.tricks$/ && sp_state == "play" {
713 say(sp_order[0] " took " int(sp_tricks[0]) "/" sp_bid(0) ", " \
714 sp_order[2] " took " int(sp_tricks[2]) "/" sp_bid(2))
715 say(sp_order[1] " took " int(sp_tricks[1]) "/" sp_bid(1) ", " \
716 sp_order[3] " took " int(sp_tricks[3]) "/" sp_bid(3))
719 (TO == NICK || DST == sp_channel) &&
720 /^\.(score|status)$/ {
721 if (sp_state == "new") {
722 say("There is no game in progress")
724 if (sp_state ~ "join|bid|pass|play") {
726 sp_playto " points, " \
729 if (sp_state == "join") {
730 say("Waiting for players: " \
731 sp_order[0] " " sp_order[1] " " \
732 sp_order[2] " " sp_order[3])
734 if (sp_state ~ "bid|pass|play") {
735 say(sp_team(0) ": " \
736 int(sp_scores[0]) " points, " \
737 int(sp_bags(0)) " bags")
738 say(sp_team(1) ": " \
739 int(sp_scores[1]) " points, " \
740 int(sp_bags(1)) " bags")
744 /^\.((new|end|load)game|join|look|bid|pass|play)/ {
745 sp_save("var/sp_cur.json");