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
142 gsub(/s/, "\002♠", cards)
143 gsub(/h/, "\002♥", cards)
144 gsub(/d/, "\002♦", cards)
145 gsub(/c/, "\002♣", cards)
150 function sp_next(who, prev)
153 sp_turn = who ? sp_players[who] : (sp_turn + 1) % 4
154 if (length(sp_order) == 4)
155 sp_player = sp_order[sp_turn]
159 function sp_deal( shuf)
161 say("/me deals the cards")
162 asorti(sp_deck, shuf, "sp_usort")
163 for (i=1; i<=52; i++)
164 sp_hands[sp_order[i%4]][shuf[i]] = 1
166 sp_dealer = (sp_dealer+1)%4
168 sp_player = sp_order[sp_turn]
169 say("Bidding starts with " sp_player "!")
172 function sp_hand(to, who, sort, str)
174 asorti(sp_hands[who], sort, "sp_csort")
175 for (i=0; i<length(sort); i++)
176 str = str "" sprintf("%4s", sort[i])
177 gsub(/^ +| +$/, "", str)
178 return sp_pretty(str, to)
181 function sp_hasa(who, expr)
183 for (c in sp_hands[who]) {
190 function sp_type(card)
192 return substr(card, length(card))
195 function sp_usort(a,b,c,d) {
199 function sp_csort(i1,v1,i2,v2) {
200 return sp_deck[i1] > sp_deck[i2] ? +1 :
201 sp_deck[i1] < sp_deck[i2] ? -1 : 0;
204 function sp_winner( card, tmp)
206 for (card in sp_pile)
207 if (card !~ sp_suit && card !~ /s/)
209 asorti(sp_pile, tmp, "sp_csort")
210 #print "pile: " tmp[1] ">" tmp[2] ">" tmp[3] ">" tmp[4]
216 #return "{" sp_order[i+0] "," sp_order[i+2] "}"
217 return sp_order[i+0] "/" sp_order[i+2]
220 function sp_bags(i, bags)
222 bags = sp_scores[i] % sp_limit
228 function sp_bidders( i, turn, bid, bids)
230 for (i = 0; i < 4; i++) {
231 turn = (sp_dealer + i) % 4
232 if (sp_bids[turn] && !sp_nil[turn])
233 bid = sp_order[turn] ":" sp_bids[turn]
234 else if (sp_nil[turn] == 1)
235 bid = sp_order[turn] ":" "nil"
236 else if (sp_nil[turn] == 2)
237 bid = sp_order[turn] ":" "blind"
242 gsub(/^ +| +$/, "", bids)
246 function sp_score( bids, tricks)
248 for (i=0; i<2; i++) {
249 bids = sp_bids[i] + sp_bids[i+2]
250 tricks = sp_tricks[i] + sp_tricks[i+2]
252 if (sp_bags(i) + bags >= sp_limit) {
253 say(sp_team(i) " bag out")
254 sp_scores[i] -= sp_limit * 10
256 if (tricks >= bids) {
257 say(sp_team(i) " make their bid: " tricks "/" bids)
258 sp_scores[i] += bids*10 + bags;
260 say(sp_team(i) " go bust: " tricks "/" bids)
261 sp_scores[i] -= bids*10;
264 for (i=0; i<4; i++) {
267 say(sp_order[i] " " \
268 (sp_nil[i] == 1 && !sp_tricks[i] ? "makes nil!" :
269 sp_nil[i] == 1 && sp_tricks[i] ? "fails at nil!" :
270 sp_nil[i] == 2 && !sp_tricks[i] ? "makes blind nil!" :
271 sp_nil[i] == 2 && sp_tricks[i] ? "fails miserably at blind nil!" :
273 sp_scores[i%2] += 100 * sp_nil[i] * \
274 (sp_tricks[i] == 0 ? 1 : -1)
278 function sp_play(card, winner, pi)
280 delete sp_hands[sp_from][card]
281 sp_pile[card] = sp_player
282 sp_piles = sp_piles (sp_piles?",":"") card
289 if (length(sp_pile) == 1)
290 sp_suit = sp_type(card)
293 if (length(sp_pile) == 4) {
295 pi = sp_players[sp_pile[winner]]
297 say(sp_pile[winner] " wins with " sp_pretty(winner, FROM) \
298 " (" sp_pretty(sp_piles, FROM) ")")
299 sp_next(sp_pile[winner])
304 if (sp_tricks[0] + sp_tricks[1] + \
305 sp_tricks[2] + sp_tricks[3] == 13) {
308 if (sp_scores[0] >= sp_playto || sp_scores[1] >= sp_playto &&
309 sp_scores[0] != sp_scores[1]) {
311 winner = sp_scores[0] > sp_scores[1] ? 0 : 1
313 say(sp_team(winner) " wins the game " \
314 sp_scores[winner] " to " sp_scores[looser])
315 say(sp_order[winner+0] "++")
316 say(sp_order[winner+2] "++")
317 say(sp_order[looser+0] "--")
318 say(sp_order[looser+2] "--")
322 if (sp_scores[0] == sp_scores[1] &&
323 sp_scores[0] >= sp_playto)
324 say("It's tie! Playing an extra round!");
333 cmd = "od -An -N4 -td4 /dev/random"
339 sp_load("var/sp_cur.json");
341 # say(sp_channel, "Game restored.")
345 sp_from = AUTH in sp_auths ? sp_auths[AUTH] : FROM
346 sp_valid = sp_from && sp_from == sp_player
352 say("Spades! " sp_pretty("As,Ah,Ad,Ac", FROM))
357 sp_save("var/sp_save.json");
363 sp_load("var/sp_save.json");
368 /^\.help [Ss]pades$/ {
369 say("Spades -- play a game of spades")
371 say(".newgame [score] -- start a game to <score> points, default 500")
372 say(".endgame -- abort the current game")
373 say(".savegame -- save the current game to disk")
374 say(".loadgame -- load the previously saved game")
375 say(".join -- join the current game")
376 say(".look -- look at your cards")
377 say(".bid n -- bid for <n> tricks")
378 say(".play [card] -- play a card")
379 say(".score -- check the score")
380 say(".tricks -- check how many trick have been taken")
381 say(".bids -- check what everyone bid")
387 /^\.deal (\w+) (.*)/ {
389 for (i=3; i<=NF; i++)
391 say(sp_channel, FROM " is cheating for " $2)
396 /^\.newgame ?([0-9]+)?/ {
397 if (sp_state != "new") {
398 reply("There is already a game in progress.")
401 sp_playto = $2 ? $2 : 200
402 sp_limit = sp_playto > 200 ? 10 : 5;
405 say(sp_owner " starts a game of Spades to " sp_playto " with " sp_limit " bags!")
406 #say("#rhnoise", sp_owner " starts a game of Spades in " DST "!")
410 (sp_from == sp_owner || AUTH == OWNER) &&
412 if (sp_state == "new") {
413 reply("There is no game in progress.")
415 say(FROM " ends the game")
421 if (sp_state == "new") {
422 reply("There is no game in progress")
424 else if (sp_state == "play") {
425 reply("The game has already started")
427 else if (sp_state == "join" && sp_from in sp_players) {
428 reply("You are already playing")
430 else if (sp_state == "join") {
434 sp_auths[AUTH] = FROM
436 say(FROM " joins the game!")
438 if (sp_state == "join" && sp_turn == 0)
443 (sp_state "bid" || sp_state == "play") &&
445 if (sp_from in sp_players)
446 say(".slap " FROM ", it is not your turn.")
448 say(".slap " FROM ", you are not playing.")
454 if ($2 < 0 || $2 > 13) {
455 say("You can only bid from 0 to 13")
459 if ($2 == 0 && !sp_looked[i]) {
460 say(FROM " goes blind nil!")
462 } else if ($2 == 0) {
463 say(FROM " goes nil!")
468 if (sp_turn != sp_dealer) {
469 say("Bidding goes to " sp_player "!")
471 for (p in sp_players)
472 say(p, "You have: " sp_hand(p, p))
474 for (i=0; i<2; i++) {
475 if (sp_nil[i] == 2 || sp_nil[i+2] == 2) {
476 say(sp_team(i) ": select a card to pass " \
477 "(/msg " NICK " .pass <card>)")
481 if (sp_state == "play")
482 say("Play starts with " sp_player "!")
487 sp_state == "pass" &&
490 _team = sp_from in sp_players ? sp_players[sp_from] % 2 : 0
492 # check validity and pass
493 if (!(sp_from in sp_players)) {
494 say(".slap " FROM ", you are not playing.")
496 else if (sp_nil[_team] != 2 && sp_nil[_team+2] != 2) {
497 reply("Your team did not go blind")
499 else if (sp_pass[sp_players[sp_from]]) {
500 reply("You have already passed a card")
502 else if (!(_card in sp_deck)) {
503 reply("Invalid card")
505 else if (!(_card in sp_hands[sp_from])) {
506 reply("You do not have that card")
509 sp_pass[sp_players[sp_from]] = $2
510 say(sp_channel, FROM " passes a card")
513 # check for end of passing
514 if (((sp_nil[0] != 2 && sp_nil[2] != 2) || (sp_pass[0] && sp_pass[2])) &&
515 ((sp_nil[1] != 2 && sp_nil[3] != 2) || (sp_pass[1] && sp_pass[3]))) {
519 delete sp_hands[sp_order[i]][_card]
520 sp_hands[sp_order[_partner]][_card] = 1
522 say(sp_channel, "Cards have been passed, play starts with " sp_player "!")
523 for (p in sp_players)
524 say(p, "You have: " sp_hand(p, p))
529 sp_state ~ "(bid|pass|play)" &&
531 if (!(sp_from in sp_players)) {
532 say(".slap " FROM ", you are not playing.")
534 sp_looked[sp_players[sp_from]] = 1
535 say(FROM, "You have: " sp_hand(FROM, sp_from))
540 sp_state == "play" &&
543 if (!(card in sp_deck)) {
544 reply("Invalid card")
546 else if (!(card in sp_hands[sp_from])) {
547 reply("You do not have that card")
549 else if (sp_suit && card !~ sp_suit && sp_hasa(sp_from, sp_suit)) {
550 reply("You must follow suit (" sp_suit ")")
552 else if (card ~ /s/ && length(sp_hands[sp_from]) == 13 && sp_hasa(sp_from, "[^s]$")) {
553 reply("You cannot trump on the first hand")
555 else if (card ~ /s/ && length(sp_pile) == 0 && sp_hasa(sp_from, "[^s]$") && !sp_broken) {
556 reply("Spades have not been broken")
560 if (sp_state == "play") {
561 if (length(sp_hands[sp_from]))
562 say(FROM, "You have: " sp_hand(FROM, sp_from))
564 say(sp_player ": it is your turn! " \
565 "(" sp_pretty(sp_piles, sp_player) ")")
567 say(sp_player ": it is your turn!")
572 /^\.bids$/ && sp_state == "play" {
573 say(sp_order[0] " bid " sp_bids[0] ", " \
574 sp_order[2] " bid " sp_bids[2] ", " \
575 "total: " sp_bids[0] + sp_bids[2])
576 say(sp_order[1] " bid " sp_bids[1] ", " \
577 sp_order[3] " bid " sp_bids[3] ", " \
578 "total: " sp_bids[1] + sp_bids[3])
581 /^\.tricks$/ && sp_state == "play" {
582 say(sp_order[0] " took " int(sp_tricks[0]) "/" int(sp_bids[0]) ", " \
583 sp_order[2] " took " int(sp_tricks[2]) "/" int(sp_bids[2]))
584 say(sp_order[1] " took " int(sp_tricks[1]) "/" int(sp_bids[1]) ", " \
585 sp_order[3] " took " int(sp_tricks[3]) "/" int(sp_bids[3]))
588 /^\.turn/ && sp_state ~ "(play|bid)" {
590 _pile = sp_pretty(sp_piles, FROM)
591 if (sp_state == "bid" && !_bids)
592 say("It is " sp_player "'s bid!")
593 if (sp_state == "bid" && _bids)
594 say("It is " sp_player "'s bid! (" _bids ")")
595 if (sp_state == "play" && !_pile)
596 say("It is " sp_player "'s turn!")
597 if (sp_state == "play" && _pile)
598 say("It is " sp_player "'s turn! (" _pile ")")
601 (TO == NICK || DST == sp_channel) &&
602 /^\.(score|status)$/ {
603 if (sp_state == "new") {
604 say("There is no game in progress")
606 if (sp_state == "join") {
607 say("Waiting for players: " \
608 sp_order[0] " " sp_order[1] " " \
609 sp_order[2] " " sp_order[3])
611 if (sp_state == "bid" || sp_state == "play") {
613 sp_playto " points, " \
615 say(sp_team(0) ": " \
616 int(sp_scores[0]) " points, " \
617 int(sp_bags(0)) " bags")
618 say(sp_team(1) ": " \
619 int(sp_scores[1]) " points, " \
620 int(sp_bags(1)) " bags")
624 /^\.((new|end|load)game|join|look|bid|play)/ {
625 sp_save("var/sp_cur.json");
629 #/^\.playfor [^ ]*$/ {
632 #/^\.standin [^ ]*$/ {
633 # if (p in sp_players) {
635 # for (p in sp_standin) {
636 # if ($2 in sp_standin)
637 # say(here " is already playing for " sp_standin[p]);
639 # sp_standin[away] = here