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)
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_passer(who)
253 return sp_nil[(who+0)%4] == 2 || sp_nil[(who+1)%4] != 0 ||
254 sp_nil[(who+2)%4] == 2 || sp_nil[(who+3)%4] != 0
257 function sp_bidders( i, turn, bid, bids)
259 for (i = 0; i < 4; i++) {
260 turn = (sp_dealer + i) % 4
261 if (bid = sp_bid(turn))
262 bids = bids " " sp_order[turn] ":" bid
264 gsub(/^ +| +$/, "", bids)
268 function sp_score( bids, times, tricks)
270 for (i=0; i<2; i++) {
271 bids = sp_bids[i] + sp_bids[i+2]
272 tricks = sp_tricks[i] + sp_tricks[i+2]
274 times = int((sp_bags(i) + bags) / sp_limit)
276 say(sp_team(i) " bag" (times>1?" way ":" ") "out")
277 sp_scores[i] -= sp_limit * 10 * times;
279 if (tricks >= bids) {
280 say(sp_team(i) " make their bid: " tricks "/" bids)
281 sp_scores[i] += bids*10 + bags;
283 say(sp_team(i) " go bust: " tricks "/" bids)
284 sp_scores[i] -= bids*10;
287 for (i=0; i<4; i++) {
290 say(sp_order[i] " " \
291 (sp_nil[i] == 1 && !sp_tricks[i] ? "makes nil!" :
292 sp_nil[i] == 1 && sp_tricks[i] ? "fails at nil!" :
293 sp_nil[i] == 2 && !sp_tricks[i] ? "makes blind nil!" :
294 sp_nil[i] == 2 && sp_tricks[i] ? "fails miserably at blind nil!" :
296 sp_scores[i%2] += sp_limit * 10 * sp_nil[i] * \
297 (sp_tricks[i] == 0 ? 1 : -1)
301 function sp_play(card, winner, pi)
303 delete sp_hands[sp_from][card]
304 sp_pile[card] = sp_player
305 sp_piles = sp_piles (sp_piles?",":"") card
312 if (length(sp_pile) == 1)
313 sp_suit = sp_type(card)
316 if (length(sp_pile) == 4) {
318 pi = sp_players[sp_pile[winner]]
320 say(sp_pile[winner] " wins with " sp_pretty(winner, FROM) \
321 " (" sp_pretty(sp_piles, FROM) ")")
322 sp_next(sp_pile[winner])
327 if (sp_tricks[0] + sp_tricks[1] + \
328 sp_tricks[2] + sp_tricks[3] == 13) {
331 if (sp_scores[0] >= sp_playto || sp_scores[1] >= sp_playto &&
332 sp_scores[0] != sp_scores[1]) {
334 winner = sp_scores[0] > sp_scores[1] ? 0 : 1
336 say(CHANNEL, sp_team(winner) " wins the game " \
337 sp_scores[winner] " to " sp_scores[looser])
338 say(CHANNEL, sp_order[winner+0] "++")
339 say(CHANNEL, sp_order[winner+2] "++")
343 if (sp_scores[0] == sp_scores[1] &&
344 sp_scores[0] >= sp_playto)
345 say("It's tie! Playing an extra round!");
354 cmd = "od -An -N4 -td4 /dev/random"
360 sp_load("var/sp_cur.json");
362 # say(sp_channel, "Game restored.")
366 sp_from = AUTH in sp_auths ? sp_auths[AUTH] : \
367 AUTH in sp_share ? sp_share[AUTH] : FROM
368 sp_valid = sp_from && sp_from == sp_player
374 say("Spades! " sp_pretty("As,Ah,Ad,Ac", FROM))
379 sp_save("var/sp_save.json");
385 sp_load("var/sp_save.json");
390 /^\.help [Ss]pades$/ {
391 say("Spades -- play a game of spades")
392 say(".help game -- setup and administer the game")
393 say(".help play -- commands for playing spades")
394 say(".help auth -- control player authorization")
399 say(".newgame [score] -- start a game to <score> points, default 500")
400 say(".endgame -- abort the current game")
401 say(".savegame -- save the current game to disk")
402 say(".loadgame -- load the previously saved game")
407 say(".join -- join the current game")
408 say(".look -- look at your cards")
409 say(".bid [n] -- bid for <n> tricks")
410 say(".pass [card] -- pass a card to your partner")
411 say(".play [card] -- play a card")
412 say(".turn -- check whose turn it is")
413 say(".bids -- check what everyone bid")
414 say(".tricks -- check how many trick have been taken")
415 say(".score -- check the score")
420 say(".auth [who] -- display authentication info for a user")
421 say(".allow [who] -- allow another person to play on your behalf")
422 say(".deny [who] -- prevent a previously allowed user from playing")
423 say(".show -- display which users can play for which players")
429 /^\.deal (\w+) (.*)/ {
430 say(sp_channel, FROM " is cheating for " $2)
432 for (i=3; i<=NF; i++)
438 /^\.order (\w+) ([0-4])/ {
439 say(sp_channel, FROM " is cheating for " $2)
442 sp_player = sp_order[sp_turn]
446 sp_state == "play" &&
447 /^\.force (\w+) (\S+)$/ {
448 say(sp_channel, FROM " is cheating for " $2)
456 match($0, /^\.newgame ?([0-9]+) *- *([0-9]+)$/, _arr) {
457 if (_arr[2] > _arr[1])
458 $0 = $1 " " int(rand() * (_arr[2]-_arr[1])+_arr[1])
461 /^\.newgame ?([0-9]+)?$/ {
462 if (sp_state != "new") {
463 reply("There is already a game in progress.")
467 sp_playto = $2 ? $2 : 200
468 sp_limit = sp_playto > 200 ? 10 : 5;
471 say(sp_owner " starts a game of Spades to " sp_playto " with " sp_limit " bags!")
475 (sp_from == sp_owner || AUTH == OWNER) &&
477 if (sp_state == "new") {
478 reply("There is no game in progress.")
480 say(FROM " ends the game")
486 if (sp_state == "new") {
487 reply("There is no game in progress")
489 else if (sp_state == "play") {
490 reply("The game has already started")
492 else if (sp_state == "join" && sp_from in sp_players) {
493 reply("You are already playing")
495 else if (sp_state == "join") {
499 sp_auths[AUTH] = FROM
501 say(FROM " joins the game!")
503 if (sp_state == "join" && sp_turn == 0) {
510 _who = $2 in USERS ? USERS[$2]["auth"] : ""
511 _str = _who && _who != $2 ? $2 " (" _who ")" : $2
512 if (sp_state ~ "new|join") {
513 reply("The game has not yet started")
515 else if (!(sp_from in sp_players)) {
516 reply("You are not playing")
519 reply(_str " is not logged in")
521 else if (_who in sp_players || _who in sp_auths) {
522 reply(_str " is a primary player")
524 else if (_who in sp_share) {
525 reply(_str " is already playing for " sp_share[_who])
528 reply(_str " can now play for " sp_from)
529 sp_share[_who] = sp_from
534 _who = $2 in USERS ? USERS[$2]["auth"] : $2
535 _str = _who && _who != $2 ? $2 " (" _who ")" : $2
536 if (sp_state ~ "new|join") {
537 reply("The game has not yet started")
539 else if (!(sp_from in sp_players)) {
540 reply("You are not playing")
542 else if (_who in sp_players || _who in sp_auths) {
543 reply(_str " is a primary player")
545 else if (!(_who in sp_share) || sp_share[_who] != sp_from) {
546 reply(_str " is not playing for " sp_from)
549 reply(_str " can no longer play for " sp_from)
550 delete sp_share[_who]
554 sp_state ~ "(bid|pass|play)" &&
558 _lines[sp_share[_i]] = _lines[sp_share[_i]] " " _i
560 say(_i " allowed:" _lines[_i])
564 (sp_state == "bid" || sp_state == "play") &&
566 if (sp_from in sp_players)
567 say(".slap " FROM ", it is not your turn.")
569 say(".slap " FROM ", you are not playing.")
575 if ($2 < 0 || $2 > 13) {
576 say("You can only bid from 0 to 13")
580 if ($2 == 0 && !sp_looked[i]) {
581 say(FROM " goes blind nil!")
583 } else if ($2 == 0) {
584 say(FROM " goes nil!")
589 if (sp_turn != sp_dealer) {
590 say("Bidding goes to " sp_player "!")
592 for (p in sp_players)
593 say(p, "You have: " sp_hand(p, p))
595 for (i=0; i<2; i++) {
597 say(sp_team(i) ": select a card to pass " \
598 "(/msg " NICK " .pass <card>)")
602 if (sp_state == "play")
603 say("Play starts with " sp_player "!")
608 sp_state == "pass" &&
611 _team = sp_from in sp_players ? sp_players[sp_from] % 2 : 0
613 # check validity and pass
614 if (!(sp_from in sp_players)) {
615 say(".slap " FROM ", you are not playing.")
617 else if (!sp_passer(_team)) {
618 reply("Your team did not go blind")
620 else if (sp_pass[sp_players[sp_from]]) {
621 reply("You have already passed a card")
623 else if (!(_card in sp_deck)) {
624 reply("Invalid card")
626 else if (!(_card in sp_hands[sp_from])) {
627 reply("You do not have that card")
630 sp_pass[sp_players[sp_from]] = $2
631 say(sp_channel, FROM " passes a card")
634 # check for end of passing
635 if ((!sp_passer(0) || (sp_pass[0] && sp_pass[2])) &&
636 (!sp_passer(1) || (sp_pass[1] && sp_pass[3]))) {
640 delete sp_hands[sp_order[i]][_card]
641 sp_hands[sp_order[_partner]][_card] = 1
643 say(sp_channel, "Cards have been passed, play starts with " sp_player "!")
644 for (p in sp_players)
645 say(p, "You have: " sp_hand(p, p))
650 sp_state ~ "(bid|pass|play)" &&
652 if (!(sp_from in sp_players)) {
653 say(".slap " FROM ", you are not playing.")
655 sp_looked[sp_players[sp_from]] = 1
656 say(FROM, "You have: " sp_hand(FROM, sp_from))
661 sp_state == "play" &&
664 gsub(/[^A-Za-z0-9]/, "", _card);
665 if (!(_card in sp_deck)) {
666 reply("Invalid card")
668 else if (!(_card in sp_hands[sp_from])) {
669 reply("You do not have that card")
671 else if (sp_suit && _card !~ sp_suit && sp_hasa(sp_from, sp_suit)) {
672 reply("You must follow suit (" sp_suit ")")
674 else if (_card ~ /s/ && length(sp_hands[sp_from]) == 13 && sp_hasa(sp_from, "[^s]$")) {
675 reply("You cannot trump on the first hand")
677 else if (_card ~ /s/ && length(sp_pile) == 0 && sp_hasa(sp_from, "[^s]$") && !sp_broken) {
678 reply("Spades have not been broken")
682 if (sp_state == "play") {
683 if (length(sp_hands[sp_from]))
684 say(FROM, "You have: " sp_hand(FROM, sp_from))
686 say(sp_player ": it is your turn! " \
687 "(" sp_pretty(sp_piles, sp_player) ")")
689 say(sp_player ": it is your turn!")
694 /^\.bids/ && sp_state == "bid" ||
695 /^\.turn/ && sp_state ~ "(bid|pass|play)" {
697 _pile = sp_pretty(sp_piles, FROM)
698 if (sp_state == "bid" && !_bids)
699 say("It is " sp_player "'s bid!")
700 if (sp_state == "bid" && _bids)
701 say("It is " sp_player "'s bid! (" _bids ")")
702 if (sp_state == "play" && !_pile)
703 say("It is " sp_player "'s turn!")
704 if (sp_state == "play" && _pile)
705 say("It is " sp_player "'s turn! (" _pile ")")
706 for (_i=0; sp_state == "pass" && _i<4; _i++)
707 if (sp_passer(_i) && !sp_pass[_i])
708 say("Waiting for " sp_order[_i] " to pass a card!")
711 /^\.bids$/ && sp_state ~ "(pass|play)" {
712 say(sp_order[0] " bid " sp_bid(0) ", " \
713 sp_order[2] " bid " sp_bid(2) ", " \
714 "total: " sp_bids[0] + sp_bids[2])
715 say(sp_order[1] " bid " sp_bid(1) ", " \
716 sp_order[3] " bid " sp_bid(3) ", " \
717 "total: " sp_bids[1] + sp_bids[3])
720 /^\.tricks$/ && sp_state == "play" {
721 say(sp_order[0] " took " int(sp_tricks[0]) "/" sp_bid(0) ", " \
722 sp_order[2] " took " int(sp_tricks[2]) "/" sp_bid(2))
723 say(sp_order[1] " took " int(sp_tricks[1]) "/" sp_bid(1) ", " \
724 sp_order[3] " took " int(sp_tricks[3]) "/" sp_bid(3))
727 (TO == NICK || DST == sp_channel) &&
728 /^\.(score|status)$/ {
729 if (sp_state == "new") {
730 say("There is no game in progress")
732 if (sp_state ~ "join|bid|pass|play") {
734 sp_playto " points, " \
737 if (sp_state == "join") {
738 say("Waiting for players: " \
739 sp_order[0] " " sp_order[1] " " \
740 sp_order[2] " " sp_order[3])
742 if (sp_state ~ "bid|pass|play") {
743 say(sp_team(0) ": " \
744 int(sp_scores[0]) " points, " \
745 int(sp_bags(0)) " bags")
746 say(sp_team(1) ": " \
747 int(sp_scores[1]) " points, " \
748 int(sp_bags(1)) " bags")
752 /^\.((new|end|load)game|join|look|bid|pass|play)/ {
753 sp_save("var/sp_cur.json");