]> Pileus Git - ~andy/rhawk/blob - spades.awk
Update .turn command
[~andy/rhawk] / spades.awk
1 # For saving
2 @include "json.awk"
3
4 # Functions
5 function sp_init(cards, tmp0, tmp1)
6 {
7         # Init deck
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"
12         split(cards, tmp0)
13         for (i=1; i<=length(tmp0); i++)
14                 sp_deck[tmp0[i]] = i
15 }
16
17 function sp_reset(type)
18 {
19         # Per hand
20         if (type >= 0) {
21                 sp_suit     = ""    #     The lead suit {s,h,d,c}
22                 sp_piles    = ""    # [x] Played cards this turn
23                 delete sp_pile      # [x] Played cards this turn
24         }
25
26         # Per round
27         if (type >= 1) {
28                 sp_state    = "bid" #     {new,join,bid,pass,play}
29                 sp_broken   = 0     #     Whether spades are broken
30                 delete sp_looked    # [i] Whether a player has looked a their cards
31                 delete sp_bids      # [i] Each players bid
32                 delete sp_nil       # [i] Nil multiplier 0=regular, 1=nil, 2=blind
33                 delete sp_pass      # [i] Cards to pass
34                 delete sp_tricks    # [i] Tricks this round
35         }
36
37         # Per game
38         if (type >= 2) {
39                 sp_channel  = ""    #     channel to play in
40                 sp_state    = "new" #     {new,join,bid,play}
41                 sp_owner    = ""    #     Who started the game
42                 sp_playto   = 0     #     Score the game will go to
43                 sp_dealer   =-1     #     Who is dealing this round
44                 sp_turn     = 0     #     Index of who's turn it is
45                 sp_player   = ""    #     Who's turn it is
46                 sp_valid    = 0     #     Message sent from sp_player
47                 delete sp_hands     # [p] Each players cards
48                 delete sp_players   # [p] Player names players["name"] -> i
49                 delete sp_order     # [i] Player order order[i] -> "name"
50                 delete sp_scores    # [i] Teams score
51         }
52 }
53
54 function sp_acopy(dst, src,     key)
55 {
56         if (isarray(src))
57                 for (key in src)
58                         json_copy(dst, key, src[key])
59 }
60
61 function sp_save(file,  game)
62 {
63         # Per hand
64         game["suit"]    = sp_suit;
65         game["piles"]   = sp_piles;
66         json_copy(game, "pile",    sp_pile);
67
68         # Per round
69         game["state"]   = sp_state;
70         game["broken"]  = sp_broken;
71         json_copy(game, "looked",  sp_looked);
72         json_copy(game, "bids",    sp_bids);
73         json_copy(game, "nil",     sp_nil);
74         json_copy(game, "pass",    sp_pass);
75         json_copy(game, "tricks",  sp_tricks);
76
77         # Per game
78         game["channel"] = sp_channel;
79         game["owner"]   = sp_owner;
80         game["playto"]  = sp_playto;
81         game["dealer"]  = sp_dealer;
82         game["turn"]    = sp_turn;
83         game["player"]  = sp_player;
84         json_copy(game, "hands",   sp_hands);
85         json_copy(game, "players", sp_players);
86         json_copy(game, "order",   sp_order);
87         json_copy(game, "scores",  sp_scores);
88
89         # Save
90         json_save(file, game);
91         say("Game saved.")
92 }
93
94 function sp_load(file,  game)
95 {
96         # Load
97         json_load(file, game);
98
99         # Per hand
100         sp_suit    = game["suit"];
101         sp_piles   = game["piles"];
102         sp_acopy(sp_pile,    game["pile"]);
103
104         # Per round
105         sp_state   = game["state"];
106         sp_broken  = game["broken"];
107         sp_acopy(sp_looked,  game["looked"]);
108         sp_acopy(sp_bids,    game["bids"]);
109         sp_acopy(sp_nil,     game["nil"]);
110         sp_acopy(sp_pass,    game["pass"]);
111         sp_acopy(sp_tricks,  game["tricks"]);
112
113         # Per game
114         sp_channel = game["channel"];
115         sp_owner   = game["owner"];
116         sp_playto  = game["playto"];
117         sp_dealer  = game["dealer"];
118         sp_turn    = game["turn"];
119         sp_player  = game["player"];
120         sp_acopy(sp_hands,   game["hands"]);
121         sp_acopy(sp_players, game["players"]);
122         sp_acopy(sp_order,   game["order"]);
123         sp_acopy(sp_scores,  game["scores"]);
124         say("Game loaded.")
125 }
126
127 function sp_pretty(cards, who)
128 {
129         if (!plain[who]) {
130                 gsub(/[0-9JQKA]*[sc]/, "\0031,00\002&\017", cards) # black
131                 gsub(/[0-9JQKA]*[hd]/, "\0034,00\002&\017", cards) # red
132                 gsub(/s/, "\002♠", cards)
133                 gsub(/h/, "\002♥", cards)
134                 gsub(/d/, "\002♦", cards)
135                 gsub(/c/, "\002♣", cards)
136         }
137         return cards
138 }
139
140 function sp_next(who, prev)
141 {
142         prev      = sp_turn
143         sp_turn   = who ? sp_players[who] : (sp_turn + 1) % 4
144         if (length(sp_order) == 4)
145                 sp_player = sp_order[sp_turn]
146         return prev
147 }
148
149 function sp_deal(       shuf)
150 {
151         say("/me deals the cards")
152         asorti(sp_deck, shuf, "sp_usort")
153         for (i=1; i<=52; i++)
154                 sp_hands[sp_order[i%4]][shuf[i]] = 1
155         sp_state  = "bid"
156         sp_dealer = (sp_dealer+1)%4
157         sp_turn   =  sp_dealer
158         sp_player =  sp_order[sp_turn]
159         say("Bidding starts with " sp_player "!")
160 }
161
162 function sp_hand(who,   sort, str)
163 {
164         asorti(sp_hands[who], sort, "sp_csort")
165         for (i=0; i<length(sort); i++)
166                 str = str "" sprintf("%4s", sort[i])
167         gsub(/^ +| +$/, "", str)
168         return sp_pretty(str, who)
169 }
170
171 function sp_hasa(who, expr)
172 {
173         for (c in sp_hands[who]) {
174                 if (c ~ expr)
175                         return 1
176         }
177         return 0
178 }
179
180 function sp_type(card)
181 {
182         return substr(card, length(card))
183 }
184
185 function sp_usort(a,b,c,d) {
186         return rand() - 0.5
187 }
188
189 function sp_csort(i1,v1,i2,v2) {
190         return sp_deck[i1] > sp_deck[i2] ? +1 :
191                sp_deck[i1] < sp_deck[i2] ? -1 : 0;
192 }
193
194 function sp_winner(     card, tmp)
195 {
196         for (card in sp_pile)
197                 if (card !~ sp_suit && card !~ /s/)
198                         delete sp_pile[card]
199         asorti(sp_pile, tmp, "sp_csort")
200         #print "pile: " tmp[1] ">" tmp[2] ">" tmp[3] ">" tmp[4]
201         return tmp[1]
202 }
203
204 function sp_team(i)
205 {
206         #return "{" sp_order[i+0] "," sp_order[i+2] "}"
207         return sp_order[i+0] "/" sp_order[i+2]
208 }
209
210 function sp_bags(i,     bags)
211 {
212         bags = sp_scores[i] % 10
213         if (bags < 0)
214                 bags += 10
215         return bags
216 }
217
218 function sp_score(      bids, tricks)
219 {
220         for (i=0; i<2; i++) {
221                 bids   = sp_bids[i]   + sp_bids[i+2]
222                 tricks = sp_tricks[i] + sp_tricks[i+2]
223                 bags   = tricks - bids
224                 if (sp_bags(i) + bags >= 10) {
225                         say(sp_team(i) " bag out")
226                         sp_scores[i] -= 100
227                 }
228                 if (tricks >= bids) {
229                         say(sp_team(i) " make their bid: " tricks "/" bids)
230                         sp_scores[i] += bids*10 + bags;
231                 } else {
232                         say(sp_team(i) " go bust: " tricks "/" bids)
233                         sp_scores[i] -= bids*10;
234                 }
235         }
236         for (i=0; i<4; i++) {
237                 if (!sp_nil[i])
238                         continue
239                 say(sp_order[i] " " \
240                     (sp_nil[i] == 1 && !sp_tricks[i] ? "makes nil!"       :
241                      sp_nil[i] == 1 &&  sp_tricks[i] ? "fails at nil!"    :
242                      sp_nil[i] == 2 && !sp_tricks[i] ? "makes blind nil!" :
243                      sp_nil[i] == 2 &&  sp_tricks[i] ? "fails miserably at blind nil!" :
244                                                        "unknown"))
245                 sp_scores[i%2] += 100 * sp_nil[i] * \
246                         (sp_tricks[i] == 0 ? 1 : -1)
247         }
248 }
249
250 function sp_play(card,  winner, pi)
251 {
252         delete sp_hands[FROM][card]
253         sp_pile[card] = sp_player
254         sp_piles      = sp_piles (sp_piles?",":"") card
255         sp_next()
256
257         if (card ~ /s/)
258                 sp_broken = 1
259
260         # Start hand
261         if (length(sp_pile) == 1)
262                 sp_suit = sp_type(card)
263
264         # Finish hand
265         if (length(sp_pile) == 4) {
266                 winner = sp_winner()
267                 pi     = sp_players[sp_pile[winner]]
268                 sp_tricks[pi]++
269                 say(sp_pile[winner] " wins with " sp_pretty(winner, FROM) \
270                     " (" sp_pretty(sp_piles, FROM) ")")
271                 sp_next(sp_pile[winner])
272                 sp_reset(0)
273         }
274
275         # Finish round
276         if (sp_tricks[0] + sp_tricks[1] + \
277             sp_tricks[2] + sp_tricks[3] == 13) {
278                 say("Round over!")
279                 sp_score()
280                 if (sp_scores[0] >= sp_playto || sp_scores[1] >= sp_playto &&
281                     sp_scores[0]              != sp_scores[1]) {
282                         say("Game over!")
283                         winner = sp_scores[0] > sp_scores[1] ? 0 : 1
284                         looser = !winner
285                         say(sp_team(winner) " wins the game " \
286                             sp_scores[winner] " to " sp_scores[looser])
287                         say(sp_order[winner+0] "++")
288                         say(sp_order[winner+2] "++")
289                         say(sp_order[looser+0] "--")
290                         say(sp_order[looser+2] "--")
291                         sp_reset(2)
292
293                 } else {
294                         if (sp_scores[0] == sp_scores[1] && 
295                             sp_scores[0] >= sp_playto)
296                                 say("It's tie! Playing an extra round!");
297                         sp_reset(1)
298                         sp_deal()
299                 }
300         }
301 }
302
303 # Misc
304 BEGIN {
305         cmd = "od -An -N4 -td4 /dev/random"
306         cmd | getline seed
307         close(cmd)
308         srand(seed)
309         sp_init()
310         sp_reset(2)
311 }
312
313 // {
314         sp_valid = (FROM && FROM == sp_player)
315 }
316
317 ! /help/ &&
318 /[Ss]pades/ {
319         say("Spades! " sp_pretty("As,Ah,Ad,Ac", FROM))
320 }
321
322 FROM == OWNER &&
323 /^\.savegame/ {
324         sp_save("var/spades.json");
325 }
326
327 FROM == OWNER &&
328 /^\.loadgame/ {
329         sp_load("var/spades.json");
330 }
331
332 # Help
333 /^\.help [Ss]pades$/ {
334         say("Spades -- play a game of spades")
335         say("Examples:")
336         say(".newgame [score] -- start a game to <score> points, default 500")
337         say(".endgame -- abort the current game")
338         say(".savegame -- save the current game to disk")
339         say(".loadgame -- load the previously saved game")
340         say(".join -- join the current game")
341         say(".look -- look at your cards")
342         say(".bid n -- bid for <n> tricks")
343         say(".play [card] -- play a card")
344         say(".score -- check the score")
345         say(".tricks -- check how many trick have been taken")
346         say(".bids -- check what everyone bid")
347         next
348 }
349
350 # Debugging
351 FROM == OWNER &&
352 /^\.deal (\w+) (.*)/ {
353         delete sp_hands[$2]
354         for (i=3; i<=NF; i++)
355                 sp_hands[$2][$i] = 1
356         say(sp_channel, FROM " is cheating for " $2)
357 }
358
359
360 # Setup
361 /^\.newgame ?([0-9]+)?/ {
362         if (sp_state != "new") {
363                 reply("There is already a game in progress.")
364         } else {
365                 sp_owner   = FROM
366                 sp_playto  = $2 ? $2 : 200
367                 sp_state   = "join"
368                 sp_channel = DST
369                 say(sp_owner " starts a game of Spades to " sp_playto "!")
370                 #say("#rhnoise", sp_owner " starts a game of Spades in " DST "!")
371         }
372 }
373
374 (FROM == sp_owner || FROM == OWNER) &&
375 /^\.endgame$/ {
376         if (sp_state == "new") {
377                 reply("There is no game in progress.")
378         } else {
379                 say(FROM " ends the game")
380                 sp_reset(2)
381         }
382 }
383
384 /^\.join$/ {
385         if (sp_state == "new") {
386                 reply("There is no game in progress")
387         }
388         else if (sp_state == "play") {
389                 reply("The game has already started")
390         }
391         else if (sp_state == "join" && FROM in sp_players) {
392                 reply("You are already playing")
393         }
394         else if (sp_state == "join") {
395                 i = sp_next()
396                 sp_order[i] = FROM
397                 sp_players[FROM] = i
398                 say(FROM " joins the game!")
399         }
400         if (sp_state == "join" && sp_turn == 0)
401                 sp_deal()
402 }
403
404 !sp_valid &&
405 (sp_state "bid" || sp_state == "play") &&
406 /^\.(bid|play)\>/ {
407         if (FROM in sp_players)
408                 say(".slap " FROM ", it is not your turn.")
409         else
410                 say(".slap " FROM ", you are not playing.")
411 }
412
413 sp_valid &&
414 sp_state == "bid" &&
415 /^\.bid [0-9]+$/ {
416         if ($2 < 0 || $2 > 13) {
417                 say("You can only bid from 0 to 13")
418         } else {
419                 i = sp_next()
420                 sp_bids[i] = $2
421                 if ($2 == 0 && !sp_looked[i]) {
422                         say(FROM " goes blind nil!")
423                         sp_nil[i] = 2
424                 } else if ($2 == 0) {
425                         say(FROM " goes nil!")
426                         sp_nil[i] = 1
427                 } else {
428                         sp_nil[i] = 0
429                 }
430                 if (sp_turn != sp_dealer) {
431                         say("Bidding goes to " sp_player "!")
432                 } else {
433                         for (p in sp_players)
434                                 say(p, "You have: " sp_hand(p))
435                         sp_state = "play"
436                         for (i=0; i<2; i++) {
437                                 if (sp_nil[i] == 2 || sp_nil[i+2] == 2) {
438                                         say(sp_team(i) ": select a card to pass " \
439                                             "(/msg " NICK " .pass <card>)")
440                                         sp_state = "pass"
441                                 }
442                         }
443                         if (sp_state == "play")
444                                 say("Play starts with " sp_player "!")
445                 }
446         }
447 }
448
449 sp_state == "pass" &&
450 /^\.pass (\S+)$/ {
451         card = $2
452         team = sp_players[FROM] % 2
453         if (!(FROM in sp_players)) {
454                 say(".slap " FROM ", you are not playing.")
455         }
456         else if (sp_nil[team] != 2 && sp_nil[team+2] != 2) {
457                 reply("Your team did not go blind")
458         }
459         else if (sp_pass[sp_players[FROM]]) {
460                 reply("You have already passed a card")
461         }
462         else if (!(card in sp_deck)) {
463                 reply("Invalid card")
464         }
465         else if (!(card in sp_hands[FROM])) {
466                 reply("You do not have that card")
467         }
468         else {
469                 sp_pass[sp_players[FROM]] = $2
470                 say(sp_channel, FROM " passes a card")
471         }
472         if (((sp_nil[0] != 2 && sp_nil[2] != 2) || (sp_pass[0] && sp_pass[2])) &&
473             ((sp_nil[1] != 2 && sp_nil[3] != 2) || (sp_pass[1] && sp_pass[3]))) {
474                 for (i in sp_pass) {
475                         partner = (i+2)%4
476                         card    = sp_pass[i]
477                         delete sp_hands[sp_order[i]][card]
478                         sp_hands[sp_order[partner]][card] = 1
479                 }
480                 say(sp_channel, "Cards have been passed, play starts with " sp_player "!")
481                 for (p in sp_players)
482                         say(p, "You have: " sp_hand(p))
483                 sp_state = "play"
484         }
485 }
486
487 sp_state ~ "(play|bid)" &&
488 /^\.look$/ {
489         if (!(FROM in sp_players)) {
490                 say(".slap " FROM ", you are not playing.")
491         } else {
492                 sp_looked[sp_players[FROM]] = 1
493                 say(FROM, "You have: " sp_hand(FROM))
494         }
495 }
496
497 sp_valid &&
498 sp_state == "play" &&
499 /^\.play (\S+)$/ {
500         card = $2
501         if (!(card in sp_deck)) {
502                 reply("Invalid card")
503         }
504         else if (!(card in sp_hands[FROM])) {
505                 reply("You do not have that card")
506         }
507         else if (sp_suit && card !~ sp_suit && sp_hasa(FROM, sp_suit)) {
508                 reply("You must follow suit (" sp_suit ")")
509         }
510         else if (card ~ /s/ && length(sp_hands[FROM]) == 13 && sp_hasa(FROM, "[^s]$")) {
511                 reply("You cannot trump on the first hand")
512         }
513         else if (card ~ /s/ && length(sp_pile) == 0 && sp_hasa(FROM, "[^s]$") && !sp_broken) {
514                 reply("Spades have not been broken")
515         }
516         else {
517                 sp_play(card)
518                 if (sp_state == "play") {
519                         if (length(sp_hands[FROM]))
520                                 say(FROM, "You have: " sp_hand(FROM))
521                         if (sp_piles)
522                                 say(sp_player ": it is your turn! " \
523                                     "(" sp_pretty(sp_piles, sp_player) ")")
524                         else
525                                 say(sp_player ": it is your turn!")
526                 }
527         }
528 }
529
530 /^\.bids$/ && sp_state == "play" {
531         say(sp_order[0] " bid " sp_bids[0] ", " \
532             sp_order[2] " bid " sp_bids[2] ", " \
533             "total: " sp_bids[0] + sp_bids[2])
534         say(sp_order[1] " bid " sp_bids[1] ", " \
535             sp_order[3] " bid " sp_bids[3] ", " \
536             "total: " sp_bids[1] + sp_bids[3])
537 }
538
539 /^\.tricks$/ && sp_state == "play" {
540         say(sp_order[0] " took " int(sp_tricks[0]) "/" int(sp_bids[0]) ", " \
541             sp_order[2] " took " int(sp_tricks[2]) "/" int(sp_bids[2]))
542         say(sp_order[1] " took " int(sp_tricks[1]) "/" int(sp_bids[1]) ", " \
543             sp_order[3] " took " int(sp_tricks[3]) "/" int(sp_bids[3]))
544 }
545
546 /\.turn/ && sp_state ~ "(play|bid)" {
547         if (sp_turn > 0) {
548                 t = sp_turn
549                 d = sp_dealer
550                 print d, t
551                 _bids = (t>0 ? (     sp_order[(d+0)%4] ": " sp_bids[(d+0)%4]) : "") \
552                         (t>1 ? (", " sp_order[(d+1)%4] ": " sp_bids[(d+1)%4]) : "") \
553                         (t>2 ? (", " sp_order[(d+2)%4] ": " sp_bids[(d+2)%4]) : "") \
554                         (t>3 ? (", " sp_order[(d+3)%4] ": " sp_bids[(d+3)%4]) : "")
555                 _pile = sp_pretty(sp_piles, sp_player)
556         }
557         if (sp_state == "bid" && sp_turn <= 0)
558                 say("It is " sp_player "'s bid!")
559         if (sp_state == "bid" && sp_turn >  0)
560                 say("It is " sp_player "'s bid! (" _bids ")")
561         if (sp_state == "play" && sp_turn <= 0)
562                 say("It is " sp_player "'s turn!")
563         if (sp_state == "play" && sp_turn >  0)
564                 say("It is " sp_player "'s turn! (" _pile ")")
565 }
566
567 (TO == NICK || DST == sp_channel) &&
568 /^\.(score|status)$/ {
569         if (sp_state == "new") {
570                 say("There is no game in progress")
571         }
572         if (sp_state == "join") {
573                 say("Waiting for players: " \
574                     sp_order[0] " " sp_order[1] " " \
575                     sp_order[2] " " sp_order[3])
576         }
577         if (sp_state == "bid" || sp_state == "play") {
578                 say(sp_team(0) ": " \
579                     int(sp_scores[0]) " points, " \
580                     int(sp_bags(0))   " bags")
581                 say(sp_team(1) ": " \
582                     int(sp_scores[1]) " points, " \
583                     int(sp_bags(1))   " bags")
584         }
585 }
586
587 # Standin
588 #/^\.playfor [^ ]*$/ {
589 #}
590 #
591 #/^\.standin [^ ]*$/ {
592 #       if (p in sp_players) {
593 #       }
594 #       for (p in sp_standin) {
595 #               if ($2 in sp_standin) 
596 #               say(here " is already playing for " sp_standin[p]);
597 #       }
598 #       sp_standin[away] = here
599 #}
600 #