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(sp_team(winner) " wins the game " \
317 sp_scores[winner] " to " sp_scores[looser])
318 say(sp_order[winner+0] "++")
319 say(sp_order[winner+2] "++")
320 say(sp_order[looser+0] "--")
321 say(sp_order[looser+2] "--")
325 if (sp_scores[0] == sp_scores[1] &&
326 sp_scores[0] >= sp_playto)
327 say("It's tie! Playing an extra round!");
336 cmd = "od -An -N4 -td4 /dev/random"
342 sp_load("var/sp_cur.json");
344 # say(sp_channel, "Game restored.")
348 sp_from = AUTH in sp_auths ? sp_auths[AUTH] : FROM
349 sp_valid = sp_from && sp_from == sp_player
355 say("Spades! " sp_pretty("As,Ah,Ad,Ac", FROM))
360 sp_save("var/sp_save.json");
366 sp_load("var/sp_save.json");
371 /^\.help [Ss]pades$/ {
372 say("Spades -- play a game of spades")
374 say(".newgame [score] -- start a game to <score> points, default 500")
375 say(".endgame -- abort the current game")
376 say(".savegame -- save the current game to disk")
377 say(".loadgame -- load the previously saved game")
378 say(".join -- join the current game")
379 say(".look -- look at your cards")
380 say(".bid n -- bid for <n> tricks")
381 say(".play [card] -- play a card")
382 say(".score -- check the score")
383 say(".tricks -- check how many trick have been taken")
384 say(".bids -- check what everyone bid")
390 /^\.deal (\w+) (.*)/ {
391 say(sp_channel, FROM " is cheating for " $2)
393 for (i=3; i<=NF; i++)
399 sp_state == "play" &&
400 /^\.play (\w+) (\S+)$/ {
401 say(sp_channel, FROM " is cheating for " $2)
409 /^\.newgame ?([0-9]+)?/ {
410 if (sp_state != "new") {
411 reply("There is already a game in progress.")
415 sp_playto = $2 ? $2 : 200
416 sp_limit = sp_playto > 200 ? 10 : 5;
419 say(sp_owner " starts a game of Spades to " sp_playto " with " sp_limit " bags!")
423 (sp_from == sp_owner || AUTH == OWNER) &&
425 if (sp_state == "new") {
426 reply("There is no game in progress.")
428 say(FROM " ends the game")
434 if (sp_state == "new") {
435 reply("There is no game in progress")
437 else if (sp_state == "play") {
438 reply("The game has already started")
440 else if (sp_state == "join" && sp_from in sp_players) {
441 reply("You are already playing")
443 else if (sp_state == "join") {
447 sp_auths[AUTH] = FROM
449 say(FROM " joins the game!")
451 if (sp_state == "join" && sp_turn == 0)
456 (sp_state "bid" || sp_state == "play") &&
458 if (sp_from in sp_players)
459 say(".slap " FROM ", it is not your turn.")
461 say(".slap " FROM ", you are not playing.")
467 if ($2 < 0 || $2 > 13) {
468 say("You can only bid from 0 to 13")
472 if ($2 == 0 && !sp_looked[i]) {
473 say(FROM " goes blind nil!")
475 } else if ($2 == 0) {
476 say(FROM " goes nil!")
481 if (sp_turn != sp_dealer) {
482 say("Bidding goes to " sp_player "!")
484 for (p in sp_players)
485 say(p, "You have: " sp_hand(p, p))
487 for (i=0; i<2; i++) {
488 if (sp_nil[i] == 2 || sp_nil[i+2] == 2) {
489 say(sp_team(i) ": select a card to pass " \
490 "(/msg " NICK " .pass <card>)")
494 if (sp_state == "play")
495 say("Play starts with " sp_player "!")
500 sp_state == "pass" &&
503 _team = sp_from in sp_players ? sp_players[sp_from] % 2 : 0
505 # check validity and pass
506 if (!(sp_from in sp_players)) {
507 say(".slap " FROM ", you are not playing.")
509 else if (sp_nil[_team] != 2 && sp_nil[_team+2] != 2) {
510 reply("Your team did not go blind")
512 else if (sp_pass[sp_players[sp_from]]) {
513 reply("You have already passed a card")
515 else if (!(_card in sp_deck)) {
516 reply("Invalid card")
518 else if (!(_card in sp_hands[sp_from])) {
519 reply("You do not have that card")
522 sp_pass[sp_players[sp_from]] = $2
523 say(sp_channel, FROM " passes a card")
526 # check for end of passing
527 if (((sp_nil[0] != 2 && sp_nil[2] != 2) || (sp_pass[0] && sp_pass[2])) &&
528 ((sp_nil[1] != 2 && sp_nil[3] != 2) || (sp_pass[1] && sp_pass[3]))) {
532 delete sp_hands[sp_order[i]][_card]
533 sp_hands[sp_order[_partner]][_card] = 1
535 say(sp_channel, "Cards have been passed, play starts with " sp_player "!")
536 for (p in sp_players)
537 say(p, "You have: " sp_hand(p, p))
542 sp_state ~ "(bid|pass|play)" &&
544 if (!(sp_from in sp_players)) {
545 say(".slap " FROM ", you are not playing.")
547 sp_looked[sp_players[sp_from]] = 1
548 say(FROM, "You have: " sp_hand(FROM, sp_from))
553 sp_state == "play" &&
556 if (!(_card in sp_deck)) {
557 reply("Invalid card")
559 else if (!(_card in sp_hands[sp_from])) {
560 reply("You do not have that card")
562 else if (sp_suit && _card !~ sp_suit && sp_hasa(sp_from, sp_suit)) {
563 reply("You must follow suit (" sp_suit ")")
565 else if (_card ~ /s/ && length(sp_hands[sp_from]) == 13 && sp_hasa(sp_from, "[^s]$")) {
566 reply("You cannot trump on the first hand")
568 else if (_card ~ /s/ && length(sp_pile) == 0 && sp_hasa(sp_from, "[^s]$") && !sp_broken) {
569 reply("Spades have not been broken")
573 if (sp_state == "play") {
574 if (length(sp_hands[sp_from]))
575 say(FROM, "You have: " sp_hand(FROM, sp_from))
577 say(sp_player ": it is your turn! " \
578 "(" sp_pretty(sp_piles, sp_player) ")")
580 say(sp_player ": it is your turn!")
585 /^\.bids$/ && sp_state ~ "(pass|play)" {
586 say(sp_order[0] " bid " sp_bids[0] ", " \
587 sp_order[2] " bid " sp_bids[2] ", " \
588 "total: " sp_bids[0] + sp_bids[2])
589 say(sp_order[1] " bid " sp_bids[1] ", " \
590 sp_order[3] " bid " sp_bids[3] ", " \
591 "total: " sp_bids[1] + sp_bids[3])
594 /^\.tricks$/ && sp_state == "play" {
595 say(sp_order[0] " took " int(sp_tricks[0]) "/" int(sp_bids[0]) ", " \
596 sp_order[2] " took " int(sp_tricks[2]) "/" int(sp_bids[2]))
597 say(sp_order[1] " took " int(sp_tricks[1]) "/" int(sp_bids[1]) ", " \
598 sp_order[3] " took " int(sp_tricks[3]) "/" int(sp_bids[3]))
601 /^\.turn/ && sp_state ~ "(bid|pass|play)" {
603 _pile = sp_pretty(sp_piles, FROM)
604 if (sp_state == "bid" && !_bids)
605 say("It is " sp_player "'s bid!")
606 if (sp_state == "bid" && _bids)
607 say("It is " sp_player "'s bid! (" _bids ")")
608 if (sp_state == "play" && !_pile)
609 say("It is " sp_player "'s turn!")
610 if (sp_state == "play" && _pile)
611 say("It is " sp_player "'s turn! (" _pile ")")
612 for (_i=0; sp_state == "pass" && _i<4; _i++)
613 if ((sp_nil[_i%2+0]==2 || sp_nil[_i%2+2]==2) && !sp_pass[_i])
614 say("Waiting for " sp_order[_i] " to pass a card!")
617 (TO == NICK || DST == sp_channel) &&
618 /^\.(score|status)$/ {
619 if (sp_state == "new") {
620 say("There is no game in progress")
622 if (sp_state == "join") {
623 say("Waiting for players: " \
624 sp_order[0] " " sp_order[1] " " \
625 sp_order[2] " " sp_order[3])
627 if (sp_state ~ "bid|pass|play") {
629 sp_playto " points, " \
631 say(sp_team(0) ": " \
632 int(sp_scores[0]) " points, " \
633 int(sp_bags(0)) " bags")
634 say(sp_team(1) ": " \
635 int(sp_scores[1]) " points, " \
636 int(sp_bags(1)) " bags")
640 /^\.((new|end|load)game|join|look|bid|play)/ {
641 sp_save("var/sp_cur.json");
645 #/^\.playfor [^ ]*$/ {
648 #/^\.standin [^ ]*$/ {
649 # if (p in sp_players) {
651 # for (p in sp_standin) {
652 # if ($2 in sp_standin)
653 # say(here " is already playing for " sp_standin[p]);
655 # sp_standin[away] = here