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] == 2 || sp_nil[i+2] == 2) {
580 say(sp_team(i) ": select a card to pass " \
581 "(/msg " NICK " .pass <card>)")
585 if (sp_state == "play")
586 say("Play starts with " sp_player "!")
591 sp_state == "pass" &&
594 _team = sp_from in sp_players ? sp_players[sp_from] % 2 : 0
596 # check validity and pass
597 if (!(sp_from in sp_players)) {
598 say(".slap " FROM ", you are not playing.")
600 else if (sp_nil[_team] != 2 && sp_nil[_team+2] != 2) {
601 reply("Your team did not go blind")
603 else if (sp_pass[sp_players[sp_from]]) {
604 reply("You have already passed a card")
606 else if (!(_card in sp_deck)) {
607 reply("Invalid card")
609 else if (!(_card in sp_hands[sp_from])) {
610 reply("You do not have that card")
613 sp_pass[sp_players[sp_from]] = $2
614 say(sp_channel, FROM " passes a card")
617 # check for end of passing
618 if (((sp_nil[0] != 2 && sp_nil[2] != 2) || (sp_pass[0] && sp_pass[2])) &&
619 ((sp_nil[1] != 2 && sp_nil[3] != 2) || (sp_pass[1] && sp_pass[3]))) {
623 delete sp_hands[sp_order[i]][_card]
624 sp_hands[sp_order[_partner]][_card] = 1
626 say(sp_channel, "Cards have been passed, play starts with " sp_player "!")
627 for (p in sp_players)
628 say(p, "You have: " sp_hand(p, p))
633 sp_state ~ "(bid|pass|play)" &&
635 if (!(sp_from in sp_players)) {
636 say(".slap " FROM ", you are not playing.")
638 sp_looked[sp_players[sp_from]] = 1
639 say(FROM, "You have: " sp_hand(FROM, sp_from))
644 sp_state == "play" &&
647 gsub(/[^A-Za-z0-9]/, "", _card);
648 if (!(_card in sp_deck)) {
649 reply("Invalid card")
651 else if (!(_card in sp_hands[sp_from])) {
652 reply("You do not have that card")
654 else if (sp_suit && _card !~ sp_suit && sp_hasa(sp_from, sp_suit)) {
655 reply("You must follow suit (" sp_suit ")")
657 else if (_card ~ /s/ && length(sp_hands[sp_from]) == 13 && sp_hasa(sp_from, "[^s]$")) {
658 reply("You cannot trump on the first hand")
660 else if (_card ~ /s/ && length(sp_pile) == 0 && sp_hasa(sp_from, "[^s]$") && !sp_broken) {
661 reply("Spades have not been broken")
665 if (sp_state == "play") {
666 if (length(sp_hands[sp_from]))
667 say(FROM, "You have: " sp_hand(FROM, sp_from))
669 say(sp_player ": it is your turn! " \
670 "(" sp_pretty(sp_piles, sp_player) ")")
672 say(sp_player ": it is your turn!")
677 /^\.bids/ && sp_state == "bid" ||
678 /^\.turn/ && sp_state ~ "(bid|pass|play)" {
680 _pile = sp_pretty(sp_piles, FROM)
681 if (sp_state == "bid" && !_bids)
682 say("It is " sp_player "'s bid!")
683 if (sp_state == "bid" && _bids)
684 say("It is " sp_player "'s bid! (" _bids ")")
685 if (sp_state == "play" && !_pile)
686 say("It is " sp_player "'s turn!")
687 if (sp_state == "play" && _pile)
688 say("It is " sp_player "'s turn! (" _pile ")")
689 for (_i=0; sp_state == "pass" && _i<4; _i++)
690 if ((sp_nil[_i%2+0]==2 || sp_nil[_i%2+2]==2) && !sp_pass[_i])
691 say("Waiting for " sp_order[_i] " to pass a card!")
694 /^\.bids$/ && sp_state ~ "(pass|play)" {
695 say(sp_order[0] " bid " sp_bid(0) ", " \
696 sp_order[2] " bid " sp_bid(2) ", " \
697 "total: " sp_bids[0] + sp_bids[2])
698 say(sp_order[1] " bid " sp_bid(1) ", " \
699 sp_order[3] " bid " sp_bid(3) ", " \
700 "total: " sp_bids[1] + sp_bids[3])
703 /^\.tricks$/ && sp_state == "play" {
704 say(sp_order[0] " took " int(sp_tricks[0]) "/" sp_bid(0) ", " \
705 sp_order[2] " took " int(sp_tricks[2]) "/" sp_bid(2))
706 say(sp_order[1] " took " int(sp_tricks[1]) "/" sp_bid(1) ", " \
707 sp_order[3] " took " int(sp_tricks[3]) "/" sp_bid(3))
710 (TO == NICK || DST == sp_channel) &&
711 /^\.(score|status)$/ {
712 if (sp_state == "new") {
713 say("There is no game in progress")
715 if (sp_state ~ "join|bid|pass|play") {
717 sp_playto " points, " \
720 if (sp_state == "join") {
721 say("Waiting for players: " \
722 sp_order[0] " " sp_order[1] " " \
723 sp_order[2] " " sp_order[3])
725 if (sp_state ~ "bid|pass|play") {
726 say(sp_team(0) ": " \
727 int(sp_scores[0]) " points, " \
728 int(sp_bags(0)) " bags")
729 say(sp_team(1) ": " \
730 int(sp_scores[1]) " points, " \
731 int(sp_bags(1)) " bags")
735 /^\.((new|end|load)game|join|look|bid|pass|play)/ {
736 sp_save("var/sp_cur.json");