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("Bidding starts with " sp_player "!")
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)
303 function sp_play(card, winner, pi)
305 delete sp_hands[sp_from][card]
306 sp_pile[card] = sp_player
307 sp_piles = sp_piles (sp_piles?",":"") card
314 if (length(sp_pile) == 1)
315 sp_suit = sp_type(card)
318 if (length(sp_pile) == 4) {
320 pi = sp_players[sp_pile[winner]]
322 say(sp_pile[winner] " wins with " sp_pretty(winner, FROM) \
323 " (" sp_pretty(sp_piles, FROM) ")")
324 sp_next(sp_pile[winner])
329 if (sp_tricks[0] + sp_tricks[1] + \
330 sp_tricks[2] + sp_tricks[3] == 13) {
333 if (sp_scores[0] >= sp_playto || sp_scores[1] >= sp_playto &&
334 sp_scores[0] != sp_scores[1]) {
336 winner = sp_scores[0] > sp_scores[1] ? 0 : 1
338 say(CHANNEL, sp_team(winner) " wins the game " \
339 sp_scores[winner] " to " sp_scores[looser])
340 say(CHANNEL, sp_order[winner+0] "++")
341 say(CHANNEL, sp_order[winner+2] "++")
345 if (sp_scores[0] == sp_scores[1] &&
346 sp_scores[0] >= sp_playto)
347 say("It's tie! Playing an extra round!");
356 cmd = "od -An -N4 -td4 /dev/random"
362 sp_load("var/sp_cur.json");
364 # say(sp_channel, "Game restored.")
368 sp_from = AUTH in sp_auths ? sp_auths[AUTH] : \
369 AUTH in sp_share ? sp_share[AUTH] : FROM
370 sp_valid = sp_from && sp_from == sp_player
376 say("Spades! " sp_pretty("As,Ah,Ad,Ac", FROM))
381 sp_save("var/sp_save.json");
387 sp_load("var/sp_save.json");
392 /^\.help [Ss]pades$/ {
393 say("Spades -- play a game of spades")
394 say(".help game -- setup and administer the game")
395 say(".help play -- commands for playing spades")
396 say(".help auth -- control player authorization")
401 say(".newgame [score] -- start a game to <score> points, default 500")
402 say(".endgame -- abort the current game")
403 say(".savegame -- save the current game to disk")
404 say(".loadgame -- load the previously saved game")
409 say(".join -- join the current game")
410 say(".look -- look at your cards")
411 say(".bid [n] -- bid for <n> tricks")
412 say(".pass [card] -- pass a card to your partner")
413 say(".play [card] -- play a card")
414 say(".turn -- check whose turn it is")
415 say(".bids -- check what everyone bid")
416 say(".tricks -- check how many trick have been taken")
417 say(".score -- check the score")
422 say(".auth [who] -- display authentication info for a user")
423 say(".allow [who] -- allow another person to play on your behalf")
424 say(".deny [who] -- prevent a previously allowed user from playing")
425 say(".show -- display which users can play for which players")
431 /^\.deal (\w+) (.*)/ {
432 say(sp_channel, FROM " is cheating for " $2)
434 for (i=3; i<=NF; i++)
440 /^\.order (\w+) ([0-4])/ {
441 say(sp_channel, FROM " is cheating for " $2)
444 sp_player = sp_order[sp_turn]
448 sp_state == "play" &&
449 /^\.force (\w+) (\S+)$/ {
450 say(sp_channel, FROM " is cheating for " $2)
458 match($0, /^\.newgame ?([0-9]+) *- *([0-9]+)$/, _arr) {
459 if (_arr[2] > _arr[1])
460 $0 = $1 " " int(rand() * (_arr[2]-_arr[1])+_arr[1])
463 /^\.newgame ?([0-9]+)?$/ {
464 if (sp_state != "new") {
465 reply("There is already a game in progress.")
469 sp_playto = $2 ? $2 : 200
470 sp_limit = sp_playto > 200 ? 10 : 5;
473 say(sp_owner " starts a game of Spades to " sp_playto " with " sp_limit " bags!")
477 (sp_from == sp_owner || AUTH == OWNER) &&
479 if (sp_state == "new") {
480 reply("There is no game in progress.")
482 say(FROM " ends the game")
488 if (sp_state == "new") {
489 reply("There is no game in progress")
491 else if (sp_state == "play") {
492 reply("The game has already started")
494 else if (sp_state == "join" && sp_from in sp_players) {
495 reply("You are already playing")
497 else if (sp_state == "join") {
501 sp_auths[AUTH] = FROM
503 say(FROM " joins the game!")
505 if (sp_state == "join" && sp_turn == 0) {
512 _who = $2 in USERS ? USERS[$2]["auth"] : ""
513 _str = _who && _who != $2 ? $2 " (" _who ")" : $2
514 if (sp_state ~ "new|join") {
515 reply("The game has not yet started")
517 else if (!(sp_from in sp_players)) {
518 reply("You are not playing")
521 reply(_str " is not logged in")
523 else if (_who in sp_players || _who in sp_auths) {
524 reply(_str " is a primary player")
526 else if (_who in sp_share) {
527 reply(_str " is already playing for " sp_share[_who])
530 reply(_str " can now play for " sp_from)
531 sp_share[_who] = sp_from
536 _who = $2 in USERS ? USERS[$2]["auth"] : $2
537 _str = _who && _who != $2 ? $2 " (" _who ")" : $2
538 if (sp_state ~ "new|join") {
539 reply("The game has not yet started")
541 else if (!(sp_from in sp_players)) {
542 reply("You are not playing")
544 else if (_who in sp_players || _who in sp_auths) {
545 reply(_str " is a primary player")
547 else if (!(_who in sp_share) || sp_share[_who] != sp_from) {
548 reply(_str " is not playing for " sp_from)
551 reply(_str " can no longer play for " sp_from)
552 delete sp_share[_who]
556 sp_state ~ "(bid|pass|play)" &&
560 _lines[sp_share[_i]] = _lines[sp_share[_i]] " " _i
562 say(_i " allowed:" _lines[_i])
566 (sp_state == "bid" || sp_state == "play") &&
568 if (sp_from in sp_players)
569 say(".slap " FROM ", it is not your turn.")
571 say(".slap " FROM ", you are not playing.")
577 if ($2 < 0 || $2 > 13) {
578 say("You can only bid from 0 to 13")
582 if ($2 == 0 && !sp_looked[i]) {
583 say(FROM " goes blind nil!")
585 } else if ($2 == 0) {
586 say(FROM " goes nil!")
591 if (sp_turn != sp_dealer) {
592 say("Bidding goes to " sp_player "!")
594 for (p in sp_players)
595 say(p, "You have: " sp_hand(p, p))
597 for (i=0; i<2; i++) {
599 say(sp_team(i) ": select a card to pass " \
600 "(/msg " NICK " .pass <card>)")
604 if (sp_state == "play")
605 say("Play starts with " sp_player "!")
610 sp_state == "pass" &&
613 _team = sp_from in sp_players ? sp_players[sp_from] % 2 : 0
615 # check validity and pass
616 if (!(sp_from in sp_players)) {
617 say(".slap " FROM ", you are not playing.")
619 else if (!sp_passer(_team)) {
620 reply("Your team did not go blind")
622 else if (sp_pass[sp_players[sp_from]]) {
623 reply("You have already passed a card")
625 else if (!(_card in sp_deck)) {
626 reply("Invalid card")
628 else if (!(_card in sp_hands[sp_from])) {
629 reply("You do not have that card")
632 sp_pass[sp_players[sp_from]] = $2
633 say(sp_channel, FROM " passes a card")
636 # check for end of passing
637 if ((!sp_passer(0) || (sp_pass[0] && sp_pass[2])) &&
638 (!sp_passer(1) || (sp_pass[1] && sp_pass[3]))) {
642 delete sp_hands[sp_order[i]][_card]
643 sp_hands[sp_order[_partner]][_card] = 1
645 say(sp_channel, "Cards have been passed, play starts with " sp_player "!")
646 for (p in sp_players)
647 say(p, "You have: " sp_hand(p, p))
652 sp_state ~ "(bid|pass|play)" &&
654 if (!(sp_from in sp_players)) {
655 say(".slap " FROM ", you are not playing.")
657 sp_looked[sp_players[sp_from]] = 1
658 say(FROM, "You have: " sp_hand(FROM, sp_from))
663 sp_state == "play" &&
666 gsub(/[^A-Za-z0-9]/, "", _card);
667 if (!(_card in sp_deck)) {
668 reply("Invalid card")
670 else if (!(_card in sp_hands[sp_from])) {
671 reply("You do not have that card")
673 else if (sp_suit && _card !~ sp_suit && sp_hasa(sp_from, sp_suit)) {
674 reply("You must follow suit (" sp_suit ")")
676 else if (_card ~ /s/ && length(sp_hands[sp_from]) == 13 && sp_hasa(sp_from, "[^s]$")) {
677 reply("You cannot trump on the first hand")
679 else if (_card ~ /s/ && length(sp_pile) == 0 && sp_hasa(sp_from, "[^s]$") && !sp_broken) {
680 reply("Spades have not been broken")
684 if (sp_state == "play") {
685 if (length(sp_hands[sp_from]))
686 say(FROM, "You have: " sp_hand(FROM, sp_from))
688 say(sp_player ": it is your turn! " \
689 "(" sp_pretty(sp_piles, sp_player) ")")
691 say(sp_player ": it is your turn!")
696 /^\.bids/ && sp_state == "bid" ||
697 /^\.turn/ && sp_state ~ "(bid|pass|play)" {
699 _pile = sp_pretty(sp_piles, FROM)
700 if (sp_state == "bid" && !_bids)
701 say("It is " sp_player "'s bid!")
702 if (sp_state == "bid" && _bids)
703 say("It is " sp_player "'s bid! (" _bids ")")
704 if (sp_state == "play" && !_pile)
705 say("It is " sp_player "'s turn!")
706 if (sp_state == "play" && _pile)
707 say("It is " sp_player "'s turn! (" _pile ")")
708 for (_i=0; sp_state == "pass" && _i<4; _i++)
709 if (sp_passer(_i) && !sp_pass[_i])
710 say("Waiting for " sp_order[_i] " to pass a card!")
713 /^\.bids$/ && sp_state ~ "(pass|play)" {
714 say(sp_order[0] " bid " sp_bid(0) ", " \
715 sp_order[2] " bid " sp_bid(2) ", " \
716 "total: " sp_bids[0] + sp_bids[2])
717 say(sp_order[1] " bid " sp_bid(1) ", " \
718 sp_order[3] " bid " sp_bid(3) ", " \
719 "total: " sp_bids[1] + sp_bids[3])
722 /^\.tricks$/ && sp_state == "play" {
723 say(sp_order[0] " took " int(sp_tricks[0]) "/" sp_bid(0) ", " \
724 sp_order[2] " took " int(sp_tricks[2]) "/" sp_bid(2))
725 say(sp_order[1] " took " int(sp_tricks[1]) "/" sp_bid(1) ", " \
726 sp_order[3] " took " int(sp_tricks[3]) "/" sp_bid(3))
729 (TO == NICK || DST == sp_channel) &&
730 /^\.(score|status)$/ {
731 if (sp_state == "new") {
732 say("There is no game in progress")
734 if (sp_state ~ "join|bid|pass|play") {
736 sp_playto " points, " \
739 if (sp_state == "join") {
740 say("Waiting for players: " \
741 sp_order[0] " " sp_order[1] " " \
742 sp_order[2] " " sp_order[3])
744 if (sp_state ~ "bid|pass|play") {
745 say(sp_team(0) ": " \
746 int(sp_scores[0]) " points, " \
747 int(sp_bags(0)) " bags")
748 say(sp_team(1) ": " \
749 int(sp_scores[1]) " points, " \
750 int(sp_bags(1)) " bags")
754 /^\.((new|end|load)game|join|look|bid|pass|play)/ {
755 sp_save("var/sp_cur.json");