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_cloaks # [c] Player cloaks cloaks["cloak"] -> "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, "cloaks", sp_cloaks);
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 json_load(file, game);
108 sp_suit = game["suit"];
109 sp_piles = game["piles"];
110 sp_acopy(sp_pile, game["pile"]);
113 sp_state = game["state"];
114 sp_broken = game["broken"];
115 sp_acopy(sp_looked, game["looked"]);
116 sp_acopy(sp_bids, game["bids"]);
117 sp_acopy(sp_nil, game["nil"]);
118 sp_acopy(sp_pass, game["pass"]);
119 sp_acopy(sp_tricks, game["tricks"]);
122 sp_channel = game["channel"];
123 sp_owner = game["owner"];
124 sp_playto = game["playto"];
125 sp_dealer = game["dealer"];
126 sp_turn = game["turn"];
127 sp_player = game["player"];
128 sp_limit = game["limit"];
129 sp_acopy(sp_hands, game["hands"]);
130 sp_acopy(sp_players, game["players"]);
131 sp_acopy(sp_cloaks, game["cloaks"]);
132 sp_acopy(sp_order, game["order"]);
133 sp_acopy(sp_scores, game["scores"]);
136 function sp_pretty(cards, who)
139 gsub(/[0-9JQKA]*[sc]/, "\0031,00\002&\017", cards) # black
140 gsub(/[0-9JQKA]*[hd]/, "\0034,00\002&\017", cards) # red
141 gsub(/s/, "\002♠", cards)
142 gsub(/h/, "\002♥", cards)
143 gsub(/d/, "\002♦", cards)
144 gsub(/c/, "\002♣", cards)
149 function sp_next(who, prev)
152 sp_turn = who ? sp_players[who] : (sp_turn + 1) % 4
153 if (length(sp_order) == 4)
154 sp_player = sp_order[sp_turn]
158 function sp_deal( shuf)
160 say("/me deals the cards")
161 asorti(sp_deck, shuf, "sp_usort")
162 for (i=1; i<=52; i++)
163 sp_hands[sp_order[i%4]][shuf[i]] = 1
165 sp_dealer = (sp_dealer+1)%4
167 sp_player = sp_order[sp_turn]
168 say("Bidding starts with " sp_player "!")
171 function sp_hand(who, sort, str)
173 asorti(sp_hands[who], sort, "sp_csort")
174 for (i=0; i<length(sort); i++)
175 str = str "" sprintf("%4s", sort[i])
176 gsub(/^ +| +$/, "", str)
177 return sp_pretty(str, who)
180 function sp_hasa(who, expr)
182 for (c in sp_hands[who]) {
189 function sp_type(card)
191 return substr(card, length(card))
194 function sp_usort(a,b,c,d) {
198 function sp_csort(i1,v1,i2,v2) {
199 return sp_deck[i1] > sp_deck[i2] ? +1 :
200 sp_deck[i1] < sp_deck[i2] ? -1 : 0;
203 function sp_winner( card, tmp)
205 for (card in sp_pile)
206 if (card !~ sp_suit && card !~ /s/)
208 asorti(sp_pile, tmp, "sp_csort")
209 #print "pile: " tmp[1] ">" tmp[2] ">" tmp[3] ">" tmp[4]
215 #return "{" sp_order[i+0] "," sp_order[i+2] "}"
216 return sp_order[i+0] "/" sp_order[i+2]
219 function sp_bags(i, bags)
221 bags = sp_scores[i] % sp_limit
227 function sp_bidders( i, turn, bid, bids)
229 for (i = 0; i < 4; i++) {
230 turn = (sp_dealer + i) % 4
231 if (sp_bids[turn] && !sp_nil[turn]) {
232 bid = sp_order[turn] ":" sp_bids[turn]
236 gsub(/^ +| +$/, "", bids)
240 function sp_score( bids, tricks)
242 for (i=0; i<2; i++) {
243 bids = sp_bids[i] + sp_bids[i+2]
244 tricks = sp_tricks[i] + sp_tricks[i+2]
246 if (sp_bags(i) + bags >= sp_limit) {
247 say(sp_team(i) " bag out")
248 sp_scores[i] -= sp_limit * 10
250 if (tricks >= bids) {
251 say(sp_team(i) " make their bid: " tricks "/" bids)
252 sp_scores[i] += bids*10 + bags;
254 say(sp_team(i) " go bust: " tricks "/" bids)
255 sp_scores[i] -= bids*10;
258 for (i=0; i<4; i++) {
261 say(sp_order[i] " " \
262 (sp_nil[i] == 1 && !sp_tricks[i] ? "makes nil!" :
263 sp_nil[i] == 1 && sp_tricks[i] ? "fails at nil!" :
264 sp_nil[i] == 2 && !sp_tricks[i] ? "makes blind nil!" :
265 sp_nil[i] == 2 && sp_tricks[i] ? "fails miserably at blind nil!" :
267 sp_scores[i%2] += 100 * sp_nil[i] * \
268 (sp_tricks[i] == 0 ? 1 : -1)
272 function sp_play(card, winner, pi)
274 delete sp_hands[sp_from][card]
275 sp_pile[card] = sp_player
276 sp_piles = sp_piles (sp_piles?",":"") card
283 if (length(sp_pile) == 1)
284 sp_suit = sp_type(card)
287 if (length(sp_pile) == 4) {
289 pi = sp_players[sp_pile[winner]]
291 say(sp_pile[winner] " wins with " sp_pretty(winner, FROM) \
292 " (" sp_pretty(sp_piles, FROM) ")")
293 sp_next(sp_pile[winner])
298 if (sp_tricks[0] + sp_tricks[1] + \
299 sp_tricks[2] + sp_tricks[3] == 13) {
302 if (sp_scores[0] >= sp_playto || sp_scores[1] >= sp_playto &&
303 sp_scores[0] != sp_scores[1]) {
305 winner = sp_scores[0] > sp_scores[1] ? 0 : 1
307 say(sp_team(winner) " wins the game " \
308 sp_scores[winner] " to " sp_scores[looser])
309 say(sp_order[winner+0] "++")
310 say(sp_order[winner+2] "++")
311 say(sp_order[looser+0] "--")
312 say(sp_order[looser+2] "--")
316 if (sp_scores[0] == sp_scores[1] &&
317 sp_scores[0] >= sp_playto)
318 say("It's tie! Playing an extra round!");
327 cmd = "od -An -N4 -td4 /dev/random"
333 sp_load("var/sp_cur.json");
335 say(sp_channel, "Game restored.")
339 sp_from = AUTH in sp_cloaks ? sp_cloaks[AUTH] : FROM
340 sp_valid = sp_from && sp_from == sp_player
345 say("Spades! " sp_pretty("As,Ah,Ad,Ac", FROM))
350 sp_save("var/sp_save.json");
356 sp_load("var/sp_save.json");
361 /^\.help [Ss]pades$/ {
362 say("Spades -- play a game of spades")
364 say(".newgame [score] -- start a game to <score> points, default 500")
365 say(".endgame -- abort the current game")
366 say(".savegame -- save the current game to disk")
367 say(".loadgame -- load the previously saved game")
368 say(".join -- join the current game")
369 say(".look -- look at your cards")
370 say(".bid n -- bid for <n> tricks")
371 say(".play [card] -- play a card")
372 say(".score -- check the score")
373 say(".tricks -- check how many trick have been taken")
374 say(".bids -- check what everyone bid")
380 /^\.deal (\w+) (.*)/ {
382 for (i=3; i<=NF; i++)
384 say(sp_channel, FROM " is cheating for " $2)
389 /^\.newgame ?([0-9]+)?/ {
390 if (sp_state != "new") {
391 reply("There is already a game in progress.")
394 sp_playto = $2 ? $2 : 200
395 sp_limit = sp_playto > 200 ? 10 : 5;
398 say(sp_owner " starts a game of Spades to " sp_playto " with " sp_limit " bags!")
399 #say("#rhnoise", sp_owner " starts a game of Spades in " DST "!")
403 (sp_from == sp_owner || FROM == OWNER) &&
405 if (sp_state == "new") {
406 reply("There is no game in progress.")
408 say(FROM " ends the game")
414 if (sp_state == "new") {
415 reply("There is no game in progress")
417 else if (sp_state == "play") {
418 reply("The game has already started")
420 else if (sp_state == "join" && sp_from in sp_players) {
421 reply("You are already playing")
423 else if (sp_state == "join") {
427 sp_cloaks[AUTH] = FROM
429 say(FROM " joins the game!")
431 if (sp_state == "join" && sp_turn == 0)
436 (sp_state "bid" || sp_state == "play") &&
438 if (sp_from in sp_players)
439 say(".slap " FROM ", it is not your turn.")
441 say(".slap " FROM ", you are not playing.")
447 if ($2 < 0 || $2 > 13) {
448 say("You can only bid from 0 to 13")
452 if ($2 == 0 && !sp_looked[i]) {
453 say(FROM " goes blind nil!")
455 } else if ($2 == 0) {
456 say(FROM " goes nil!")
461 if (sp_turn != sp_dealer) {
462 say("Bidding goes to " sp_player "!")
464 for (p in sp_players)
465 say(p, "You have: " sp_hand(p))
467 for (i=0; i<2; i++) {
468 if (sp_nil[i] == 2 || sp_nil[i+2] == 2) {
469 say(sp_team(i) ": select a card to pass " \
470 "(/msg " NICK " .pass <card>)")
474 if (sp_state == "play")
475 say("Play starts with " sp_player "!")
480 sp_state == "pass" &&
483 _team = sp_from in sp_players ? sp_players[sp_from] % 2 : 0
485 # check validity and pass
486 if (!(sp_from in sp_players)) {
487 say(".slap " FROM ", you are not playing.")
489 else if (sp_nil[_team] != 2 && sp_nil[_team+2] != 2) {
490 reply("Your team did not go blind")
492 else if (sp_pass[sp_players[sp_from]]) {
493 reply("You have already passed a card")
495 else if (!(_card in sp_deck)) {
496 reply("Invalid card")
498 else if (!(_card in sp_hands[sp_from])) {
499 reply("You do not have that card")
502 sp_pass[sp_players[sp_from]] = $2
503 say(sp_channel, FROM " passes a card")
506 # check for end of passing
507 if (((sp_nil[0] != 2 && sp_nil[2] != 2) || (sp_pass[0] && sp_pass[2])) &&
508 ((sp_nil[1] != 2 && sp_nil[3] != 2) || (sp_pass[1] && sp_pass[3]))) {
512 delete sp_hands[sp_order[i]][_card]
513 sp_hands[sp_order[_partner]][_card] = 1
515 say(sp_channel, "Cards have been passed, play starts with " sp_player "!")
516 for (p in sp_players)
517 say(p, "You have: " sp_hand(p))
522 sp_state ~ "(play|bid)" &&
524 if (!(sp_from in sp_players)) {
525 say(".slap " FROM ", you are not playing.")
527 sp_looked[sp_players[sp_from]] = 1
528 say(FROM, "You have: " sp_hand(sp_from))
533 sp_state == "play" &&
536 if (!(card in sp_deck)) {
537 reply("Invalid card")
539 else if (!(card in sp_hands[sp_from])) {
540 reply("You do not have that card")
542 else if (sp_suit && card !~ sp_suit && sp_hasa(sp_from, sp_suit)) {
543 reply("You must follow suit (" sp_suit ")")
545 else if (card ~ /s/ && length(sp_hands[sp_from]) == 13 && sp_hasa(sp_from, "[^s]$")) {
546 reply("You cannot trump on the first hand")
548 else if (card ~ /s/ && length(sp_pile) == 0 && sp_hasa(sp_from, "[^s]$") && !sp_broken) {
549 reply("Spades have not been broken")
553 if (sp_state == "play") {
554 if (length(sp_hands[sp_from]))
555 say(FROM, "You have: " sp_hand(sp_from))
557 say(sp_player ": it is your turn! " \
558 "(" sp_pretty(sp_piles, sp_player) ")")
560 say(sp_player ": it is your turn!")
565 /^\.bids$/ && sp_state == "play" {
566 say(sp_order[0] " bid " sp_bids[0] ", " \
567 sp_order[2] " bid " sp_bids[2] ", " \
568 "total: " sp_bids[0] + sp_bids[2])
569 say(sp_order[1] " bid " sp_bids[1] ", " \
570 sp_order[3] " bid " sp_bids[3] ", " \
571 "total: " sp_bids[1] + sp_bids[3])
574 /^\.tricks$/ && sp_state == "play" {
575 say(sp_order[0] " took " int(sp_tricks[0]) "/" int(sp_bids[0]) ", " \
576 sp_order[2] " took " int(sp_tricks[2]) "/" int(sp_bids[2]))
577 say(sp_order[1] " took " int(sp_tricks[1]) "/" int(sp_bids[1]) ", " \
578 sp_order[3] " took " int(sp_tricks[3]) "/" int(sp_bids[3]))
581 /^\.turn/ && sp_state ~ "(play|bid)" {
583 _pile = sp_pretty(sp_piles, FROM)
584 if (sp_state == "bid" && !_bids)
585 say("It is " sp_player "'s bid!")
586 if (sp_state == "bid" && _bids)
587 say("It is " sp_player "'s bid! (" _bids ")")
588 if (sp_state == "play" && !_pile)
589 say("It is " sp_player "'s turn!")
590 if (sp_state == "play" && _pile)
591 say("It is " sp_player "'s turn! (" _pile ")")
594 (TO == NICK || DST == sp_channel) &&
595 /^\.(score|status)$/ {
596 if (sp_state == "new") {
597 say("There is no game in progress")
599 if (sp_state == "join") {
600 say("Waiting for players: " \
601 sp_order[0] " " sp_order[1] " " \
602 sp_order[2] " " sp_order[3])
604 if (sp_state == "bid" || sp_state == "play") {
605 say(sp_team(0) ": " \
606 int(sp_scores[0]) " points, " \
607 int(sp_bags(0)) " bags")
608 say(sp_team(1) ": " \
609 int(sp_scores[1]) " points, " \
610 int(sp_bags(1)) " bags")
614 /^\.((new|end|load)game|join|look|bid|play)/ {
615 sp_save("var/sp_cur.json");
619 #/^\.playfor [^ ]*$/ {
622 #/^\.standin [^ ]*$/ {
623 # if (p in sp_players) {
625 # for (p in sp_standin) {
626 # if ($2 in sp_standin)
627 # say(here " is already playing for " sp_standin[p]);
629 # sp_standin[away] = here