1 /* game_blackjack.c - NeonServ v5.6
2 * Copyright (C) 2011-2012 Philipp Kreil (pk910)
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.
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.
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/>.
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"
27 struct bj_game *bj_active_games = NULL;
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"};
36 unsigned int points : 8;
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},
54 struct bj_card_deck *bj_shuffle_deck() {
55 struct bj_card *card, *last_card = NULL;
57 #define ADD_CARD(ctype,ccard) \
58 card = malloc(sizeof(*card)); \
62 card->next = last_card; \
64 last_card->prev = card; \
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);
84 struct bj_card_deck *deck = malloc(sizeof(*deck));
85 deck->cards = last_card;
86 deck->count = card_count;
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);
95 for(card = deck->cards; card; card = card->next) {
98 card->prev->next = card->next;
100 deck->cards = card->next;
102 card->next->prev = card->prev;
113 void bj_free_deck(struct bj_card_deck *deck) {
114 struct bj_card *card, *next_card;
116 for(card = deck->cards; card; card = next_card) {
117 next_card = card->next;
124 void bj_free_player(struct bj_player *player, struct bj_card_deck *deck) {
126 struct bj_card *card, *next_card;
127 for(card = player->cards; card; card = next_card) {
128 next_card = card->next;
130 card->next = deck->cards;
131 deck->cards->prev = card;
139 player->prev->next = player->next;
141 player->next->prev = player->prev;
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);
152 bj_free_deck(game->deck);
154 timeq_del(game->timer);
155 struct bj_game *cgame, *pgame = NULL;
156 for(cgame = bj_active_games; cgame; cgame = cgame->next) {
159 pgame->next = game->next;
161 bj_active_games = game->next;
169 struct bj_player *bj_get_next_player(struct bj_game *game) {
170 struct bj_player *player = game->active_player;
171 player = player->next;
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 ;)
184 while(bj_card_types[i].name) {
185 if(bj_card_types[i].type == card->card) {
186 points += bj_card_types[i].points;
192 while((ace_count--) > 0) {
201 static void bj_print_player_cards(struct bj_player *player, char *cards_buf) {
202 struct bj_card *card;
203 int cards_bufpos = 0;
205 for(card = player->cards; card; card = card->next) {
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];
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);
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);
234 TIMEQ_CALLBACK(bj_game_wait_timeout) {
235 struct bj_game *game = data;
237 if(game->players == 1) {
238 bj_reply(game, NULL, "NF_BJ_LESS_PLAYERS");
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);
250 TIMEQ_CALLBACK(bj_player_timeout) {
251 struct bj_game *game = data;
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);
259 void bj_action_take_card(struct bj_game *game, struct bj_player *player) {
260 int points = bj_get_player_card_points(player);
262 reply(game->textbot, player->chanuser->user, "NF_BJ_POINTS_EXCEEDED");
266 timeq_del(game->timer);
269 struct bj_card *card = bj_get_card(game->deck);
271 bj_free_deck(game->deck);
272 game->deck = bj_shuffle_deck();
273 card = bj_get_card(game->deck);
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)
280 card->prev = last_card;
282 last_card->next = card;
284 player->cards = card;
289 int i, cardpoints = 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];
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)
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);
313 reply(game->textbot, player->chanuser->user, "NF_BJ_POINTS_EXCEEDED");
314 game->timer = timeq_add(30, module_id, bj_player_timeout, game);
317 void bj_action_next_player(struct bj_game *game, struct bj_player *player) {
319 timeq_del(game->timer);
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);
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) {
347 bj_action_next_player(game, NULL);
350 bj_free_player(player, game->deck);
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) {
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;
375 points_a = player_a->count;
376 if(player_b->count > 21)
377 points_b = player_b->count * -1;
379 points_b = player_b->count;
380 return points_b - points_a;
383 void bj_game_end(struct bj_game *game) {
384 bj_reply(game, NULL, "NF_BJ_GAME_FINISHED");
386 table = table_init(4, game->players+1, 0);
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);
394 struct bj_player *players[game->players];
395 struct bj_player *player;
397 for(player = game->player; player; player = player->next) {
398 player->count = bj_get_player_card_points(player);
399 players[i++] = player;
401 qsort(players, game->players, sizeof(struct bj_player *), bj_highscore_sort);
404 char cardsbuf[MAXLEN];
405 for(i = 0; i < game->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);
416 char **table_lines = table_end(table);
417 for(i = 0; i < table->entrys; i++) {
418 bj_reply(game, NULL, table_lines[i]);