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