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(sp_player ": you bid first!")
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)
301 if (sp_scores[0] > sp_scores[1])
302 say(sp_team(0) " lead " sp_scores[0] " to " sp_scores[1] " of " sp_playto)
303 else if (sp_scores[1] > sp_scores[0])
304 say(sp_team(1) " lead " sp_scores[1] " to " sp_scores[0] " of " sp_playto)
306 say("tied at " sp_scores[0])
309 function sp_play(card, winner, pi)
311 delete sp_hands[sp_from][card]
312 sp_pile[card] = sp_player
313 sp_piles = sp_piles (sp_piles?",":"") card
320 if (length(sp_pile) == 1)
321 sp_suit = sp_type(card)
324 if (length(sp_pile) == 4) {
326 pi = sp_players[sp_pile[winner]]
328 say(sp_pile[winner] " wins with " sp_pretty(winner, FROM) \
329 " (" sp_pretty(sp_piles, FROM) ")")
330 sp_next(sp_pile[winner])
335 if (sp_tricks[0] + sp_tricks[1] + \
336 sp_tricks[2] + sp_tricks[3] == 13) {
339 if (sp_scores[0] >= sp_playto || sp_scores[1] >= sp_playto &&
340 sp_scores[0] != sp_scores[1]) {
342 winner = sp_scores[0] > sp_scores[1] ? 0 : 1
344 say(CHANNEL, sp_team(winner) " wins the game " \
345 sp_scores[winner] " to " sp_scores[looser])
346 say(CHANNEL, sp_order[winner+0] "++")
347 say(CHANNEL, sp_order[winner+2] "++")
351 if (sp_scores[0] == sp_scores[1] &&
352 sp_scores[0] >= sp_playto)
353 say("It's tie! Playing an extra round!");
362 cmd = "od -An -N4 -td4 /dev/random"
368 sp_load("var/sp_cur.json");
370 # say(sp_channel, "Game restored.")
374 sp_from = AUTH in sp_auths ? sp_auths[AUTH] : \
375 AUTH in sp_share ? sp_share[AUTH] : FROM
376 sp_valid = sp_from && sp_from == sp_player
382 say("Spades! " sp_pretty("As,Ah,Ad,Ac", FROM))
387 sp_save("var/sp_save.json");
393 sp_load("var/sp_save.json");
399 say(".help spades -- play a game of spades")
402 /^\.help [Ss]pades$/ {
403 say("Spades -- play a game of spades")
404 say(".help game -- setup and administer the game")
405 say(".help play -- commands for playing spades")
406 say(".help auth -- control player authorization")
411 say(".newgame [score] -- start a game to <score> points, default 500")
412 say(".endgame -- abort the current game")
413 say(".savegame -- save the current game to disk")
414 say(".loadgame -- load the previously saved game")
419 say(".join -- join the current game")
420 say(".look -- look at your cards")
421 say(".bid [n] -- bid for <n> tricks")
422 say(".pass [card] -- pass a card to your partner")
423 say(".play [card] -- play a card")
424 say(".turn -- check whose turn it is")
425 say(".bids -- check what everyone bid")
426 say(".tricks -- check how many trick have been taken")
427 say(".score -- check the score")
432 say(".auth [who] -- display authentication info for a user")
433 say(".allow [who] -- allow another person to play on your behalf")
434 say(".deny [who] -- prevent a previously allowed user from playing")
435 say(".show -- display which users can play for which players")
441 /^\.deal (\w+) (.*)/ {
442 say(sp_channel, FROM " is cheating for " $2)
444 for (i=3; i<=NF; i++)
450 /^\.order (\w+) ([0-4])/ {
451 say(sp_channel, FROM " is cheating for " $2)
454 sp_player = sp_order[sp_turn]
458 sp_state == "play" &&
459 /^\.force (\w+) (\S+)$/ {
460 say(sp_channel, FROM " is cheating for " $2)
468 match($0, /^\.newgame ?([1-9][0-9]*) *- *([1-9][0-9]*)$/, _arr) {
469 if (_arr[2] > _arr[1])
470 $0 = $1 " " int(rand() * (_arr[2]-_arr[1])+_arr[1])
473 /^\.newgame ?([1-9][0-9]*)?$/ {
474 if (sp_state != "new") {
475 reply("There is already a game in progress.")
479 sp_playto = $2 ? $2 : 200
480 sp_limit = sp_playto > 200 ? 10 : 5;
483 say(sp_owner " starts a game of Spades to " sp_playto " with " sp_limit " bags!")
487 (sp_from == sp_owner || AUTH == OWNER) &&
489 if (sp_state == "new") {
490 reply("There is no game in progress.")
492 say(FROM " ends the game")
498 if (sp_state == "new") {
499 reply("There is no game in progress")
501 else if (sp_state == "play") {
502 reply("The game has already started")
504 else if (sp_state == "join" && sp_from in sp_players) {
505 reply("You are already playing")
507 else if (sp_state == "join") {
511 sp_auths[AUTH] = FROM
513 say(FROM " joins the game!")
515 if (sp_state == "join" && sp_turn == 0) {
522 _who = $2 in USERS ? USERS[$2]["auth"] : ""
523 _str = _who && _who != $2 ? $2 " (" _who ")" : $2
524 if (sp_state ~ "new|join") {
525 reply("The game has not yet started")
527 else if (!(sp_from in sp_players)) {
528 reply("You are not playing")
531 reply(_str " is not logged in")
533 else if (_who in sp_players || _who in sp_auths) {
534 reply(_str " is a primary player")
536 else if (_who in sp_share) {
537 reply(_str " is already playing for " sp_share[_who])
540 reply(_str " can now play for " sp_from)
541 sp_share[_who] = sp_from
546 _who = $2 in USERS ? USERS[$2]["auth"] : $2
547 _str = _who && _who != $2 ? $2 " (" _who ")" : $2
548 if (sp_state ~ "new|join") {
549 reply("The game has not yet started")
551 else if (!(sp_from in sp_players)) {
552 reply("You are not playing")
554 else if (_who in sp_players || _who in sp_auths) {
555 reply(_str " is a primary player")
557 else if (!(_who in sp_share) || sp_share[_who] != sp_from) {
558 reply(_str " is not playing for " sp_from)
561 reply(_str " can no longer play for " sp_from)
562 delete sp_share[_who]
566 sp_state ~ "(bid|pass|play)" &&
570 _lines[sp_share[_i]] = _lines[sp_share[_i]] " " _i
572 say(_i " allowed:" _lines[_i])
576 (sp_state == "bid" || sp_state == "play") &&
578 if (sp_from in sp_players)
579 say(".slap " FROM ", it is not your turn.")
581 say(".slap " FROM ", you are not playing.")
586 /^\.bid (0|[1-9][0-9]*)$/ {
587 if ($2 < 0 || $2 > 13) {
588 reply("You can only bid from 0 to 13")
592 if ($2 == 0 && !sp_looked[i]) {
593 say(FROM " goes blind nil!")
595 } else if ($2 == 0) {
596 say(FROM " goes nil!")
601 if (sp_turn != sp_dealer) {
602 say(sp_player ": it is your bid! (" sp_bidders() ")")
604 for (p in sp_players)
605 say(p, "You have: " sp_hand(p, p))
607 for (i=0; i<2; i++) {
609 say(sp_team(i) ": select a card to pass " \
610 "(/msg " NICK " .pass <card>)")
614 if (sp_state == "play")
615 say(sp_player ": you have the opening lead!")
620 sp_state == "pass" &&
623 _team = sp_from in sp_players ? sp_players[sp_from] % 2 : 0
625 # check validity and pass
626 if (!(sp_from in sp_players)) {
627 say(".slap " FROM ", you are not playing.")
629 else if (!sp_passer(_team)) {
630 reply("Your team did not go blind")
632 else if (sp_pass[sp_players[sp_from]]) {
633 reply("You have already passed a card")
635 else if (!(_card in sp_deck)) {
636 reply("Invalid card")
638 else if (!(_card in sp_hands[sp_from])) {
639 reply("You do not have that card")
642 sp_pass[sp_players[sp_from]] = $2
643 say(sp_channel, FROM " passes a card")
646 # check for end of passing
647 if ((!sp_passer(0) || (sp_pass[0] && sp_pass[2])) &&
648 (!sp_passer(1) || (sp_pass[1] && sp_pass[3]))) {
652 delete sp_hands[sp_order[i]][_card]
653 sp_hands[sp_order[_partner]][_card] = 1
655 say(sp_channel, "Cards have been passed!")
656 say(sp_channel, sp_player ": you have the opening lead!")
657 for (p in sp_players)
658 say(p, "You have: " sp_hand(p, p))
663 sp_state ~ "(bid|pass|play)" &&
665 if (!(sp_from in sp_players)) {
666 say(".slap " FROM ", you are not playing.")
668 sp_looked[sp_players[sp_from]] = 1
669 say(FROM, "You have: " sp_hand(FROM, sp_from))
674 sp_state == "play" &&
677 gsub(/[^A-Za-z0-9]/, "", _card);
678 if (!(_card in sp_deck)) {
679 reply("Invalid card")
681 else if (!(_card in sp_hands[sp_from])) {
682 reply("You do not have that card")
684 else if (sp_suit && _card !~ sp_suit && sp_hasa(sp_from, sp_suit)) {
685 reply("You must follow suit (" sp_suit ")")
687 else if (_card ~ /s/ && length(sp_hands[sp_from]) == 13 && sp_hasa(sp_from, "[^s]$")) {
688 reply("You cannot trump on the first hand")
690 else if (_card ~ /s/ && length(sp_pile) == 0 && sp_hasa(sp_from, "[^s]$") && !sp_broken) {
691 reply("Spades have not been broken")
695 if (sp_state == "play") {
696 if (length(sp_hands[sp_from]))
697 say(FROM, "You have: " sp_hand(FROM, sp_from))
699 say(sp_player ": it is your turn! " \
700 "(" sp_pretty(sp_piles, sp_player) ")")
702 say(sp_player ": it is your turn!")
707 /^\.bids/ && sp_state == "bid" ||
708 /^\.turn/ && sp_state ~ "(bid|pass|play)" {
710 _pile = sp_pretty(sp_piles, FROM)
714 if (/!/ && sp_share[_i] == sp_player)
715 _extra = _extra " " _i "!"
717 if (sp_state == "bid" && !_bids)
718 say("It is " sp_player "'s bid!" _extra)
719 if (sp_state == "bid" && _bids)
720 say("It is " sp_player "'s bid!" _extra " (" _bids ")")
721 if (sp_state == "play" && !_pile)
722 say("It is " sp_player "'s turn!" _extra)
723 if (sp_state == "play" && _pile)
724 say("It is " sp_player "'s turn!" _extra " (" _pile ")")
726 for (_i=0; sp_state == "pass" && _i<4; _i++)
727 if (sp_passer(_i) && !sp_pass[_i])
728 say("Waiting for " sp_order[_i] " to pass a card!")
731 /^\.bids$/ && sp_state ~ "(pass|play)" {
732 say(sp_order[0] " bid " sp_bid(0) ", " \
733 sp_order[2] " bid " sp_bid(2) ", " \
734 "total: " sp_bids[0] + sp_bids[2])
735 say(sp_order[1] " bid " sp_bid(1) ", " \
736 sp_order[3] " bid " sp_bid(3) ", " \
737 "total: " sp_bids[1] + sp_bids[3])
740 /^\.tricks$/ && sp_state == "play" {
741 say(sp_order[0] " took " int(sp_tricks[0]) "/" sp_bid(0) ", " \
742 sp_order[2] " took " int(sp_tricks[2]) "/" sp_bid(2))
743 say(sp_order[1] " took " int(sp_tricks[1]) "/" sp_bid(1) ", " \
744 sp_order[3] " took " int(sp_tricks[3]) "/" sp_bid(3))
747 (TO == NICK || DST == sp_channel) &&
748 /^\.(score|status)$/ {
749 if (sp_state == "new") {
750 say("There is no game in progress")
752 if (sp_state ~ "join|bid|pass|play") {
754 sp_playto " points, " \
757 if (sp_state == "join") {
758 say("Waiting for players: " \
759 sp_order[0] " " sp_order[1] " " \
760 sp_order[2] " " sp_order[3])
762 if (sp_state ~ "bid|pass|play") {
763 say(sp_team(0) ": " \
764 int(sp_scores[0]) " points, " \
765 int(sp_bags(0)) " bags")
766 say(sp_team(1) ": " \
767 int(sp_scores[1]) " points, " \
768 int(sp_bags(1)) " bags")
772 /^\.((new|end|load)game|join|look|bid|pass|play)/ {
773 sp_save("var/sp_cur.json");