fixed game_blackjack point counter
[NeonServV5.git] / src / modules / NeonFun.mod / game_blackjack.c
1 /* game_blackjack.c - NeonServ v5.6
2  * Copyright (C) 2011-2012  Philipp Kreil (pk910)
3  * 
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License 
15  * along with this program. If not, see <http://www.gnu.org/licenses/>. 
16  */
17 #include "../module.h"
18 #include "game_blackjack.h"
19 #include "bot_NeonFun.h"
20 #include "../../IRCParser.h"
21 #include "../../bots.h"
22 #include "../../UserNode.h"
23 #include "../../ChanUser.h"
24 #include "../../tools.h"
25 #include "../botid.h"
26
27 struct bj_game *bj_active_games = NULL;
28
29 static int bj_ctype[] =         {BJ_CTYPE_HEARTS, BJ_CTYPE_SPADES, BJ_CTYPE_DIAMONDS, BJ_CTYPE_CLUBS};
30 static int bj_irc_colors[] =    {4,               1,               4,                 1};
31 static char *bj_color_chars[] = {"\xE2\x9D\xA4",  "\xE2\x99\xA0",  "\xE2\x99\xA6",    "\xE2\x99\xA3"};
32
33 static const struct {
34     const char *name;
35     unsigned char type;
36     unsigned int points : 8;
37 } bj_card_types[] = {
38     {"2",  BJ_CARD_NUMBER_2,  2},
39     {"3",  BJ_CARD_NUMBER_3,  3},
40     {"4",  BJ_CARD_NUMBER_4,  4},
41     {"5",  BJ_CARD_NUMBER_5,  5},
42     {"6",  BJ_CARD_NUMBER_6,  6},
43     {"7",  BJ_CARD_NUMBER_7,  7},
44     {"8",  BJ_CARD_NUMBER_8,  8},
45     {"9",  BJ_CARD_NUMBER_9,  9},
46     {"10", BJ_CARD_NUMBER_10, 10},
47     {"J",  BJ_CARD_JACK,      10},
48     {"Q",  BJ_CARD_QUEEN,     10},
49     {"K",  BJ_CARD_KING,      10},
50     {"A",  BJ_CARD_ACE,       11},
51     {NULL, 0}
52 };
53
54 struct bj_card_deck *bj_shuffle_deck() {
55     struct bj_card *card, *last_card = NULL;
56     int card_count = 0;
57     #define ADD_CARD(ctype,ccard) \
58     card = malloc(sizeof(*card)); \
59     card->type = ctype; \
60     card->card = ccard; \
61     card->prev = NULL; \
62     card->next = last_card; \
63     if(last_card) \
64         last_card->prev = card; \
65     last_card = card; \
66     card_count++;
67     int typecount = BJ_CTYPE_COUNT;
68     for(typecount--; typecount >= 0; typecount--) {
69         ADD_CARD(bj_ctype[typecount], BJ_CARD_NUMBER_2);
70         ADD_CARD(bj_ctype[typecount], BJ_CARD_NUMBER_3);
71         ADD_CARD(bj_ctype[typecount], BJ_CARD_NUMBER_4);
72         ADD_CARD(bj_ctype[typecount], BJ_CARD_NUMBER_5);
73         ADD_CARD(bj_ctype[typecount], BJ_CARD_NUMBER_6);
74         ADD_CARD(bj_ctype[typecount], BJ_CARD_NUMBER_7);
75         ADD_CARD(bj_ctype[typecount], BJ_CARD_NUMBER_8);
76         ADD_CARD(bj_ctype[typecount], BJ_CARD_NUMBER_9);
77         ADD_CARD(bj_ctype[typecount], BJ_CARD_NUMBER_10);
78         ADD_CARD(bj_ctype[typecount], BJ_CARD_JACK);
79         ADD_CARD(bj_ctype[typecount], BJ_CARD_QUEEN);
80         ADD_CARD(bj_ctype[typecount], BJ_CARD_KING);
81         ADD_CARD(bj_ctype[typecount], BJ_CARD_ACE);
82     }
83     #undef ADD_CARD
84     struct bj_card_deck *deck = malloc(sizeof(*deck));
85     deck->cards = last_card;
86     deck->count = card_count;
87     return deck;
88 }
89
90 struct bj_card *bj_get_card(struct bj_card_deck *deck) {
91     if(!deck->count) return NULL;
92     int card_id = (rand() % deck->count);
93     int i = 0;
94     struct bj_card *card;
95     for(card = deck->cards; card; card = card->next) {
96         if(i == card_id) {
97             if(card->prev)
98                 card->prev->next = card->next;
99             else
100                 deck->cards = card->next;
101             if(card->next)
102                 card->next->prev = card->prev;
103             deck->count--;
104             card->next = NULL;
105             card->prev = NULL;
106             return card;
107         }
108         i++;
109     }
110     return NULL;
111 }
112
113 void bj_free_deck(struct bj_card_deck *deck) {
114     struct bj_card *card, *next_card;
115     if(deck->count) {
116         for(card = deck->cards; card; card = next_card) {
117             next_card = card->next;
118             free(card);
119         }
120     }
121     free(deck);
122 }
123
124 void bj_free_player(struct bj_player *player, struct bj_card_deck *deck) {
125     if(player->count) {
126         struct bj_card *card, *next_card;
127         for(card = player->cards; card; card = next_card) {
128             next_card = card->next;
129             if(deck) {
130                 card->next = deck->cards;
131                 deck->cards->prev = card;
132                 deck->cards = card;
133                 deck->count++;
134             } else
135                 free(card);
136         }
137     }
138     if(player->prev)
139         player->prev->next = player->next;
140     if(player->next)
141         player->next->prev = player->prev;
142     free(player);
143 }
144
145 void bj_free_game(struct bj_game *game) {
146     struct bj_player *player, *next_player;
147     for(player = game->player; player; player = next_player) {
148         next_player = player->next;
149         bj_free_player(player, NULL);
150     }
151     if(game->deck)
152         bj_free_deck(game->deck);
153     if(game->timer)
154         timeq_del(game->timer);
155     struct bj_game *cgame, *pgame = NULL;
156     for(cgame = bj_active_games; cgame; cgame = cgame->next) {
157         if(cgame == game) {
158             if(pgame)
159                 pgame->next = game->next;
160             else
161                 bj_active_games = game->next;
162             break;
163         } else
164             pgame = cgame;
165     }
166     free(game);
167 }
168
169 struct bj_player *bj_get_next_player(struct bj_game *game) {
170     struct bj_player *player = game->active_player;
171     player = player->next;
172     return player;
173 }
174
175 int bj_get_player_card_points(struct bj_player *player) {
176     int i, points = 0, ace_count = 0;
177     struct bj_card *card;
178     for(card = player->cards; card; card = card->next) {
179         if(card->card == BJ_CARD_ACE) {
180             ace_count++; //add these points later ;)
181             continue;
182         }
183         i = 0;
184         while(bj_card_types[i].name) {
185             if(bj_card_types[i].type == card->card) {
186                 points += bj_card_types[i].points;
187                 break;
188             }
189             i++;
190         }
191     }
192     while((ace_count--) > 0) {
193         if(points <= 10)
194             points += 11;
195         else
196             points += 1;
197     }
198     return points;
199 }
200
201 static void bj_print_player_cards(struct bj_player *player, char *cards_buf) {
202     struct bj_card *card;
203     int cards_bufpos = 0;
204     cards_buf[0] = '\0';
205     for(card = player->cards; card; card = card->next) {
206         int cardcolor = 1;
207         char *cardchar = "";
208         int i;
209         for(i = 0; i < BJ_CTYPE_COUNT; i++) {
210             if(bj_ctype[i] == card->type) {
211                 cardcolor = bj_irc_colors[i];
212                 cardchar = bj_color_chars[i];
213                 break;
214             }
215         }
216         i = 0;
217         while(bj_card_types[i].name) {
218             if(bj_card_types[i].type == card->card) {
219                 cards_bufpos += sprintf(cards_buf + cards_bufpos, "%s[\003%d%s%s\003]", (cards_bufpos ? " " : ""), cardcolor, cardchar, bj_card_types[i].name);
220                 break;
221             }
222             i++;
223         }
224     }
225 }
226
227 void bj_show_player_cards(struct bj_game *game, struct bj_player *player) {
228     char cards_buf[MAXLEN];
229     bj_print_player_cards(player, cards_buf);
230     int card_points = bj_get_player_card_points(player);
231     reply(game->textbot, player->chanuser->user, "NF_BJ_YOUR_CARDS", card_points, cards_buf);
232 }
233
234 TIMEQ_CALLBACK(bj_game_wait_timeout) {
235     struct bj_game *game = data;
236     game->timer = NULL;
237     if(game->players == 1) {
238         bj_reply(game, NULL, "NF_BJ_LESS_PLAYERS");
239         bj_free_game(game);
240         return;
241     }
242     game->deck = bj_shuffle_deck();
243     bj_reply(game, NULL, "NF_BJ_START");
244     game->state = BJ_STATE_RUNNING;
245     game->active_player = game->player; //active player
246     bj_reply(game, NULL, "NF_BJ_USER_HURRY_UP", game->active_player->chanuser->user->nick);
247     game->timer = timeq_add(30, module_id, bj_player_timeout, game);
248 }
249
250 TIMEQ_CALLBACK(bj_player_timeout) {
251     struct bj_game *game = data;
252     game->timer = NULL;
253     //player timeout (next player)
254     struct bj_player *player = game->active_player;
255     bj_reply(game, NULL, "NF_BJ_USER_TIMEOUT", player->chanuser->user->nick);
256     bj_action_next_player(game, player);
257 }
258
259 void bj_action_take_card(struct bj_game *game, struct bj_player *player) {
260     int points = bj_get_player_card_points(player);
261     if(points > 21) {
262         reply(game->textbot, player->chanuser->user, "NF_BJ_POINTS_EXCEEDED");
263         return;
264     }
265     if(game->timer) {
266         timeq_del(game->timer);
267         game->timer = NULL;
268     }
269     struct bj_card *card = bj_get_card(game->deck);
270     if(!card) {
271         bj_free_deck(game->deck);
272         game->deck = bj_shuffle_deck();
273         card = bj_get_card(game->deck);
274     }
275     struct bj_card *last_card;
276     for(last_card = player->cards; last_card; last_card = last_card->next) {
277         if(last_card->next == NULL)
278             break;
279     }
280     card->prev = last_card;
281     if(last_card)
282         last_card->next = card;
283     else
284         player->cards = card;
285     player->count++;
286     char cardbuf[16];
287     int cardcolor = 1;
288     char *cardchar = "";
289     int i, cardpoints = 0;
290     cardbuf[0] = '\0';
291     for(i = 0; i < BJ_CTYPE_COUNT; i++) {
292         if(bj_ctype[i] == card->type) {
293             cardcolor = bj_irc_colors[i];
294             cardchar = bj_color_chars[i];
295             break;
296         }
297     }
298     i = 0;
299     while(bj_card_types[i].name) {
300         if(bj_card_types[i].type == card->card) {
301             sprintf(cardbuf, "[\003%d%s%s\003]", cardcolor, cardchar, bj_card_types[i].name);
302             cardpoints = bj_card_types[i].points;
303             if(bj_card_types[i].type == BJ_CARD_ACE && points > 10)
304                 cardpoints = 1;
305             break;
306         }
307         i++;
308     }
309     reply(game->textbot, player->chanuser->user, "NF_BJ_TAKE", cardbuf, cardpoints);
310     bj_show_player_cards(game, player);
311     points = bj_get_player_card_points(player);
312     if(points > 21)
313         reply(game->textbot, player->chanuser->user, "NF_BJ_POINTS_EXCEEDED");
314     game->timer = timeq_add(30, module_id, bj_player_timeout, game);
315 }
316
317 void bj_action_next_player(struct bj_game *game, struct bj_player *player) {
318     if(game->timer) {
319         timeq_del(game->timer);
320         game->timer = NULL;
321     }
322     if(player)
323         bj_show_player_cards(game, player);
324     game->active_player = bj_get_next_player(game);
325     if(game->active_player) {
326         bj_reply(game, NULL, "NF_BJ_USER_HURRY_UP", game->active_player->chanuser->user->nick);
327         game->timer = timeq_add(30, module_id, bj_player_timeout, game);
328     } else {
329         bj_game_end(game);
330         bj_free_game(game);
331     }
332 }
333
334 void bj_event_part(struct ChanUser *chanuser) {
335     struct bj_game *game;
336     for(game = bj_active_games; game; game = game->next) {
337         if(chanuser->chan == game->channel) {
338             struct bj_player *player;
339             for(player = game->player; player; player = player->next) {
340                 if(player->chanuser == chanuser) {
341                     if(game->active_player == player) {
342                         if(bj_get_next_player(game) == NULL) {
343                             bj_game_end(game);
344                             bj_free_game(game);
345                             return;
346                         } else
347                             bj_action_next_player(game, NULL);
348                     }
349                     game->players--;
350                     bj_free_player(player, game->deck);
351                     return;
352                 }
353             }
354         }
355     }
356 }
357
358 void bj_event_freechan(struct ChanNode *chan) {
359     struct bj_game *game;
360     for(game = bj_active_games; game; game = game->next) {
361         if(game->channel == chan) {
362             bj_free_game(game);
363             return;
364         }
365     }
366 }
367
368 static int bj_highscore_sort(const void *a, const void *b) {
369     const struct bj_player *player_a = *((struct bj_player * const *) a);
370     const struct bj_player *player_b = *((struct bj_player * const *) b); 
371     int points_a, points_b;
372     if(player_a->count > 21)
373         points_a = player_a->count * -1;
374     else
375         points_a = player_a->count;
376     if(player_b->count > 21)
377         points_b = player_b->count * -1;
378     else
379         points_b = player_b->count;
380     return points_b - points_a;
381 }
382
383 void bj_game_end(struct bj_game *game) {
384     bj_reply(game, NULL, "NF_BJ_GAME_FINISHED");
385     struct Table *table;
386     table = table_init(4, game->players+1, 0);
387     char *content[4];
388     content[0] = get_language_string(NULL, "NF_BJ_RANK");
389     content[1] = get_language_string(NULL, "NF_BJ_NAME");
390     content[2] = get_language_string(NULL, "NF_BJ_POINTS");
391     content[3] = get_language_string(NULL, "NF_BJ_CARDS");
392     table_add(table, content);
393     //sort users
394     struct bj_player *players[game->players];
395     struct bj_player *player;
396     int i = 0;
397     for(player = game->player; player; player = player->next) {
398         player->count = bj_get_player_card_points(player);
399         players[i++] = player;
400     }
401     qsort(players, game->players, sizeof(struct bj_player *), bj_highscore_sort);
402     char rankbuf[12];
403     char pointbuf[12];
404     char cardsbuf[MAXLEN];
405     for(i = 0; i < game->players; i++) {
406         player = players[i];
407         sprintf(rankbuf, "#%d", i+1);
408         content[0] = rankbuf;
409         content[1] = player->chanuser->user->nick;
410         sprintf(pointbuf, "%d", player->count);
411         content[2] = pointbuf;
412         bj_print_player_cards(player, cardsbuf);
413         content[3] = cardsbuf;
414         table_add(table, content);
415     }
416     char **table_lines = table_end(table);
417     for(i = 0; i < table->entrys; i++) {
418         bj_reply(game, NULL, table_lines[i]);
419     }
420     table_free(table);
421 }