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_deal( shuf)
166 say("/me deals the cards")
167 asorti(sp_deck, shuf, "sp_usort")
168 for (i=1; i<=52; i++)
169 sp_hands[sp_order[i%4]][shuf[i]] = 1
171 sp_dealer = (sp_dealer+1)%4
173 sp_player = sp_order[sp_turn]
174 say("Bidding starts with " sp_player "!")
177 function sp_hand(to, who, sort, str)
179 asorti(sp_hands[who], sort, "sp_csort")
180 for (i=0; i<length(sort); i++)
181 str = str "" sprintf("%4s", sort[i])
182 gsub(/^ +| +$/, "", str)
183 return sp_pretty(str, to)
186 function sp_hasa(who, expr)
188 for (c in sp_hands[who]) {
195 function sp_type(card)
197 return substr(card, length(card))
200 function sp_usort(a,b,c,d) {
204 function sp_csort(i1,v1,i2,v2) {
205 return sp_deck[i1] > sp_deck[i2] ? +1 :
206 sp_deck[i1] < sp_deck[i2] ? -1 : 0;
209 function sp_winner( card, tmp)
211 for (card in sp_pile)
212 if (card !~ sp_suit && card !~ /s/)
214 asorti(sp_pile, tmp, "sp_csort")
215 #print "pile: " tmp[1] ">" tmp[2] ">" tmp[3] ">" tmp[4]
221 #return "{" sp_order[i+0] "," sp_order[i+2] "}"
222 return sp_order[i+0] "/" sp_order[i+2]
225 function sp_bags(i, bags)
227 bags = sp_scores[i] % sp_limit
233 function sp_bidders( i, turn, bid, bids)
235 for (i = 0; i < 4; i++) {
236 turn = (sp_dealer + i) % 4
237 if (sp_bids[turn] && !sp_nil[turn])
238 bid = sp_order[turn] ":" sp_bids[turn]
239 else if (sp_nil[turn] == 1)
240 bid = sp_order[turn] ":" "nil"
241 else if (sp_nil[turn] == 2)
242 bid = sp_order[turn] ":" "blind"
247 gsub(/^ +| +$/, "", bids)
251 function sp_score( bids, times, tricks)
253 for (i=0; i<2; i++) {
254 bids = sp_bids[i] + sp_bids[i+2]
255 tricks = sp_tricks[i] + sp_tricks[i+2]
257 times = int((sp_bags(i) + bags) / sp_limit)
259 say(sp_team(i) " bag" (times>1?" way ":" ") "out")
260 sp_scores[i] -= sp_limit * 10 * times;
262 if (tricks >= bids) {
263 say(sp_team(i) " make their bid: " tricks "/" bids)
264 sp_scores[i] += bids*10 + bags;
266 say(sp_team(i) " go bust: " tricks "/" bids)
267 sp_scores[i] -= bids*10;
270 for (i=0; i<4; i++) {
273 say(sp_order[i] " " \
274 (sp_nil[i] == 1 && !sp_tricks[i] ? "makes nil!" :
275 sp_nil[i] == 1 && sp_tricks[i] ? "fails at nil!" :
276 sp_nil[i] == 2 && !sp_tricks[i] ? "makes blind nil!" :
277 sp_nil[i] == 2 && sp_tricks[i] ? "fails miserably at blind nil!" :
279 sp_scores[i%2] += sp_limit * 10 * sp_nil[i] * \
280 (sp_tricks[i] == 0 ? 1 : -1)
284 function sp_play(card, winner, pi)
286 delete sp_hands[sp_from][card]
287 sp_pile[card] = sp_player
288 sp_piles = sp_piles (sp_piles?",":"") card
295 if (length(sp_pile) == 1)
296 sp_suit = sp_type(card)
299 if (length(sp_pile) == 4) {
301 pi = sp_players[sp_pile[winner]]
303 say(sp_pile[winner] " wins with " sp_pretty(winner, FROM) \
304 " (" sp_pretty(sp_piles, FROM) ")")
305 sp_next(sp_pile[winner])
310 if (sp_tricks[0] + sp_tricks[1] + \
311 sp_tricks[2] + sp_tricks[3] == 13) {
314 if (sp_scores[0] >= sp_playto || sp_scores[1] >= sp_playto &&
315 sp_scores[0] != sp_scores[1]) {
317 winner = sp_scores[0] > sp_scores[1] ? 0 : 1
319 say(CHANNEL, sp_team(winner) " wins the game " \
320 sp_scores[winner] " to " sp_scores[looser])
321 say(CHANNEL, sp_order[winner+0] "++")
322 say(CHANNEL, sp_order[winner+2] "++")
326 if (sp_scores[0] == sp_scores[1] &&
327 sp_scores[0] >= sp_playto)
328 say("It's tie! Playing an extra round!");
337 cmd = "od -An -N4 -td4 /dev/random"
343 sp_load("var/sp_cur.json");
345 # say(sp_channel, "Game restored.")
349 sp_from = AUTH in sp_auths ? sp_auths[AUTH] : \
350 AUTH in sp_share ? sp_share[AUTH] : FROM
351 sp_valid = sp_from && sp_from == sp_player
357 say("Spades! " sp_pretty("As,Ah,Ad,Ac", FROM))
362 sp_save("var/sp_save.json");
368 sp_load("var/sp_save.json");
373 /^\.help [Ss]pades$/ {
374 say("Spades -- play a game of spades")
375 say(".help game -- setup and administer the game")
376 say(".help play -- commands for playing spades")
377 say(".help auth -- control player authorization")
382 say(".newgame [score] -- start a game to <score> points, default 500")
383 say(".endgame -- abort the current game")
384 say(".savegame -- save the current game to disk")
385 say(".loadgame -- load the previously saved game")
390 say(".join -- join the current game")
391 say(".look -- look at your cards")
392 say(".bid [n] -- bid for <n> tricks")
393 say(".pass [card] -- pass a card to your partner")
394 say(".play [card] -- play a card")
395 say(".turn -- check whose turn it is")
396 say(".bids -- check what everyone bid")
397 say(".tricks -- check how many trick have been taken")
398 say(".score -- check the score")
403 say(".auth [who] -- display authentication info for a user")
404 say(".allow [who] -- allow another person to play on your behalf")
405 say(".deny [who] -- prevent a previously allowed user from playing")
406 say(".show -- display which users can play for which players")
412 /^\.deal (\w+) (.*)/ {
413 say(sp_channel, FROM " is cheating for " $2)
415 for (i=3; i<=NF; i++)
421 sp_state == "play" &&
422 /^\.force (\w+) (\S+)$/ {
423 say(sp_channel, FROM " is cheating for " $2)
431 /^\.newgame ?([0-9]+)?$/ {
432 if (sp_state != "new") {
433 reply("There is already a game in progress.")
437 sp_playto = $2 ? $2 : 200
438 sp_limit = sp_playto > 200 ? 10 : 5;
441 say(sp_owner " starts a game of Spades to " sp_playto " with " sp_limit " bags!")
445 (sp_from == sp_owner || AUTH == OWNER) &&
447 if (sp_state == "new") {
448 reply("There is no game in progress.")
450 say(FROM " ends the game")
456 if (sp_state == "new") {
457 reply("There is no game in progress")
459 else if (sp_state == "play") {
460 reply("The game has already started")
462 else if (sp_state == "join" && sp_from in sp_players) {
463 reply("You are already playing")
465 else if (sp_state == "join") {
469 sp_auths[AUTH] = FROM
471 say(FROM " joins the game!")
473 if (sp_state == "join" && sp_turn == 0)
478 _who = $2 in USERS ? USERS[$2]["auth"] : ""
479 _str = _who && _who != $2 ? $2 " (" _who ")" : $2
480 if (sp_state ~ "new|join") {
481 reply("The game has not yet started")
483 else if (!(sp_from in sp_players)) {
484 reply("You are not playing")
487 reply(_str " is not logged in")
489 else if (_who in sp_players || _who in sp_auths) {
490 reply(_str " is a primary player")
492 else if (_who in sp_share) {
493 reply(_str " is already playing for " sp_share[_who])
496 reply(_str " can now play for " sp_from)
497 sp_share[_who] = sp_from
502 _who = $2 in USERS ? USERS[$2]["auth"] : $2
503 _str = _who && _who != $2 ? $2 " (" _who ")" : $2
504 if (sp_state ~ "new|join") {
505 reply("The game has not yet started")
507 else if (!(sp_from in sp_players)) {
508 reply("You are not playing")
510 else if (_who in sp_players || _who in sp_auths) {
511 reply(_str " is a primary player")
513 else if (!(_who in sp_share) || sp_share[_who] != sp_from) {
514 reply(_str " is not playing for " sp_from)
517 reply(_str " can no longer play for " sp_from)
518 delete sp_share[_who]
522 sp_state ~ "(bid|pass|play)" &&
525 say(_i " can play for " sp_share[_i]);
529 (sp_state == "bid" || sp_state == "play") &&
531 if (sp_from in sp_players)
532 say(".slap " FROM ", it is not your turn.")
534 say(".slap " FROM ", you are not playing.")
540 if ($2 < 0 || $2 > 13) {
541 say("You can only bid from 0 to 13")
545 if ($2 == 0 && !sp_looked[i]) {
546 say(FROM " goes blind nil!")
548 } else if ($2 == 0) {
549 say(FROM " goes nil!")
554 if (sp_turn != sp_dealer) {
555 say("Bidding goes to " sp_player "!")
557 for (p in sp_players)
558 say(p, "You have: " sp_hand(p, p))
560 for (i=0; i<2; i++) {
561 if (sp_nil[i] == 2 || sp_nil[i+2] == 2) {
562 say(sp_team(i) ": select a card to pass " \
563 "(/msg " NICK " .pass <card>)")
567 if (sp_state == "play")
568 say("Play starts with " sp_player "!")
573 sp_state == "pass" &&
576 _team = sp_from in sp_players ? sp_players[sp_from] % 2 : 0
578 # check validity and pass
579 if (!(sp_from in sp_players)) {
580 say(".slap " FROM ", you are not playing.")
582 else if (sp_nil[_team] != 2 && sp_nil[_team+2] != 2) {
583 reply("Your team did not go blind")
585 else if (sp_pass[sp_players[sp_from]]) {
586 reply("You have already passed a card")
588 else if (!(_card in sp_deck)) {
589 reply("Invalid card")
591 else if (!(_card in sp_hands[sp_from])) {
592 reply("You do not have that card")
595 sp_pass[sp_players[sp_from]] = $2
596 say(sp_channel, FROM " passes a card")
599 # check for end of passing
600 if (((sp_nil[0] != 2 && sp_nil[2] != 2) || (sp_pass[0] && sp_pass[2])) &&
601 ((sp_nil[1] != 2 && sp_nil[3] != 2) || (sp_pass[1] && sp_pass[3]))) {
605 delete sp_hands[sp_order[i]][_card]
606 sp_hands[sp_order[_partner]][_card] = 1
608 say(sp_channel, "Cards have been passed, play starts with " sp_player "!")
609 for (p in sp_players)
610 say(p, "You have: " sp_hand(p, p))
615 sp_state ~ "(bid|pass|play)" &&
617 if (!(sp_from in sp_players)) {
618 say(".slap " FROM ", you are not playing.")
620 sp_looked[sp_players[sp_from]] = 1
621 say(FROM, "You have: " sp_hand(FROM, sp_from))
626 sp_state == "play" &&
629 gsub(/[^A-Za-z0-9]/, "", _card);
630 if (!(_card in sp_deck)) {
631 reply("Invalid card")
633 else if (!(_card in sp_hands[sp_from])) {
634 reply("You do not have that card")
636 else if (sp_suit && _card !~ sp_suit && sp_hasa(sp_from, sp_suit)) {
637 reply("You must follow suit (" sp_suit ")")
639 else if (_card ~ /s/ && length(sp_hands[sp_from]) == 13 && sp_hasa(sp_from, "[^s]$")) {
640 reply("You cannot trump on the first hand")
642 else if (_card ~ /s/ && length(sp_pile) == 0 && sp_hasa(sp_from, "[^s]$") && !sp_broken) {
643 reply("Spades have not been broken")
647 if (sp_state == "play") {
648 if (length(sp_hands[sp_from]))
649 say(FROM, "You have: " sp_hand(FROM, sp_from))
651 say(sp_player ": it is your turn! " \
652 "(" sp_pretty(sp_piles, sp_player) ")")
654 say(sp_player ": it is your turn!")
659 /^\.turn/ && sp_state ~ "(bid|pass|play)" {
661 _pile = sp_pretty(sp_piles, FROM)
662 if (sp_state == "bid" && !_bids)
663 say("It is " sp_player "'s bid!")
664 if (sp_state == "bid" && _bids)
665 say("It is " sp_player "'s bid! (" _bids ")")
666 if (sp_state == "play" && !_pile)
667 say("It is " sp_player "'s turn!")
668 if (sp_state == "play" && _pile)
669 say("It is " sp_player "'s turn! (" _pile ")")
670 for (_i=0; sp_state == "pass" && _i<4; _i++)
671 if ((sp_nil[_i%2+0]==2 || sp_nil[_i%2+2]==2) && !sp_pass[_i])
672 say("Waiting for " sp_order[_i] " to pass a card!")
675 /^\.bids$/ && sp_state ~ "(pass|play)" {
676 say(sp_order[0] " bid " sp_bids[0] ", " \
677 sp_order[2] " bid " sp_bids[2] ", " \
678 "total: " sp_bids[0] + sp_bids[2])
679 say(sp_order[1] " bid " sp_bids[1] ", " \
680 sp_order[3] " bid " sp_bids[3] ", " \
681 "total: " sp_bids[1] + sp_bids[3])
684 /^\.tricks$/ && sp_state == "play" {
685 say(sp_order[0] " took " int(sp_tricks[0]) "/" int(sp_bids[0]) ", " \
686 sp_order[2] " took " int(sp_tricks[2]) "/" int(sp_bids[2]))
687 say(sp_order[1] " took " int(sp_tricks[1]) "/" int(sp_bids[1]) ", " \
688 sp_order[3] " took " int(sp_tricks[3]) "/" int(sp_bids[3]))
691 (TO == NICK || DST == sp_channel) &&
692 /^\.(score|status)$/ {
693 if (sp_state == "new") {
694 say("There is no game in progress")
696 if (sp_state ~ "join|bid|pass|play") {
698 sp_playto " points, " \
701 if (sp_state == "join") {
702 say("Waiting for players: " \
703 sp_order[0] " " sp_order[1] " " \
704 sp_order[2] " " sp_order[3])
706 if (sp_state ~ "bid|pass|play") {
707 say(sp_team(0) ": " \
708 int(sp_scores[0]) " points, " \
709 int(sp_bags(0)) " bags")
710 say(sp_team(1) ": " \
711 int(sp_scores[1]) " points, " \
712 int(sp_bags(1)) " bags")
716 /^\.((new|end|load)game|join|look|bid|pass|play)/ {
717 sp_save("var/sp_cur.json");