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 asorti(sp_players, mixed, "sp_usort")
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 asorti(sp_deck, shuf, "sp_usort")
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(a,b,c,d) {
213 function sp_csort(i1,v1,i2,v2) {
214 return sp_deck[i1] > sp_deck[i2] ? +1 :
215 sp_deck[i1] < sp_deck[i2] ? -1 : 0;
218 function sp_winner( card, tmp)
220 for (card in sp_pile)
221 if (card !~ sp_suit && card !~ /s/)
223 asorti(sp_pile, tmp, "sp_csort")
224 #print "pile: " tmp[1] ">" tmp[2] ">" tmp[3] ">" tmp[4]
230 #return "{" sp_order[i+0] "," sp_order[i+2] "}"
231 return sp_order[i+0] "/" sp_order[i+2]
234 function sp_bags(i, bags)
236 bags = sp_scores[i] % sp_limit
244 return sp_nil[who] == 0 ? sp_bids[who] :
245 sp_nil[who] == 1 ? "nil" :
246 sp_nil[who] == 2 ? "blind" : "n/a"
249 function sp_bidders( i, turn, bid, bids)
251 for (i = 0; i < 4; i++) {
252 turn = (sp_dealer + i) % 4
253 if (bid = sp_bid(turn))
254 bids = bids " " sp_order[turn] ":" bid
256 gsub(/^ +| +$/, "", bids)
260 function sp_score( bids, times, tricks)
262 for (i=0; i<2; i++) {
263 bids = sp_bids[i] + sp_bids[i+2]
264 tricks = sp_tricks[i] + sp_tricks[i+2]
266 times = int((sp_bags(i) + bags) / sp_limit)
268 say(sp_team(i) " bag" (times>1?" way ":" ") "out")
269 sp_scores[i] -= sp_limit * 10 * times;
271 if (tricks >= bids) {
272 say(sp_team(i) " make their bid: " tricks "/" bids)
273 sp_scores[i] += bids*10 + bags;
275 say(sp_team(i) " go bust: " tricks "/" bids)
276 sp_scores[i] -= bids*10;
279 for (i=0; i<4; i++) {
282 say(sp_order[i] " " \
283 (sp_nil[i] == 1 && !sp_tricks[i] ? "makes nil!" :
284 sp_nil[i] == 1 && sp_tricks[i] ? "fails at nil!" :
285 sp_nil[i] == 2 && !sp_tricks[i] ? "makes blind nil!" :
286 sp_nil[i] == 2 && sp_tricks[i] ? "fails miserably at blind nil!" :
288 sp_scores[i%2] += sp_limit * 10 * sp_nil[i] * \
289 (sp_tricks[i] == 0 ? 1 : -1)
293 function sp_play(card, winner, pi)
295 delete sp_hands[sp_from][card]
296 sp_pile[card] = sp_player
297 sp_piles = sp_piles (sp_piles?",":"") card
304 if (length(sp_pile) == 1)
305 sp_suit = sp_type(card)
308 if (length(sp_pile) == 4) {
310 pi = sp_players[sp_pile[winner]]
312 say(sp_pile[winner] " wins with " sp_pretty(winner, FROM) \
313 " (" sp_pretty(sp_piles, FROM) ")")
314 sp_next(sp_pile[winner])
319 if (sp_tricks[0] + sp_tricks[1] + \
320 sp_tricks[2] + sp_tricks[3] == 13) {
323 if (sp_scores[0] >= sp_playto || sp_scores[1] >= sp_playto &&
324 sp_scores[0] != sp_scores[1]) {
326 winner = sp_scores[0] > sp_scores[1] ? 0 : 1
328 say(CHANNEL, sp_team(winner) " wins the game " \
329 sp_scores[winner] " to " sp_scores[looser])
330 say(CHANNEL, sp_order[winner+0] "++")
331 say(CHANNEL, sp_order[winner+2] "++")
335 if (sp_scores[0] == sp_scores[1] &&
336 sp_scores[0] >= sp_playto)
337 say("It's tie! Playing an extra round!");
346 cmd = "od -An -N4 -td4 /dev/random"
352 sp_load("var/sp_cur.json");
354 # say(sp_channel, "Game restored.")
358 sp_from = AUTH in sp_auths ? sp_auths[AUTH] : \
359 AUTH in sp_share ? sp_share[AUTH] : FROM
360 sp_valid = sp_from && sp_from == sp_player
366 say("Spades! " sp_pretty("As,Ah,Ad,Ac", FROM))
371 sp_save("var/sp_save.json");
377 sp_load("var/sp_save.json");
382 /^\.help [Ss]pades$/ {
383 say("Spades -- play a game of spades")
384 say(".help game -- setup and administer the game")
385 say(".help play -- commands for playing spades")
386 say(".help auth -- control player authorization")
391 say(".newgame [score] -- start a game to <score> points, default 500")
392 say(".endgame -- abort the current game")
393 say(".savegame -- save the current game to disk")
394 say(".loadgame -- load the previously saved game")
399 say(".join -- join the current game")
400 say(".look -- look at your cards")
401 say(".bid [n] -- bid for <n> tricks")
402 say(".pass [card] -- pass a card to your partner")
403 say(".play [card] -- play a card")
404 say(".turn -- check whose turn it is")
405 say(".bids -- check what everyone bid")
406 say(".tricks -- check how many trick have been taken")
407 say(".score -- check the score")
412 say(".auth [who] -- display authentication info for a user")
413 say(".allow [who] -- allow another person to play on your behalf")
414 say(".deny [who] -- prevent a previously allowed user from playing")
415 say(".show -- display which users can play for which players")
421 /^\.deal (\w+) (.*)/ {
422 say(sp_channel, FROM " is cheating for " $2)
424 for (i=3; i<=NF; i++)
430 sp_state == "play" &&
431 /^\.force (\w+) (\S+)$/ {
432 say(sp_channel, FROM " is cheating for " $2)
440 match($0, /^\.newgame ?([0-9]+) *- *([0-9]+)$/, _arr) {
441 if (_arr[2] > _arr[1])
442 $0 = $1 " " int(rand() * (_arr[2]-_arr[1])+_arr[1])
445 /^\.newgame ?([0-9]+)?$/ {
446 if (sp_state != "new") {
447 reply("There is already a game in progress.")
451 sp_playto = $2 ? $2 : 200
452 sp_limit = sp_playto > 200 ? 10 : 5;
455 say(sp_owner " starts a game of Spades to " sp_playto " with " sp_limit " bags!")
459 (sp_from == sp_owner || AUTH == OWNER) &&
461 if (sp_state == "new") {
462 reply("There is no game in progress.")
464 say(FROM " ends the game")
470 if (sp_state == "new") {
471 reply("There is no game in progress")
473 else if (sp_state == "play") {
474 reply("The game has already started")
476 else if (sp_state == "join" && sp_from in sp_players) {
477 reply("You are already playing")
479 else if (sp_state == "join") {
483 sp_auths[AUTH] = FROM
485 say(FROM " joins the game!")
487 if (sp_state == "join" && sp_turn == 0) {
494 _who = $2 in USERS ? USERS[$2]["auth"] : ""
495 _str = _who && _who != $2 ? $2 " (" _who ")" : $2
496 if (sp_state ~ "new|join") {
497 reply("The game has not yet started")
499 else if (!(sp_from in sp_players)) {
500 reply("You are not playing")
503 reply(_str " is not logged in")
505 else if (_who in sp_players || _who in sp_auths) {
506 reply(_str " is a primary player")
508 else if (_who in sp_share) {
509 reply(_str " is already playing for " sp_share[_who])
512 reply(_str " can now play for " sp_from)
513 sp_share[_who] = sp_from
518 _who = $2 in USERS ? USERS[$2]["auth"] : $2
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")
526 else if (_who in sp_players || _who in sp_auths) {
527 reply(_str " is a primary player")
529 else if (!(_who in sp_share) || sp_share[_who] != sp_from) {
530 reply(_str " is not playing for " sp_from)
533 reply(_str " can no longer play for " sp_from)
534 delete sp_share[_who]
538 sp_state ~ "(bid|pass|play)" &&
541 say(_i " can play for " sp_share[_i]);
545 (sp_state == "bid" || sp_state == "play") &&
547 if (sp_from in sp_players)
548 say(".slap " FROM ", it is not your turn.")
550 say(".slap " FROM ", you are not playing.")
556 if ($2 < 0 || $2 > 13) {
557 say("You can only bid from 0 to 13")
561 if ($2 == 0 && !sp_looked[i]) {
562 say(FROM " goes blind nil!")
564 } else if ($2 == 0) {
565 say(FROM " goes nil!")
570 if (sp_turn != sp_dealer) {
571 say("Bidding goes to " sp_player "!")
573 for (p in sp_players)
574 say(p, "You have: " sp_hand(p, p))
576 for (i=0; i<2; i++) {
577 if (sp_nil[i] == 2 || sp_nil[i+2] == 2) {
578 say(sp_team(i) ": select a card to pass " \
579 "(/msg " NICK " .pass <card>)")
583 if (sp_state == "play")
584 say("Play starts with " sp_player "!")
589 sp_state == "pass" &&
592 _team = sp_from in sp_players ? sp_players[sp_from] % 2 : 0
594 # check validity and pass
595 if (!(sp_from in sp_players)) {
596 say(".slap " FROM ", you are not playing.")
598 else if (sp_nil[_team] != 2 && sp_nil[_team+2] != 2) {
599 reply("Your team did not go blind")
601 else if (sp_pass[sp_players[sp_from]]) {
602 reply("You have already passed a card")
604 else if (!(_card in sp_deck)) {
605 reply("Invalid card")
607 else if (!(_card in sp_hands[sp_from])) {
608 reply("You do not have that card")
611 sp_pass[sp_players[sp_from]] = $2
612 say(sp_channel, FROM " passes a card")
615 # check for end of passing
616 if (((sp_nil[0] != 2 && sp_nil[2] != 2) || (sp_pass[0] && sp_pass[2])) &&
617 ((sp_nil[1] != 2 && sp_nil[3] != 2) || (sp_pass[1] && sp_pass[3]))) {
621 delete sp_hands[sp_order[i]][_card]
622 sp_hands[sp_order[_partner]][_card] = 1
624 say(sp_channel, "Cards have been passed, play starts with " sp_player "!")
625 for (p in sp_players)
626 say(p, "You have: " sp_hand(p, p))
631 sp_state ~ "(bid|pass|play)" &&
633 if (!(sp_from in sp_players)) {
634 say(".slap " FROM ", you are not playing.")
636 sp_looked[sp_players[sp_from]] = 1
637 say(FROM, "You have: " sp_hand(FROM, sp_from))
642 sp_state == "play" &&
645 gsub(/[^A-Za-z0-9]/, "", _card);
646 if (!(_card in sp_deck)) {
647 reply("Invalid card")
649 else if (!(_card in sp_hands[sp_from])) {
650 reply("You do not have that card")
652 else if (sp_suit && _card !~ sp_suit && sp_hasa(sp_from, sp_suit)) {
653 reply("You must follow suit (" sp_suit ")")
655 else if (_card ~ /s/ && length(sp_hands[sp_from]) == 13 && sp_hasa(sp_from, "[^s]$")) {
656 reply("You cannot trump on the first hand")
658 else if (_card ~ /s/ && length(sp_pile) == 0 && sp_hasa(sp_from, "[^s]$") && !sp_broken) {
659 reply("Spades have not been broken")
663 if (sp_state == "play") {
664 if (length(sp_hands[sp_from]))
665 say(FROM, "You have: " sp_hand(FROM, sp_from))
667 say(sp_player ": it is your turn! " \
668 "(" sp_pretty(sp_piles, sp_player) ")")
670 say(sp_player ": it is your turn!")
675 /^\.bids/ && sp_state == "bid" ||
676 /^\.turn/ && sp_state ~ "(bid|pass|play)" {
678 _pile = sp_pretty(sp_piles, FROM)
679 if (sp_state == "bid" && !_bids)
680 say("It is " sp_player "'s bid!")
681 if (sp_state == "bid" && _bids)
682 say("It is " sp_player "'s bid! (" _bids ")")
683 if (sp_state == "play" && !_pile)
684 say("It is " sp_player "'s turn!")
685 if (sp_state == "play" && _pile)
686 say("It is " sp_player "'s turn! (" _pile ")")
687 for (_i=0; sp_state == "pass" && _i<4; _i++)
688 if ((sp_nil[_i%2+0]==2 || sp_nil[_i%2+2]==2) && !sp_pass[_i])
689 say("Waiting for " sp_order[_i] " to pass a card!")
692 /^\.bids$/ && sp_state ~ "(pass|play)" {
693 say(sp_order[0] " bid " sp_bid(0) ", " \
694 sp_order[2] " bid " sp_bid(2) ", " \
695 "total: " sp_bids[0] + sp_bids[2])
696 say(sp_order[1] " bid " sp_bid(1) ", " \
697 sp_order[3] " bid " sp_bid(3) ", " \
698 "total: " sp_bids[1] + sp_bids[3])
701 /^\.tricks$/ && sp_state == "play" {
702 say(sp_order[0] " took " int(sp_tricks[0]) "/" sp_bid(0) ", " \
703 sp_order[2] " took " int(sp_tricks[2]) "/" sp_bid(2))
704 say(sp_order[1] " took " int(sp_tricks[1]) "/" sp_bid(1) ", " \
705 sp_order[3] " took " int(sp_tricks[3]) "/" sp_bid(3))
708 (TO == NICK || DST == sp_channel) &&
709 /^\.(score|status)$/ {
710 if (sp_state == "new") {
711 say("There is no game in progress")
713 if (sp_state ~ "join|bid|pass|play") {
715 sp_playto " points, " \
718 if (sp_state == "join") {
719 say("Waiting for players: " \
720 sp_order[0] " " sp_order[1] " " \
721 sp_order[2] " " sp_order[3])
723 if (sp_state ~ "bid|pass|play") {
724 say(sp_team(0) ": " \
725 int(sp_scores[0]) " points, " \
726 int(sp_bags(0)) " bags")
727 say(sp_team(1) ": " \
728 int(sp_scores[1]) " points, " \
729 int(sp_bags(1)) " bags")
733 /^\.((new|end|load)game|join|look|bid|pass|play)/ {
734 sp_save("var/sp_cur.json");