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