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 /^\.newgame ?([0-9]+)?$/ {
441 if (sp_state != "new") {
442 reply("There is already a game in progress.")
446 sp_playto = $2 ? $2 : 200
447 sp_limit = sp_playto > 200 ? 10 : 5;
450 say(sp_owner " starts a game of Spades to " sp_playto " with " sp_limit " bags!")
454 (sp_from == sp_owner || AUTH == OWNER) &&
456 if (sp_state == "new") {
457 reply("There is no game in progress.")
459 say(FROM " ends the game")
465 if (sp_state == "new") {
466 reply("There is no game in progress")
468 else if (sp_state == "play") {
469 reply("The game has already started")
471 else if (sp_state == "join" && sp_from in sp_players) {
472 reply("You are already playing")
474 else if (sp_state == "join") {
478 sp_auths[AUTH] = FROM
480 say(FROM " joins the game!")
482 if (sp_state == "join" && sp_turn == 0) {
489 _who = $2 in USERS ? USERS[$2]["auth"] : ""
490 _str = _who && _who != $2 ? $2 " (" _who ")" : $2
491 if (sp_state ~ "new|join") {
492 reply("The game has not yet started")
494 else if (!(sp_from in sp_players)) {
495 reply("You are not playing")
498 reply(_str " is not logged in")
500 else if (_who in sp_players || _who in sp_auths) {
501 reply(_str " is a primary player")
503 else if (_who in sp_share) {
504 reply(_str " is already playing for " sp_share[_who])
507 reply(_str " can now play for " sp_from)
508 sp_share[_who] = sp_from
513 _who = $2 in USERS ? USERS[$2]["auth"] : $2
514 _str = _who && _who != $2 ? $2 " (" _who ")" : $2
515 if (sp_state ~ "new|join") {
516 reply("The game has not yet started")
518 else if (!(sp_from in sp_players)) {
519 reply("You are not playing")
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) || sp_share[_who] != sp_from) {
525 reply(_str " is not playing for " sp_from)
528 reply(_str " can no longer play for " sp_from)
529 delete sp_share[_who]
533 sp_state ~ "(bid|pass|play)" &&
536 say(_i " can play for " sp_share[_i]);
540 (sp_state == "bid" || sp_state == "play") &&
542 if (sp_from in sp_players)
543 say(".slap " FROM ", it is not your turn.")
545 say(".slap " FROM ", you are not playing.")
551 if ($2 < 0 || $2 > 13) {
552 say("You can only bid from 0 to 13")
556 if ($2 == 0 && !sp_looked[i]) {
557 say(FROM " goes blind nil!")
559 } else if ($2 == 0) {
560 say(FROM " goes nil!")
565 if (sp_turn != sp_dealer) {
566 say("Bidding goes to " sp_player "!")
568 for (p in sp_players)
569 say(p, "You have: " sp_hand(p, p))
571 for (i=0; i<2; i++) {
572 if (sp_nil[i] == 2 || sp_nil[i+2] == 2) {
573 say(sp_team(i) ": select a card to pass " \
574 "(/msg " NICK " .pass <card>)")
578 if (sp_state == "play")
579 say("Play starts with " sp_player "!")
584 sp_state == "pass" &&
587 _team = sp_from in sp_players ? sp_players[sp_from] % 2 : 0
589 # check validity and pass
590 if (!(sp_from in sp_players)) {
591 say(".slap " FROM ", you are not playing.")
593 else if (sp_nil[_team] != 2 && sp_nil[_team+2] != 2) {
594 reply("Your team did not go blind")
596 else if (sp_pass[sp_players[sp_from]]) {
597 reply("You have already passed a card")
599 else if (!(_card in sp_deck)) {
600 reply("Invalid card")
602 else if (!(_card in sp_hands[sp_from])) {
603 reply("You do not have that card")
606 sp_pass[sp_players[sp_from]] = $2
607 say(sp_channel, FROM " passes a card")
610 # check for end of passing
611 if (((sp_nil[0] != 2 && sp_nil[2] != 2) || (sp_pass[0] && sp_pass[2])) &&
612 ((sp_nil[1] != 2 && sp_nil[3] != 2) || (sp_pass[1] && sp_pass[3]))) {
616 delete sp_hands[sp_order[i]][_card]
617 sp_hands[sp_order[_partner]][_card] = 1
619 say(sp_channel, "Cards have been passed, play starts with " sp_player "!")
620 for (p in sp_players)
621 say(p, "You have: " sp_hand(p, p))
626 sp_state ~ "(bid|pass|play)" &&
628 if (!(sp_from in sp_players)) {
629 say(".slap " FROM ", you are not playing.")
631 sp_looked[sp_players[sp_from]] = 1
632 say(FROM, "You have: " sp_hand(FROM, sp_from))
637 sp_state == "play" &&
640 gsub(/[^A-Za-z0-9]/, "", _card);
641 if (!(_card in sp_deck)) {
642 reply("Invalid card")
644 else if (!(_card in sp_hands[sp_from])) {
645 reply("You do not have that card")
647 else if (sp_suit && _card !~ sp_suit && sp_hasa(sp_from, sp_suit)) {
648 reply("You must follow suit (" sp_suit ")")
650 else if (_card ~ /s/ && length(sp_hands[sp_from]) == 13 && sp_hasa(sp_from, "[^s]$")) {
651 reply("You cannot trump on the first hand")
653 else if (_card ~ /s/ && length(sp_pile) == 0 && sp_hasa(sp_from, "[^s]$") && !sp_broken) {
654 reply("Spades have not been broken")
658 if (sp_state == "play") {
659 if (length(sp_hands[sp_from]))
660 say(FROM, "You have: " sp_hand(FROM, sp_from))
662 say(sp_player ": it is your turn! " \
663 "(" sp_pretty(sp_piles, sp_player) ")")
665 say(sp_player ": it is your turn!")
670 /^\.bids/ && sp_state == "bid" ||
671 /^\.turn/ && sp_state ~ "(bid|pass|play)" {
673 _pile = sp_pretty(sp_piles, FROM)
674 if (sp_state == "bid" && !_bids)
675 say("It is " sp_player "'s bid!")
676 if (sp_state == "bid" && _bids)
677 say("It is " sp_player "'s bid! (" _bids ")")
678 if (sp_state == "play" && !_pile)
679 say("It is " sp_player "'s turn!")
680 if (sp_state == "play" && _pile)
681 say("It is " sp_player "'s turn! (" _pile ")")
682 for (_i=0; sp_state == "pass" && _i<4; _i++)
683 if ((sp_nil[_i%2+0]==2 || sp_nil[_i%2+2]==2) && !sp_pass[_i])
684 say("Waiting for " sp_order[_i] " to pass a card!")
687 /^\.bids$/ && sp_state ~ "(pass|play)" {
688 say(sp_order[0] " bid " sp_bid(0) ", " \
689 sp_order[2] " bid " sp_bid(2) ", " \
690 "total: " sp_bids[0] + sp_bids[2])
691 say(sp_order[1] " bid " sp_bid(1) ", " \
692 sp_order[3] " bid " sp_bid(3) ", " \
693 "total: " sp_bids[1] + sp_bids[3])
696 /^\.tricks$/ && sp_state == "play" {
697 say(sp_order[0] " took " int(sp_tricks[0]) "/" sp_bid(0) ", " \
698 sp_order[2] " took " int(sp_tricks[2]) "/" sp_bid(2))
699 say(sp_order[1] " took " int(sp_tricks[1]) "/" sp_bid(1) ", " \
700 sp_order[3] " took " int(sp_tricks[3]) "/" sp_bid(3))
703 (TO == NICK || DST == sp_channel) &&
704 /^\.(score|status)$/ {
705 if (sp_state == "new") {
706 say("There is no game in progress")
708 if (sp_state ~ "join|bid|pass|play") {
710 sp_playto " points, " \
713 if (sp_state == "join") {
714 say("Waiting for players: " \
715 sp_order[0] " " sp_order[1] " " \
716 sp_order[2] " " sp_order[3])
718 if (sp_state ~ "bid|pass|play") {
719 say(sp_team(0) ": " \
720 int(sp_scores[0]) " points, " \
721 int(sp_bags(0)) " bags")
722 say(sp_team(1) ": " \
723 int(sp_scores[1]) " points, " \
724 int(sp_bags(1)) " bags")
728 /^\.((new|end|load)game|join|look|bid|pass|play)/ {
729 sp_save("var/sp_cur.json");