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,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
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_order # [i] Player order order[i] -> "name"
57 delete sp_scores # [i] Teams score
61 function sp_acopy(dst, src, key)
65 json_copy(dst, key, src[key])
68 function sp_save(file, game)
71 game["suit"] = sp_suit;
72 game["piles"] = sp_piles;
73 json_copy(game, "pile", sp_pile);
76 game["state"] = sp_state;
77 game["broken"] = sp_broken;
78 json_copy(game, "looked", sp_looked);
79 json_copy(game, "bids", sp_bids);
80 json_copy(game, "nil", sp_nil);
81 json_copy(game, "pass", sp_pass);
82 json_copy(game, "tricks", sp_tricks);
85 game["channel"] = sp_channel;
86 game["owner"] = sp_owner;
87 game["playto"] = sp_playto;
88 game["dealer"] = sp_dealer;
89 game["turn"] = sp_turn;
90 game["player"] = sp_player;
91 game["limit"] = sp_limit;
92 json_copy(game, "hands", sp_hands);
93 json_copy(game, "players", sp_players);
94 json_copy(game, "auths", sp_auths);
95 json_copy(game, "order", sp_order);
96 json_copy(game, "scores", sp_scores);
99 json_save(file, game);
102 function sp_load(file, game)
105 if (!json_load(file, game))
109 sp_suit = game["suit"];
110 sp_piles = game["piles"];
111 sp_acopy(sp_pile, game["pile"]);
114 sp_state = game["state"];
115 sp_broken = game["broken"];
116 sp_acopy(sp_looked, game["looked"]);
117 sp_acopy(sp_bids, game["bids"]);
118 sp_acopy(sp_nil, game["nil"]);
119 sp_acopy(sp_pass, game["pass"]);
120 sp_acopy(sp_tricks, game["tricks"]);
123 sp_channel = game["channel"];
124 sp_owner = game["owner"];
125 sp_playto = game["playto"];
126 sp_dealer = game["dealer"];
127 sp_turn = game["turn"];
128 sp_player = game["player"];
129 sp_limit = game["limit"];
130 sp_acopy(sp_hands, game["hands"]);
131 sp_acopy(sp_players, game["players"]);
132 sp_acopy(sp_auths, game["auths"]);
133 sp_acopy(sp_order, game["order"]);
134 sp_acopy(sp_scores, game["scores"]);
137 function sp_pretty(cards, who)
140 gsub(/[0-9JQKA]*[sc]/, "\0031,00\002&\017", cards) # black
141 gsub(/[0-9JQKA]*[hd]/, "\0034,00\002&\017", cards) # red
143 if (!nounicode[who]) {
144 gsub(/s/, "\002♠", cards)
145 gsub(/h/, "\002♥", cards)
146 gsub(/d/, "\002♦", cards)
147 gsub(/c/, "\002♣", cards)
152 function sp_next(who, prev)
155 sp_turn = who ? sp_players[who] : (sp_turn + 1) % 4
156 if (length(sp_order) == 4)
157 sp_player = sp_order[sp_turn]
161 function sp_deal( shuf)
163 say("/me deals the cards")
164 asorti(sp_deck, shuf, "sp_usort")
165 for (i=1; i<=52; i++)
166 sp_hands[sp_order[i%4]][shuf[i]] = 1
168 sp_dealer = (sp_dealer+1)%4
170 sp_player = sp_order[sp_turn]
171 say("Bidding starts with " sp_player "!")
174 function sp_hand(to, who, sort, str)
176 asorti(sp_hands[who], sort, "sp_csort")
177 for (i=0; i<length(sort); i++)
178 str = str "" sprintf("%4s", sort[i])
179 gsub(/^ +| +$/, "", str)
180 return sp_pretty(str, to)
183 function sp_hasa(who, expr)
185 for (c in sp_hands[who]) {
192 function sp_type(card)
194 return substr(card, length(card))
197 function sp_usort(a,b,c,d) {
201 function sp_csort(i1,v1,i2,v2) {
202 return sp_deck[i1] > sp_deck[i2] ? +1 :
203 sp_deck[i1] < sp_deck[i2] ? -1 : 0;
206 function sp_winner( card, tmp)
208 for (card in sp_pile)
209 if (card !~ sp_suit && card !~ /s/)
211 asorti(sp_pile, tmp, "sp_csort")
212 #print "pile: " tmp[1] ">" tmp[2] ">" tmp[3] ">" tmp[4]
218 #return "{" sp_order[i+0] "," sp_order[i+2] "}"
219 return sp_order[i+0] "/" sp_order[i+2]
222 function sp_bags(i, bags)
224 bags = sp_scores[i] % sp_limit
230 function sp_bidders( i, turn, bid, bids)
232 for (i = 0; i < 4; i++) {
233 turn = (sp_dealer + i) % 4
234 if (sp_bids[turn] && !sp_nil[turn])
235 bid = sp_order[turn] ":" sp_bids[turn]
236 else if (sp_nil[turn] == 1)
237 bid = sp_order[turn] ":" "nil"
238 else if (sp_nil[turn] == 2)
239 bid = sp_order[turn] ":" "blind"
244 gsub(/^ +| +$/, "", bids)
248 function sp_score( bids, times, tricks)
250 for (i=0; i<2; i++) {
251 bids = sp_bids[i] + sp_bids[i+2]
252 tricks = sp_tricks[i] + sp_tricks[i+2]
254 times = int((sp_bags(i) + bags) / sp_limit)
256 say(sp_team(i) " bag" (times>1?" way ":" ") "out")
257 sp_scores[i] -= sp_limit * 10 * times;
259 if (tricks >= bids) {
260 say(sp_team(i) " make their bid: " tricks "/" bids)
261 sp_scores[i] += bids*10 + bags;
263 say(sp_team(i) " go bust: " tricks "/" bids)
264 sp_scores[i] -= bids*10;
267 for (i=0; i<4; i++) {
270 say(sp_order[i] " " \
271 (sp_nil[i] == 1 && !sp_tricks[i] ? "makes nil!" :
272 sp_nil[i] == 1 && sp_tricks[i] ? "fails at nil!" :
273 sp_nil[i] == 2 && !sp_tricks[i] ? "makes blind nil!" :
274 sp_nil[i] == 2 && sp_tricks[i] ? "fails miserably at blind nil!" :
276 sp_scores[i%2] += 100 * sp_nil[i] * \
277 (sp_tricks[i] == 0 ? 1 : -1)
281 function sp_play(card, winner, pi)
283 delete sp_hands[sp_from][card]
284 sp_pile[card] = sp_player
285 sp_piles = sp_piles (sp_piles?",":"") card
292 if (length(sp_pile) == 1)
293 sp_suit = sp_type(card)
296 if (length(sp_pile) == 4) {
298 pi = sp_players[sp_pile[winner]]
300 say(sp_pile[winner] " wins with " sp_pretty(winner, FROM) \
301 " (" sp_pretty(sp_piles, FROM) ")")
302 sp_next(sp_pile[winner])
307 if (sp_tricks[0] + sp_tricks[1] + \
308 sp_tricks[2] + sp_tricks[3] == 13) {
311 if (sp_scores[0] >= sp_playto || sp_scores[1] >= sp_playto &&
312 sp_scores[0] != sp_scores[1]) {
314 winner = sp_scores[0] > sp_scores[1] ? 0 : 1
316 say(CHANNEL, sp_team(winner) " wins the game " \
317 sp_scores[winner] " to " sp_scores[looser])
318 say(CHANNEL, sp_order[winner+0] "++")
319 say(CHANNEL, sp_order[winner+2] "++")
323 if (sp_scores[0] == sp_scores[1] &&
324 sp_scores[0] >= sp_playto)
325 say("It's tie! Playing an extra round!");
334 cmd = "od -An -N4 -td4 /dev/random"
340 sp_load("var/sp_cur.json");
342 # say(sp_channel, "Game restored.")
346 sp_from = AUTH in sp_auths ? sp_auths[AUTH] : FROM
347 sp_valid = sp_from && sp_from == sp_player
353 say("Spades! " sp_pretty("As,Ah,Ad,Ac", FROM))
358 sp_save("var/sp_save.json");
364 sp_load("var/sp_save.json");
369 /^\.help [Ss]pades$/ {
370 say("Spades -- play a game of spades")
372 say(".newgame [score] -- start a game to <score> points, default 500")
373 say(".endgame -- abort the current game")
374 say(".savegame -- save the current game to disk")
375 say(".loadgame -- load the previously saved game")
376 say(".join -- join the current game")
377 say(".look -- look at your cards")
378 say(".bid n -- bid for <n> tricks")
379 say(".play [card] -- play a card")
380 say(".score -- check the score")
381 say(".tricks -- check how many trick have been taken")
382 say(".bids -- check what everyone bid")
388 /^\.deal (\w+) (.*)/ {
389 say(sp_channel, FROM " is cheating for " $2)
391 for (i=3; i<=NF; i++)
397 sp_state == "play" &&
398 /^\.force (\w+) (\S+)$/ {
399 say(sp_channel, FROM " is cheating for " $2)
407 /^\.newgame ?([0-9]+)?/ {
408 if (sp_state != "new") {
409 reply("There is already a game in progress.")
413 sp_playto = $2 ? $2 : 200
414 sp_limit = sp_playto > 200 ? 10 : 5;
417 say(sp_owner " starts a game of Spades to " sp_playto " with " sp_limit " bags!")
421 (sp_from == sp_owner || AUTH == OWNER) &&
423 if (sp_state == "new") {
424 reply("There is no game in progress.")
426 say(FROM " ends the game")
432 if (sp_state == "new") {
433 reply("There is no game in progress")
435 else if (sp_state == "play") {
436 reply("The game has already started")
438 else if (sp_state == "join" && sp_from in sp_players) {
439 reply("You are already playing")
441 else if (sp_state == "join") {
445 sp_auths[AUTH] = FROM
447 say(FROM " joins the game!")
449 if (sp_state == "join" && sp_turn == 0)
454 (sp_state "bid" || sp_state == "play") &&
456 if (sp_from in sp_players)
457 say(".slap " FROM ", it is not your turn.")
459 say(".slap " FROM ", you are not playing.")
465 if ($2 < 0 || $2 > 13) {
466 say("You can only bid from 0 to 13")
470 if ($2 == 0 && !sp_looked[i]) {
471 say(FROM " goes blind nil!")
473 } else if ($2 == 0) {
474 say(FROM " goes nil!")
479 if (sp_turn != sp_dealer) {
480 say("Bidding goes to " sp_player "!")
482 for (p in sp_players)
483 say(p, "You have: " sp_hand(p, p))
485 for (i=0; i<2; i++) {
486 if (sp_nil[i] == 2 || sp_nil[i+2] == 2) {
487 say(sp_team(i) ": select a card to pass " \
488 "(/msg " NICK " .pass <card>)")
492 if (sp_state == "play")
493 say("Play starts with " sp_player "!")
498 sp_state == "pass" &&
501 _team = sp_from in sp_players ? sp_players[sp_from] % 2 : 0
503 # check validity and pass
504 if (!(sp_from in sp_players)) {
505 say(".slap " FROM ", you are not playing.")
507 else if (sp_nil[_team] != 2 && sp_nil[_team+2] != 2) {
508 reply("Your team did not go blind")
510 else if (sp_pass[sp_players[sp_from]]) {
511 reply("You have already passed a card")
513 else if (!(_card in sp_deck)) {
514 reply("Invalid card")
516 else if (!(_card in sp_hands[sp_from])) {
517 reply("You do not have that card")
520 sp_pass[sp_players[sp_from]] = $2
521 say(sp_channel, FROM " passes a card")
524 # check for end of passing
525 if (((sp_nil[0] != 2 && sp_nil[2] != 2) || (sp_pass[0] && sp_pass[2])) &&
526 ((sp_nil[1] != 2 && sp_nil[3] != 2) || (sp_pass[1] && sp_pass[3]))) {
530 delete sp_hands[sp_order[i]][_card]
531 sp_hands[sp_order[_partner]][_card] = 1
533 say(sp_channel, "Cards have been passed, play starts with " sp_player "!")
534 for (p in sp_players)
535 say(p, "You have: " sp_hand(p, p))
540 sp_state ~ "(bid|pass|play)" &&
542 if (!(sp_from in sp_players)) {
543 say(".slap " FROM ", you are not playing.")
545 sp_looked[sp_players[sp_from]] = 1
546 say(FROM, "You have: " sp_hand(FROM, sp_from))
551 sp_state == "play" &&
554 gsub(/[^A-Za-z0-9]/, "", _card);
555 if (!(_card in sp_deck)) {
556 reply("Invalid card")
558 else if (!(_card in sp_hands[sp_from])) {
559 reply("You do not have that card")
561 else if (sp_suit && _card !~ sp_suit && sp_hasa(sp_from, sp_suit)) {
562 reply("You must follow suit (" sp_suit ")")
564 else if (_card ~ /s/ && length(sp_hands[sp_from]) == 13 && sp_hasa(sp_from, "[^s]$")) {
565 reply("You cannot trump on the first hand")
567 else if (_card ~ /s/ && length(sp_pile) == 0 && sp_hasa(sp_from, "[^s]$") && !sp_broken) {
568 reply("Spades have not been broken")
572 if (sp_state == "play") {
573 if (length(sp_hands[sp_from]))
574 say(FROM, "You have: " sp_hand(FROM, sp_from))
576 say(sp_player ": it is your turn! " \
577 "(" sp_pretty(sp_piles, sp_player) ")")
579 say(sp_player ": it is your turn!")
584 /^\.bids$/ && sp_state ~ "(pass|play)" {
585 say(sp_order[0] " bid " sp_bids[0] ", " \
586 sp_order[2] " bid " sp_bids[2] ", " \
587 "total: " sp_bids[0] + sp_bids[2])
588 say(sp_order[1] " bid " sp_bids[1] ", " \
589 sp_order[3] " bid " sp_bids[3] ", " \
590 "total: " sp_bids[1] + sp_bids[3])
593 /^\.tricks$/ && sp_state == "play" {
594 say(sp_order[0] " took " int(sp_tricks[0]) "/" int(sp_bids[0]) ", " \
595 sp_order[2] " took " int(sp_tricks[2]) "/" int(sp_bids[2]))
596 say(sp_order[1] " took " int(sp_tricks[1]) "/" int(sp_bids[1]) ", " \
597 sp_order[3] " took " int(sp_tricks[3]) "/" int(sp_bids[3]))
600 /^\.turn/ && sp_state ~ "(bid|pass|play)" {
602 _pile = sp_pretty(sp_piles, FROM)
603 if (sp_state == "bid" && !_bids)
604 say("It is " sp_player "'s bid!")
605 if (sp_state == "bid" && _bids)
606 say("It is " sp_player "'s bid! (" _bids ")")
607 if (sp_state == "play" && !_pile)
608 say("It is " sp_player "'s turn!")
609 if (sp_state == "play" && _pile)
610 say("It is " sp_player "'s turn! (" _pile ")")
611 for (_i=0; sp_state == "pass" && _i<4; _i++)
612 if ((sp_nil[_i%2+0]==2 || sp_nil[_i%2+2]==2) && !sp_pass[_i])
613 say("Waiting for " sp_order[_i] " to pass a card!")
616 (TO == NICK || DST == sp_channel) &&
617 /^\.(score|status)$/ {
618 if (sp_state == "new") {
619 say("There is no game in progress")
621 if (sp_state == "join") {
622 say("Waiting for players: " \
623 sp_order[0] " " sp_order[1] " " \
624 sp_order[2] " " sp_order[3])
626 if (sp_state ~ "bid|pass|play") {
628 sp_playto " points, " \
630 say(sp_team(0) ": " \
631 int(sp_scores[0]) " points, " \
632 int(sp_bags(0)) " bags")
633 say(sp_team(1) ": " \
634 int(sp_scores[1]) " points, " \
635 int(sp_bags(1)) " bags")
639 /^\.((new|end|load)game|join|look|bid|play)/ {
640 sp_save("var/sp_cur.json");
644 #/^\.playfor [^ ]*$/ {
647 #/^\.standin [^ ]*$/ {
648 # if (p in sp_players) {
650 # for (p in sp_standin) {
651 # if ($2 in sp_standin)
652 # say(here " is already playing for " sp_standin[p]);
654 # sp_standin[away] = here