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)
67 json_copy(dst, key, src[key])
71 function sp_save(file, game)
74 game["suit"] = sp_suit;
75 game["piles"] = sp_piles;
76 json_copy(game, "pile", sp_pile);
79 game["state"] = sp_state;
80 game["broken"] = sp_broken;
81 json_copy(game, "looked", sp_looked);
82 json_copy(game, "bids", sp_bids);
83 json_copy(game, "nil", sp_nil);
84 json_copy(game, "pass", sp_pass);
85 json_copy(game, "tricks", sp_tricks);
88 game["channel"] = sp_channel;
89 game["owner"] = sp_owner;
90 game["playto"] = sp_playto;
91 game["dealer"] = sp_dealer;
92 game["turn"] = sp_turn;
93 game["player"] = sp_player;
94 game["limit"] = sp_limit;
95 json_copy(game, "hands", sp_hands);
96 json_copy(game, "players", sp_players);
97 json_copy(game, "auths", sp_auths);
98 json_copy(game, "share", sp_share);
99 json_copy(game, "order", sp_order);
100 json_copy(game, "scores", sp_scores);
103 json_save(file, game);
106 function sp_load(file, game)
109 if (!json_load(file, game))
113 sp_suit = game["suit"];
114 sp_piles = game["piles"];
115 sp_acopy(sp_pile, game["pile"]);
118 sp_state = game["state"];
119 sp_broken = game["broken"];
120 sp_acopy(sp_looked, game["looked"]);
121 sp_acopy(sp_bids, game["bids"]);
122 sp_acopy(sp_nil, game["nil"]);
123 sp_acopy(sp_pass, game["pass"]);
124 sp_acopy(sp_tricks, game["tricks"]);
127 sp_channel = game["channel"];
128 sp_owner = game["owner"];
129 sp_playto = game["playto"];
130 sp_dealer = game["dealer"];
131 sp_turn = game["turn"];
132 sp_player = game["player"];
133 sp_limit = game["limit"];
134 sp_acopy(sp_hands, game["hands"]);
135 sp_acopy(sp_players, game["players"]);
136 sp_acopy(sp_auths, game["auths"]);
137 sp_acopy(sp_share, game["share"]);
138 sp_acopy(sp_order, game["order"]);
139 sp_acopy(sp_scores, game["scores"]);
142 function sp_pretty(cards, who)
145 gsub(/[0-9JQKA]*[sc]/, "\0031,00\002&\017", cards) # black
146 gsub(/[0-9JQKA]*[hd]/, "\0034,00\002&\017", cards) # red
148 if (!nounicode[who]) {
149 gsub(/s/, "\002♠", cards)
150 gsub(/h/, "\002♥", cards)
151 gsub(/d/, "\002♦", cards)
152 gsub(/c/, "\002♣", cards)
157 function sp_next(who, prev)
160 sp_turn = who ? sp_players[who] : (sp_turn + 1) % 4
161 if (length(sp_order) == 4)
162 sp_player = sp_order[sp_turn]
166 function sp_shuf(i, mixed)
168 sp_usort(sp_players, mixed)
170 sp_order[i-1] = mixed[i]
171 sp_players[mixed[i]] = i-1
175 function sp_deal( shuf)
177 say("/me deals the cards")
178 sp_usort(sp_deck, shuf)
179 for (i=1; i<=52; i++)
180 sp_hands[sp_order[i%4]][shuf[i]] = 1
182 sp_dealer = (sp_dealer+1)%4
184 sp_player = sp_order[sp_turn]
185 say(sp_player ": you bid first!")
188 function sp_hand(to, who, sort, str)
190 asorti(sp_hands[who], sort, "sp_csort")
191 for (i=0; i<length(sort); i++)
192 str = str "" sprintf("%4s", sort[i])
193 gsub(/^ +| +$/, "", str)
194 return sp_pretty(str, to)
197 function sp_hasa(who, expr)
199 for (c in sp_hands[who]) {
206 function sp_type(card)
208 return substr(card, length(card))
211 function sp_usort(list, out) {
214 asorti(out, out, "@val_num_asc")
217 function sp_csort(i1,v1,i2,v2) {
218 return sp_deck[i1] > sp_deck[i2] ? +1 :
219 sp_deck[i1] < sp_deck[i2] ? -1 : 0;
222 function sp_winner( card, tmp)
224 for (card in sp_pile)
225 if (card !~ sp_suit && card !~ /s/)
227 asorti(sp_pile, tmp, "sp_csort")
228 #print "pile: " tmp[1] ">" tmp[2] ">" tmp[3] ">" tmp[4]
234 #return "{" sp_order[i+0] "," sp_order[i+2] "}"
235 return sp_order[i+0] "/" sp_order[i+2]
238 function sp_bags(i, bags)
240 bags = sp_scores[i] % sp_limit
248 return sp_nil[who] == 0 ? sp_bids[who] :
249 sp_nil[who] == 1 ? "nil" :
250 sp_nil[who] == 2 ? "blind" : "n/a"
253 function sp_passer(who)
255 return sp_nil[(who+0)%4] == 2 || sp_nil[(who+1)%4] != 0 ||
256 sp_nil[(who+2)%4] == 2 || sp_nil[(who+3)%4] != 0
259 function sp_bidders( i, turn, bid, bids)
261 for (i = 0; i < 4; i++) {
262 turn = (sp_dealer + i) % 4
263 if (bid = sp_bid(turn))
264 bids = bids " " sp_order[turn] ":" bid
266 gsub(/^ +| +$/, "", bids)
270 function sp_score( bids, times, tricks)
272 for (i=0; i<2; i++) {
273 bids = sp_bids[i] + sp_bids[i+2]
274 tricks = sp_tricks[i] + sp_tricks[i+2]
276 times = int((sp_bags(i) + bags) / sp_limit)
278 say(sp_team(i) " bag" (times>1?" way ":" ") "out")
279 sp_scores[i] -= sp_limit * 10 * times;
281 if (tricks >= bids) {
282 say(sp_team(i) " make their bid: " tricks "/" bids)
283 sp_scores[i] += bids*10 + bags;
285 say(sp_team(i) " go bust: " tricks "/" bids)
286 sp_scores[i] -= bids*10;
289 for (i=0; i<4; i++) {
292 say(sp_order[i] " " \
293 (sp_nil[i] == 1 && !sp_tricks[i] ? "makes nil!" :
294 sp_nil[i] == 1 && sp_tricks[i] ? "fails at nil!" :
295 sp_nil[i] == 2 && !sp_tricks[i] ? "makes blind nil!" :
296 sp_nil[i] == 2 && sp_tricks[i] ? "fails miserably at blind nil!" :
298 sp_scores[i%2] += sp_limit * 10 * sp_nil[i] * \
299 (sp_tricks[i] == 0 ? 1 : -1)
301 if (sp_scores[0] > sp_scores[1])
302 say(sp_team(0) " lead " sp_scores[0] " to " sp_scores[1] " of " sp_playto)
303 else if (sp_scores[1] > sp_scores[0])
304 say(sp_team(1) " lead " sp_scores[1] " to " sp_scores[0] " of " sp_playto)
306 say("tied at " sp_scores[0])
309 function sp_play(card, winner, pi)
311 delete sp_hands[sp_from][card]
312 sp_pile[card] = sp_player
313 sp_piles = sp_piles (sp_piles?",":"") card
320 if (length(sp_pile) == 1)
321 sp_suit = sp_type(card)
324 if (length(sp_pile) == 4) {
326 pi = sp_players[sp_pile[winner]]
328 say(sp_pile[winner] " wins with " sp_pretty(winner, FROM) \
329 " (" sp_pretty(sp_piles, FROM) ")")
330 sp_next(sp_pile[winner])
335 if (sp_tricks[0] + sp_tricks[1] + \
336 sp_tricks[2] + sp_tricks[3] == 13) {
339 if (sp_scores[0] >= sp_playto || sp_scores[1] >= sp_playto &&
340 sp_scores[0] != sp_scores[1]) {
342 winner = sp_scores[0] > sp_scores[1] ? 0 : 1
344 say(CHANNEL, sp_team(winner) " wins the game " \
345 sp_scores[winner] " to " sp_scores[looser])
346 say(CHANNEL, sp_order[winner+0] "++")
347 say(CHANNEL, sp_order[winner+2] "++")
351 if (sp_scores[0] == sp_scores[1] &&
352 sp_scores[0] >= sp_playto)
353 say("It's tie! Playing an extra round!");
362 cmd = "od -An -N4 -td4 /dev/random"
368 sp_load("var/sp_cur.json");
370 # say(sp_channel, "Game restored.")
374 sp_from = AUTH in sp_auths ? sp_auths[AUTH] : \
375 AUTH in sp_share ? sp_share[AUTH] : FROM
376 sp_valid = sp_from && sp_from == sp_player
382 say("Spades! " sp_pretty("As,Ah,Ad,Ac", FROM))
387 sp_save("var/sp_save.json");
393 sp_load("var/sp_save.json");
398 /^\.help [Ss]pades$/ {
399 say("Spades -- play a game of spades")
400 say(".help game -- setup and administer the game")
401 say(".help play -- commands for playing spades")
402 say(".help auth -- control player authorization")
407 say(".newgame [score] -- start a game to <score> points, default 500")
408 say(".endgame -- abort the current game")
409 say(".savegame -- save the current game to disk")
410 say(".loadgame -- load the previously saved game")
415 say(".join -- join the current game")
416 say(".look -- look at your cards")
417 say(".bid [n] -- bid for <n> tricks")
418 say(".pass [card] -- pass a card to your partner")
419 say(".play [card] -- play a card")
420 say(".turn -- check whose turn it is")
421 say(".bids -- check what everyone bid")
422 say(".tricks -- check how many trick have been taken")
423 say(".score -- check the score")
428 say(".auth [who] -- display authentication info for a user")
429 say(".allow [who] -- allow another person to play on your behalf")
430 say(".deny [who] -- prevent a previously allowed user from playing")
431 say(".show -- display which users can play for which players")
437 /^\.deal (\w+) (.*)/ {
438 say(sp_channel, FROM " is cheating for " $2)
440 for (i=3; i<=NF; i++)
446 /^\.order (\w+) ([0-4])/ {
447 say(sp_channel, FROM " is cheating for " $2)
450 sp_player = sp_order[sp_turn]
454 sp_state == "play" &&
455 /^\.force (\w+) (\S+)$/ {
456 say(sp_channel, FROM " is cheating for " $2)
464 match($0, /^\.newgame ?([1-9][0-9]*) *- *([1-9][0-9]*)$/, _arr) {
465 if (_arr[2] > _arr[1])
466 $0 = $1 " " int(rand() * (_arr[2]-_arr[1])+_arr[1])
469 /^\.newgame ?([1-9][0-9]*)?$/ {
470 if (sp_state != "new") {
471 reply("There is already a game in progress.")
475 sp_playto = $2 ? $2 : 200
476 sp_limit = sp_playto > 200 ? 10 : 5;
479 say(sp_owner " starts a game of Spades to " sp_playto " with " sp_limit " bags!")
483 (sp_from == sp_owner || AUTH == OWNER) &&
485 if (sp_state == "new") {
486 reply("There is no game in progress.")
488 say(FROM " ends the game")
494 if (sp_state == "new") {
495 reply("There is no game in progress")
497 else if (sp_state == "play") {
498 reply("The game has already started")
500 else if (sp_state == "join" && sp_from in sp_players) {
501 reply("You are already playing")
503 else if (sp_state == "join") {
507 sp_auths[AUTH] = FROM
509 say(FROM " joins the game!")
511 if (sp_state == "join" && sp_turn == 0) {
518 _who = $2 in USERS ? USERS[$2]["auth"] : ""
519 _str = _who && _who != $2 ? $2 " (" _who ")" : $2
520 if (sp_state ~ "new|join") {
521 reply("The game has not yet started")
523 else if (!(sp_from in sp_players)) {
524 reply("You are not playing")
527 reply(_str " is not logged in")
529 else if (_who in sp_players || _who in sp_auths) {
530 reply(_str " is a primary player")
532 else if (_who in sp_share) {
533 reply(_str " is already playing for " sp_share[_who])
536 reply(_str " can now play for " sp_from)
537 sp_share[_who] = sp_from
542 _who = $2 in USERS ? USERS[$2]["auth"] : $2
543 _str = _who && _who != $2 ? $2 " (" _who ")" : $2
544 if (sp_state ~ "new|join") {
545 reply("The game has not yet started")
547 else if (!(sp_from in sp_players)) {
548 reply("You are not playing")
550 else if (_who in sp_players || _who in sp_auths) {
551 reply(_str " is a primary player")
553 else if (!(_who in sp_share) || sp_share[_who] != sp_from) {
554 reply(_str " is not playing for " sp_from)
557 reply(_str " can no longer play for " sp_from)
558 delete sp_share[_who]
562 sp_state ~ "(bid|pass|play)" &&
566 _lines[sp_share[_i]] = _lines[sp_share[_i]] " " _i
568 say(_i " allowed:" _lines[_i])
572 (sp_state == "bid" || sp_state == "play") &&
574 if (sp_from in sp_players)
575 say(".slap " FROM ", it is not your turn.")
577 say(".slap " FROM ", you are not playing.")
582 /^\.bid (0|[1-9][0-9]*)$/ {
583 if ($2 < 0 || $2 > 13) {
584 reply("You can only bid from 0 to 13")
588 if ($2 == 0 && !sp_looked[i]) {
589 say(FROM " goes blind nil!")
591 } else if ($2 == 0) {
592 say(FROM " goes nil!")
597 if (sp_turn != sp_dealer) {
598 say(sp_player ": it is your bid! (" sp_bidders() ")")
600 for (p in sp_players)
601 say(p, "You have: " sp_hand(p, p))
603 for (i=0; i<2; i++) {
605 say(sp_team(i) ": select a card to pass " \
606 "(/msg " NICK " .pass <card>)")
610 if (sp_state == "play")
611 say(sp_player ": you have the opening lead!")
616 sp_state == "pass" &&
619 _team = sp_from in sp_players ? sp_players[sp_from] % 2 : 0
621 # check validity and pass
622 if (!(sp_from in sp_players)) {
623 say(".slap " FROM ", you are not playing.")
625 else if (!sp_passer(_team)) {
626 reply("Your team did not go blind")
628 else if (sp_pass[sp_players[sp_from]]) {
629 reply("You have already passed a card")
631 else if (!(_card in sp_deck)) {
632 reply("Invalid card")
634 else if (!(_card in sp_hands[sp_from])) {
635 reply("You do not have that card")
638 sp_pass[sp_players[sp_from]] = $2
639 say(sp_channel, FROM " passes a card")
642 # check for end of passing
643 if ((!sp_passer(0) || (sp_pass[0] && sp_pass[2])) &&
644 (!sp_passer(1) || (sp_pass[1] && sp_pass[3]))) {
648 delete sp_hands[sp_order[i]][_card]
649 sp_hands[sp_order[_partner]][_card] = 1
651 say(sp_channel, "Cards have been passed!")
652 say(sp_channel, sp_player ": you have the opening lead!")
653 for (p in sp_players)
654 say(p, "You have: " sp_hand(p, p))
659 sp_state ~ "(bid|pass|play)" &&
661 if (!(sp_from in sp_players)) {
662 say(".slap " FROM ", you are not playing.")
664 sp_looked[sp_players[sp_from]] = 1
665 say(FROM, "You have: " sp_hand(FROM, sp_from))
670 sp_state == "play" &&
673 gsub(/[^A-Za-z0-9]/, "", _card);
674 if (!(_card in sp_deck)) {
675 reply("Invalid card")
677 else if (!(_card in sp_hands[sp_from])) {
678 reply("You do not have that card")
680 else if (sp_suit && _card !~ sp_suit && sp_hasa(sp_from, sp_suit)) {
681 reply("You must follow suit (" sp_suit ")")
683 else if (_card ~ /s/ && length(sp_hands[sp_from]) == 13 && sp_hasa(sp_from, "[^s]$")) {
684 reply("You cannot trump on the first hand")
686 else if (_card ~ /s/ && length(sp_pile) == 0 && sp_hasa(sp_from, "[^s]$") && !sp_broken) {
687 reply("Spades have not been broken")
691 if (sp_state == "play") {
692 if (length(sp_hands[sp_from]))
693 say(FROM, "You have: " sp_hand(FROM, sp_from))
695 say(sp_player ": it is your turn! " \
696 "(" sp_pretty(sp_piles, sp_player) ")")
698 say(sp_player ": it is your turn!")
703 /^\.bids/ && sp_state == "bid" ||
704 /^\.turn/ && sp_state ~ "(bid|pass|play)" {
706 _pile = sp_pretty(sp_piles, FROM)
710 if (/!/ && sp_share[_i] == sp_player)
711 _extra = _extra " " _i "!"
713 if (sp_state == "bid" && !_bids)
714 say("It is " sp_player "'s bid!" _extra)
715 if (sp_state == "bid" && _bids)
716 say("It is " sp_player "'s bid!" _extra " (" _bids ")")
717 if (sp_state == "play" && !_pile)
718 say("It is " sp_player "'s turn!" _extra)
719 if (sp_state == "play" && _pile)
720 say("It is " sp_player "'s turn!" _extra " (" _pile ")")
722 for (_i=0; sp_state == "pass" && _i<4; _i++)
723 if (sp_passer(_i) && !sp_pass[_i])
724 say("Waiting for " sp_order[_i] " to pass a card!")
727 /^\.bids$/ && sp_state ~ "(pass|play)" {
728 say(sp_order[0] " bid " sp_bid(0) ", " \
729 sp_order[2] " bid " sp_bid(2) ", " \
730 "total: " sp_bids[0] + sp_bids[2])
731 say(sp_order[1] " bid " sp_bid(1) ", " \
732 sp_order[3] " bid " sp_bid(3) ", " \
733 "total: " sp_bids[1] + sp_bids[3])
736 /^\.tricks$/ && sp_state == "play" {
737 say(sp_order[0] " took " int(sp_tricks[0]) "/" sp_bid(0) ", " \
738 sp_order[2] " took " int(sp_tricks[2]) "/" sp_bid(2))
739 say(sp_order[1] " took " int(sp_tricks[1]) "/" sp_bid(1) ", " \
740 sp_order[3] " took " int(sp_tricks[3]) "/" sp_bid(3))
743 (TO == NICK || DST == sp_channel) &&
744 /^\.(score|status)$/ {
745 if (sp_state == "new") {
746 say("There is no game in progress")
748 if (sp_state ~ "join|bid|pass|play") {
750 sp_playto " points, " \
753 if (sp_state == "join") {
754 say("Waiting for players: " \
755 sp_order[0] " " sp_order[1] " " \
756 sp_order[2] " " sp_order[3])
758 if (sp_state ~ "bid|pass|play") {
759 say(sp_team(0) ": " \
760 int(sp_scores[0]) " points, " \
761 int(sp_bags(0)) " bags")
762 say(sp_team(1) ": " \
763 int(sp_scores[1]) " points, " \
764 int(sp_bags(1)) " bags")
768 /^\.((new|end|load)game|join|look|bid|pass|play)/ {
769 sp_save("var/sp_cur.json");