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")
376 say(".newgame [score] -- start a game to <score> points, default 500")
377 say(".endgame -- abort the current game")
378 say(".savegame -- save the current game to disk")
379 say(".loadgame -- load the previously saved game")
380 say(".join -- join the current game")
381 say(".look -- look at your cards")
382 say(".bid n -- bid for <n> tricks")
383 say(".play [card] -- play a card")
384 say(".score -- check the score")
385 say(".tricks -- check how many trick have been taken")
386 say(".bids -- check what everyone bid")
392 /^\.deal (\w+) (.*)/ {
393 say(sp_channel, FROM " is cheating for " $2)
395 for (i=3; i<=NF; i++)
401 sp_state == "play" &&
402 /^\.force (\w+) (\S+)$/ {
403 say(sp_channel, FROM " is cheating for " $2)
411 /^\.newgame ?([0-9]+)?$/ {
412 if (sp_state != "new") {
413 reply("There is already a game in progress.")
417 sp_playto = $2 ? $2 : 200
418 sp_limit = sp_playto > 200 ? 10 : 5;
421 say(sp_owner " starts a game of Spades to " sp_playto " with " sp_limit " bags!")
425 (sp_from == sp_owner || AUTH == OWNER) &&
427 if (sp_state == "new") {
428 reply("There is no game in progress.")
430 say(FROM " ends the game")
436 if (sp_state == "new") {
437 reply("There is no game in progress")
439 else if (sp_state == "play") {
440 reply("The game has already started")
442 else if (sp_state == "join" && sp_from in sp_players) {
443 reply("You are already playing")
445 else if (sp_state == "join") {
449 sp_auths[AUTH] = FROM
451 say(FROM " joins the game!")
453 if (sp_state == "join" && sp_turn == 0)
458 _who = $2 in USERS ? USERS[$2]["auth"] : ""
459 _str = _who && _who != $2 ? $2 " (" _who ")" : $2
460 if (sp_state ~ "new|join") {
461 reply("The game has not yet started")
463 else if (!(sp_from in sp_players)) {
464 reply("You are not playing")
467 reply(_str " is not logged in")
469 else if (_who in sp_players || _who in sp_auths) {
470 reply(_str " is a primary player")
472 else if (_who in sp_share) {
473 reply(_str " is already playing for " sp_share[_who])
476 reply(_str " can now play for " sp_from)
477 sp_share[_who] = sp_from
482 _who = $2 in USERS ? USERS[$2]["auth"] : $2
483 _str = _who && _who != $2 ? $2 " (" _who ")" : $2
484 if (sp_state ~ "new|join") {
485 reply("The game has not yet started")
487 else if (!(sp_from in sp_players)) {
488 reply("You are not playing")
490 else if (_who in sp_players || _who in sp_auths) {
491 reply(_str " is a primary player")
493 else if (!(_who in sp_share) || sp_share[_who] != sp_from) {
494 reply(_str " is not playing for " sp_from)
497 reply(_str " can no longer play for " sp_from)
498 delete sp_share[_who]
502 sp_state ~ "(bid|pass|play)" &&
505 say(_i " can play for " sp_share[_i]);
509 (sp_state == "bid" || sp_state == "play") &&
511 if (sp_from in sp_players)
512 say(".slap " FROM ", it is not your turn.")
514 say(".slap " FROM ", you are not playing.")
520 if ($2 < 0 || $2 > 13) {
521 say("You can only bid from 0 to 13")
525 if ($2 == 0 && !sp_looked[i]) {
526 say(FROM " goes blind nil!")
528 } else if ($2 == 0) {
529 say(FROM " goes nil!")
534 if (sp_turn != sp_dealer) {
535 say("Bidding goes to " sp_player "!")
537 for (p in sp_players)
538 say(p, "You have: " sp_hand(p, p))
540 for (i=0; i<2; i++) {
541 if (sp_nil[i] == 2 || sp_nil[i+2] == 2) {
542 say(sp_team(i) ": select a card to pass " \
543 "(/msg " NICK " .pass <card>)")
547 if (sp_state == "play")
548 say("Play starts with " sp_player "!")
553 sp_state == "pass" &&
556 _team = sp_from in sp_players ? sp_players[sp_from] % 2 : 0
558 # check validity and pass
559 if (!(sp_from in sp_players)) {
560 say(".slap " FROM ", you are not playing.")
562 else if (sp_nil[_team] != 2 && sp_nil[_team+2] != 2) {
563 reply("Your team did not go blind")
565 else if (sp_pass[sp_players[sp_from]]) {
566 reply("You have already passed a card")
568 else if (!(_card in sp_deck)) {
569 reply("Invalid card")
571 else if (!(_card in sp_hands[sp_from])) {
572 reply("You do not have that card")
575 sp_pass[sp_players[sp_from]] = $2
576 say(sp_channel, FROM " passes a card")
579 # check for end of passing
580 if (((sp_nil[0] != 2 && sp_nil[2] != 2) || (sp_pass[0] && sp_pass[2])) &&
581 ((sp_nil[1] != 2 && sp_nil[3] != 2) || (sp_pass[1] && sp_pass[3]))) {
585 delete sp_hands[sp_order[i]][_card]
586 sp_hands[sp_order[_partner]][_card] = 1
588 say(sp_channel, "Cards have been passed, play starts with " sp_player "!")
589 for (p in sp_players)
590 say(p, "You have: " sp_hand(p, p))
595 sp_state ~ "(bid|pass|play)" &&
597 if (!(sp_from in sp_players)) {
598 say(".slap " FROM ", you are not playing.")
600 sp_looked[sp_players[sp_from]] = 1
601 say(FROM, "You have: " sp_hand(FROM, sp_from))
606 sp_state == "play" &&
609 gsub(/[^A-Za-z0-9]/, "", _card);
610 if (!(_card in sp_deck)) {
611 reply("Invalid card")
613 else if (!(_card in sp_hands[sp_from])) {
614 reply("You do not have that card")
616 else if (sp_suit && _card !~ sp_suit && sp_hasa(sp_from, sp_suit)) {
617 reply("You must follow suit (" sp_suit ")")
619 else if (_card ~ /s/ && length(sp_hands[sp_from]) == 13 && sp_hasa(sp_from, "[^s]$")) {
620 reply("You cannot trump on the first hand")
622 else if (_card ~ /s/ && length(sp_pile) == 0 && sp_hasa(sp_from, "[^s]$") && !sp_broken) {
623 reply("Spades have not been broken")
627 if (sp_state == "play") {
628 if (length(sp_hands[sp_from]))
629 say(FROM, "You have: " sp_hand(FROM, sp_from))
631 say(sp_player ": it is your turn! " \
632 "(" sp_pretty(sp_piles, sp_player) ")")
634 say(sp_player ": it is your turn!")
639 /^\.turn/ && sp_state ~ "(bid|pass|play)" {
641 _pile = sp_pretty(sp_piles, FROM)
642 if (sp_state == "bid" && !_bids)
643 say("It is " sp_player "'s bid!")
644 if (sp_state == "bid" && _bids)
645 say("It is " sp_player "'s bid! (" _bids ")")
646 if (sp_state == "play" && !_pile)
647 say("It is " sp_player "'s turn!")
648 if (sp_state == "play" && _pile)
649 say("It is " sp_player "'s turn! (" _pile ")")
650 for (_i=0; sp_state == "pass" && _i<4; _i++)
651 if ((sp_nil[_i%2+0]==2 || sp_nil[_i%2+2]==2) && !sp_pass[_i])
652 say("Waiting for " sp_order[_i] " to pass a card!")
655 /^\.bids$/ && sp_state ~ "(pass|play)" {
656 say(sp_order[0] " bid " sp_bids[0] ", " \
657 sp_order[2] " bid " sp_bids[2] ", " \
658 "total: " sp_bids[0] + sp_bids[2])
659 say(sp_order[1] " bid " sp_bids[1] ", " \
660 sp_order[3] " bid " sp_bids[3] ", " \
661 "total: " sp_bids[1] + sp_bids[3])
664 /^\.tricks$/ && sp_state == "play" {
665 say(sp_order[0] " took " int(sp_tricks[0]) "/" int(sp_bids[0]) ", " \
666 sp_order[2] " took " int(sp_tricks[2]) "/" int(sp_bids[2]))
667 say(sp_order[1] " took " int(sp_tricks[1]) "/" int(sp_bids[1]) ", " \
668 sp_order[3] " took " int(sp_tricks[3]) "/" int(sp_bids[3]))
671 (TO == NICK || DST == sp_channel) &&
672 /^\.(score|status)$/ {
673 if (sp_state == "new") {
674 say("There is no game in progress")
676 if (sp_state ~ "join|bid|pass|play") {
678 sp_playto " points, " \
681 if (sp_state == "join") {
682 say("Waiting for players: " \
683 sp_order[0] " " sp_order[1] " " \
684 sp_order[2] " " sp_order[3])
686 if (sp_state ~ "bid|pass|play") {
687 say(sp_team(0) ": " \
688 int(sp_scores[0]) " points, " \
689 int(sp_bags(0)) " bags")
690 say(sp_team(1) ": " \
691 int(sp_scores[1]) " points, " \
692 int(sp_bags(1)) " bags")
696 /^\.((new|end|load)game|join|look|bid|play)/ {
697 sp_save("var/sp_cur.json");
701 #/^\.playfor [^ ]*$/ {
704 #/^\.standin [^ ]*$/ {
705 # if (p in sp_players) {
707 # for (p in sp_standin) {
708 # if ($2 in sp_standin)
709 # say(here " is already playing for " sp_standin[p]);
711 # sp_standin[away] = here