]> Pileus Git - ~andy/rhawk/blob - spades.awk
Better?
[~andy/rhawk] / spades.awk
1 # Functions
2 function sp_init(cards, tmp0, tmp1)
3 {
4         sp_valid    = 0     #     Message sent from sp_player
5         sp_player   = ""    #     Who's turn it is
6         sp_turn     = 0     #     Index of who's turn it is
7         delete sp_hands     # [p] Each players cards
8
9         # Init deck
10         cards ="As Ks Qs Js 10s 9s 8s 7s 6s 5s 4s 3s 2s "\
11                "Ah Kh Qh Jh 10h 9h 8h 7h 6h 5h 4h 3h 2h "\
12                "Ac Kc Qc Jc 10c 9c 8c 7c 6c 5c 4c 3c 2c "\
13                "Ad Kd Qd Jd 10d 9d 8d 7d 6d 5d 4d 3d 2d"
14         split(cards, tmp0)
15         for (i=1; i<=length(tmp0); i++)
16                 sp_deck[tmp0[i]] = i
17 }
18
19 function sp_reset(type)
20 {
21         # Per hand
22         if (type >= 0) {
23                 sp_suit     = ""    #     The lead suit {s,h,d,c}
24                 sp_piles    = ""    # [x] Played cards this turn
25                 delete sp_pile      # [x] Played cards this turn
26         }
27
28         # Per round
29         if (type >= 1) {
30                 sp_state    = "bid" #     {new,join,bid,pass,play}
31                 sp_broken   = 0     #     Whether spades are broken
32                 delete sp_looked    # [i] Whether a player has looked a their cards
33                 delete sp_bids      # [i] Each players bid
34                 delete sp_nil       # [i] Nil multiplier 0=regular, 1=nil, 2=blind
35                 delete sp_pass      # [i] Cards to pass
36                 delete sp_tricks    # [i] Tricks this round
37         }
38
39         # Per game
40         if (type >= 2) {
41                 sp_channel  = ""    #     channel to play in
42                 sp_state    = "new" #     {new,join,bid,play}
43                 sp_owner    = ""    #     Who started the game
44                 sp_playto   = 0     #     Score the game will go to
45                 sp_dealer   =-1     #     Who is dealing this round
46                 delete sp_players   # [p] Player names players["name"] -> i
47                 delete sp_order     # [i] Player order order[i] -> "name"
48                 delete sp_scores    # [i] Teams score
49         }
50 }
51
52 function sp_pretty(cards, who)
53 {
54         if (!plain[who]) {
55                 gsub(/[0-9JQKA]*[sc]/, "\0031,00\002&\017", cards) # black
56                 gsub(/[0-9JQKA]*[hd]/, "\0034,00\002&\017", cards) # red
57                 gsub(/s/, "\002♠", cards)
58                 gsub(/h/, "\002♥", cards)
59                 gsub(/d/, "\002♦", cards)
60                 gsub(/c/, "\002♣", cards)
61         }
62         return cards
63 }
64
65 function sp_next(who, prev)
66 {
67         prev      = sp_turn
68         sp_turn   = who ? sp_players[who] : (sp_turn + 1) % 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")
154                         sp_scores[i] += bids*10 + bags;
155                 } else {
156                         say(sp_team(i) " go bust")
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         privmsg(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                 #privmsg("#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(5)
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                 }
340                 if (sp_turn != sp_dealer) {
341                         say("Bidding goes to " sp_player "!")
342                 } else {
343                         for (p in sp_players)
344                                 privmsg(p, "You have: " sp_hand(p))
345                         sp_state = "play"
346                         for (i=0; i<2; i++) {
347                                 if (sp_nil[i] == 2 || sp_nil[i+2] == 2) {
348                                         say(sp_team(i) ": select a card to pass " \
349                                             "(/msg " NICK " .pass <card>)")
350                                         sp_state = "pass"
351                                 }
352                         }
353                         if (sp_state == "play")
354                                 say("Play starts with " sp_player "!")
355                 }
356         }
357 }
358
359 sp_state == "pass" &&
360 /^\.pass (\S+)$/ {
361         card = $2
362         team = sp_players[FROM] % 2
363         if (!(FROM in sp_players)) {
364                 say(".slap " FROM ", you are not playing.")
365         }
366         else if (sp_nil[team] != 2 && sp_nil[team+2] != 2) {
367                 reply("Your team did not go blind")
368         }
369         else if (sp_pass[sp_players[FROM]]) {
370                 reply("You have already passed a card")
371         }
372         else if (!(card in sp_deck)) {
373                 reply("Invalid card")
374         }
375         else if (!(card in sp_hands[FROM])) {
376                 reply("You do not have that card")
377         }
378         else {
379                 sp_pass[sp_players[FROM]] = $2
380                 privmsg(sp_channel, FROM " passes a card")
381         }
382         if (((sp_nil[0] != 2 && sp_nil[2] != 2) || (sp_pass[0] && sp_pass[2])) &&
383             ((sp_nil[1] != 2 && sp_nil[3] != 2) || (sp_pass[1] && sp_pass[3]))) {
384                 for (i in sp_pass) {
385                         partner = (i+2)%4
386                         card    = sp_pass[i]
387                         delete sp_hands[sp_order[i]][card]
388                         sp_hands[sp_order[partner]][card] = 1
389                 }
390                 privmsg(sp_channel, "Cards have been passed, play starts with " sp_player "!")
391                 for (p in sp_players)
392                         privmsg(p, "You have: " sp_hand(p))
393                 sp_state = "play"
394         }
395 }
396
397 sp_state ~ "(play|bid)" &&
398 /^\.look$/ {
399         if (!(FROM in sp_players)) {
400                 say(".slap " FROM ", you are not playing.")
401         } else {
402                 sp_looked[sp_players[FROM]] = 1
403                 privmsg(FROM, "You have: " sp_hand(FROM))
404         }
405 }
406
407 sp_valid &&
408 sp_state == "play" &&
409 /^\.play (\S+)$/ {
410         card = $2
411         if (!(card in sp_deck)) {
412                 reply("Invalid card")
413         }
414         else if (!(card in sp_hands[FROM])) {
415                 reply("You do not have that card")
416         }
417         else if (sp_suit && card !~ sp_suit && sp_hasa(FROM, sp_suit)) {
418                 reply("You must follow suit (" sp_suit ")")
419         }
420         else if (card ~ /s/ && length(sp_hands[FROM]) == 13 && sp_hasa(FROM, "[^s]$")) {
421                 reply("You cannot trump on the first hand")
422         }
423         else if (card ~ /s/ && length(sp_pile) == 0 && sp_hasa(FROM, "[^s]$") && !sp_broken) {
424                 reply("Spades have not been broken")
425         }
426         else {
427                 sp_play(card)
428                 privmsg(FROM, "You have: " sp_hand(FROM))
429                 if (sp_state == "play") {
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 #