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