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+) (.*)/ {
392 for (i=3; i<=NF; i++)
394 say(sp_channel, FROM " is cheating for " $2)
399 /^\.newgame ?([0-9]+)?/ {
400 if (sp_state != "new") {
401 reply("There is already a game in progress.")
404 sp_playto = $2 ? $2 : 200
405 sp_limit = sp_playto > 200 ? 10 : 5;
408 say(sp_owner " starts a game of Spades to " sp_playto " with " sp_limit " bags!")
409 #say("#rhnoise", sp_owner " starts a game of Spades in " DST "!")
413 (sp_from == sp_owner || AUTH == OWNER) &&
415 if (sp_state == "new") {
416 reply("There is no game in progress.")
418 say(FROM " ends the game")
424 if (sp_state == "new") {
425 reply("There is no game in progress")
427 else if (sp_state == "play") {
428 reply("The game has already started")
430 else if (sp_state == "join" && sp_from in sp_players) {
431 reply("You are already playing")
433 else if (sp_state == "join") {
437 sp_auths[AUTH] = FROM
439 say(FROM " joins the game!")
441 if (sp_state == "join" && sp_turn == 0)
446 (sp_state "bid" || sp_state == "play") &&
448 if (sp_from in sp_players)
449 say(".slap " FROM ", it is not your turn.")
451 say(".slap " FROM ", you are not playing.")
457 if ($2 < 0 || $2 > 13) {
458 say("You can only bid from 0 to 13")
462 if ($2 == 0 && !sp_looked[i]) {
463 say(FROM " goes blind nil!")
465 } else if ($2 == 0) {
466 say(FROM " goes nil!")
471 if (sp_turn != sp_dealer) {
472 say("Bidding goes to " sp_player "!")
474 for (p in sp_players)
475 say(p, "You have: " sp_hand(p, p))
477 for (i=0; i<2; i++) {
478 if (sp_nil[i] == 2 || sp_nil[i+2] == 2) {
479 say(sp_team(i) ": select a card to pass " \
480 "(/msg " NICK " .pass <card>)")
484 if (sp_state == "play")
485 say("Play starts with " sp_player "!")
490 sp_state == "pass" &&
493 _team = sp_from in sp_players ? sp_players[sp_from] % 2 : 0
495 # check validity and pass
496 if (!(sp_from in sp_players)) {
497 say(".slap " FROM ", you are not playing.")
499 else if (sp_nil[_team] != 2 && sp_nil[_team+2] != 2) {
500 reply("Your team did not go blind")
502 else if (sp_pass[sp_players[sp_from]]) {
503 reply("You have already passed a card")
505 else if (!(_card in sp_deck)) {
506 reply("Invalid card")
508 else if (!(_card in sp_hands[sp_from])) {
509 reply("You do not have that card")
512 sp_pass[sp_players[sp_from]] = $2
513 say(sp_channel, FROM " passes a card")
516 # check for end of passing
517 if (((sp_nil[0] != 2 && sp_nil[2] != 2) || (sp_pass[0] && sp_pass[2])) &&
518 ((sp_nil[1] != 2 && sp_nil[3] != 2) || (sp_pass[1] && sp_pass[3]))) {
522 delete sp_hands[sp_order[i]][_card]
523 sp_hands[sp_order[_partner]][_card] = 1
525 say(sp_channel, "Cards have been passed, play starts with " sp_player "!")
526 for (p in sp_players)
527 say(p, "You have: " sp_hand(p, p))
532 sp_state ~ "(bid|pass|play)" &&
534 if (!(sp_from in sp_players)) {
535 say(".slap " FROM ", you are not playing.")
537 sp_looked[sp_players[sp_from]] = 1
538 say(FROM, "You have: " sp_hand(FROM, sp_from))
543 sp_state == "play" &&
546 if (!(card in sp_deck)) {
547 reply("Invalid card")
549 else if (!(card in sp_hands[sp_from])) {
550 reply("You do not have that card")
552 else if (sp_suit && card !~ sp_suit && sp_hasa(sp_from, sp_suit)) {
553 reply("You must follow suit (" sp_suit ")")
555 else if (card ~ /s/ && length(sp_hands[sp_from]) == 13 && sp_hasa(sp_from, "[^s]$")) {
556 reply("You cannot trump on the first hand")
558 else if (card ~ /s/ && length(sp_pile) == 0 && sp_hasa(sp_from, "[^s]$") && !sp_broken) {
559 reply("Spades have not been broken")
563 if (sp_state == "play") {
564 if (length(sp_hands[sp_from]))
565 say(FROM, "You have: " sp_hand(FROM, sp_from))
567 say(sp_player ": it is your turn! " \
568 "(" sp_pretty(sp_piles, sp_player) ")")
570 say(sp_player ": it is your turn!")
575 /^\.bids$/ && sp_state ~ "(pass|play)" {
576 say(sp_order[0] " bid " sp_bids[0] ", " \
577 sp_order[2] " bid " sp_bids[2] ", " \
578 "total: " sp_bids[0] + sp_bids[2])
579 say(sp_order[1] " bid " sp_bids[1] ", " \
580 sp_order[3] " bid " sp_bids[3] ", " \
581 "total: " sp_bids[1] + sp_bids[3])
584 /^\.tricks$/ && sp_state == "play" {
585 say(sp_order[0] " took " int(sp_tricks[0]) "/" int(sp_bids[0]) ", " \
586 sp_order[2] " took " int(sp_tricks[2]) "/" int(sp_bids[2]))
587 say(sp_order[1] " took " int(sp_tricks[1]) "/" int(sp_bids[1]) ", " \
588 sp_order[3] " took " int(sp_tricks[3]) "/" int(sp_bids[3]))
591 /^\.turn/ && sp_state ~ "(bid|pass|play)" {
593 _pile = sp_pretty(sp_piles, FROM)
594 if (sp_state == "bid" && !_bids)
595 say("It is " sp_player "'s bid!")
596 if (sp_state == "bid" && _bids)
597 say("It is " sp_player "'s bid! (" _bids ")")
598 if (sp_state == "play" && !_pile)
599 say("It is " sp_player "'s turn!")
600 if (sp_state == "play" && _pile)
601 say("It is " sp_player "'s turn! (" _pile ")")
602 for (_i=0; sp_state == "pass" && _i<4; _i++)
603 if ((sp_nil[_i%2+0]==2 || sp_nil[_i%2+2]==2) && !sp_pass[_i])
604 say("Waiting for " sp_order[_i] " to pass a card!")
607 (TO == NICK || DST == sp_channel) &&
608 /^\.(score|status)$/ {
609 if (sp_state == "new") {
610 say("There is no game in progress")
612 if (sp_state == "join") {
613 say("Waiting for players: " \
614 sp_order[0] " " sp_order[1] " " \
615 sp_order[2] " " sp_order[3])
617 if (sp_state ~ "bid|pass|play") {
619 sp_playto " points, " \
621 say(sp_team(0) ": " \
622 int(sp_scores[0]) " points, " \
623 int(sp_bags(0)) " bags")
624 say(sp_team(1) ": " \
625 int(sp_scores[1]) " points, " \
626 int(sp_bags(1)) " bags")
630 /^\.((new|end|load)game|join|look|bid|play)/ {
631 sp_save("var/sp_cur.json");
635 #/^\.playfor [^ ]*$/ {
638 #/^\.standin [^ ]*$/ {
639 # if (p in sp_players) {
641 # for (p in sp_standin) {
642 # if ($2 in sp_standin)
643 # say(here " is already playing for " sp_standin[p]);
645 # sp_standin[away] = here