Kill mega-sena.sh
[lcapit-junk-code.git] / games / seabattle / seabattle.c
blob4856243a02c1102dcffdab4d7891149c3465569a
1 /*
2 * A very stupid seabattle game written using a very stupid game library
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 2 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.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 * Luiz Fernando N. Capitulino
19 * <lcapitulino@gmail.com>
21 #include <stdio.h>
22 #include <unistd.h>
23 #include <stdlib.h>
24 #include <allegro.h>
25 #include <fcntl.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
30 * Screen size, note that (x, y) = (w, h)
32 #define DEF_SCREEN_W 640
33 #define DEF_SCREEN_H 480
36 * Ship related definitions
38 #define SHIP_FIRST 0
39 #define SHIP_WATER 0
40 #define SHIP_WATER_HIT 1
41 #define SHIP_SUB 2
42 #define SHIP_SUB_HIT 3
43 #define SHIP_DEST 4
44 #define SHIP_DEST_HIT 5
45 #define SHIP_ARCC 6
46 #define SHIP_ARCC_HIT 7
47 #define SHIP_LAST SHIP_ARCC_HIT
48 #define SHIP_MAX (SHIP_LAST + 1)
50 #define SHIP_W 49
51 #define SHIP_H 49
53 #define SHIP_SUB_NR 2
54 #define SHIP_DEST_NR 4
55 #define SHIP_ARCC_NR 3
56 #define SHIP_DEF_NR (SHIP_SUB_NR + SHIP_DEST_NR + SHIP_ARCC_NR)
59 * Low-level arrays sizes
61 #define MAP_ARRAY_W (DEF_SCREEN_W / SHIP_W / 2)
62 #define MAP_ARRAY_H (DEF_SCREEN_H / SHIP_H)
65 * Global variables
67 static BITMAP *buffer, *ships[SHIP_MAX];
69 static char *ships_pics[] = {
70 "water.bmp",
71 "water_hit.bmp",
72 "sub.bmp",
73 "sub_hit.bmp",
74 "dest.bmp",
75 "dest_hit.bmp",
76 "arcc.bmp",
77 "arcc_hit.bmp",
80 struct player_object {
81 volatile int ship_nr;
82 int map_array[MAP_ARRAY_H][MAP_ARRAY_W];
85 static struct player_object player_user, player_comp;
88 * Misc functions and macros
91 #define ROUNDDOWN(a, n) \
92 ({ \
93 uint32_t __a = (uint32_t) (a); \
94 (typeof(a)) (__a - __a % (n)); \
95 }) \
98 * die(): die with a nice message from the user
100 static void die(const char *fmt, ...)
102 va_list va;
104 va_start(va, fmt);
105 vfprintf(stderr, fmt, va);
106 va_end(va);
108 exit(1);
112 * Allegro and generic game related functions
116 * game_color_depth(): Set the color depth to be used
118 static void game_color_depth(void)
120 int depth;
122 depth = desktop_color_depth();
123 if (!depth)
124 depth = 32;
125 set_color_depth(depth);
129 * game_srand_init(): Initialize the random number
130 * generator
132 static void game_srand_init(void)
134 int fd;
135 ssize_t bytes;
136 unsigned int seed;
138 fd = open("/dev/urandom", O_RDONLY);
139 if (fd < 0) {
140 perror("open()");
141 exit(1);
144 seed = 0;
145 bytes = read(fd, &seed, sizeof(unsigned int));
146 if (bytes != sizeof(unsigned int))
147 die("Could not read enough bytes\n");
148 if (!seed)
149 die("seed is zero\n");
151 close(fd);
152 srand(seed);
156 * game_rand(): Return two random generated numbers, in
157 * the range [0, MAP_ARRAY_H). Can only be called after
158 * a call to game_srand_init()
160 static void game_srand(int *x, int *y)
162 *x = rand() % MAP_ARRAY_W;
163 *y = rand() % MAP_ARRAY_H;
167 * game_print(): Print a single message in the screen
168 * buffer
170 static void game_print(const char *s)
172 textprintf_centre_ex(screen, font, DEF_SCREEN_W / 2, 120,
173 makecol(0, 255, 0), -1,
174 "%s", s);
177 static void player_init(struct player_object *p);
180 * game_init(): Initialize every single thing we need
181 * to make this game to work
183 static void game_init(void)
185 int i, err;
187 allegro_init();
188 game_color_depth();
190 err = set_gfx_mode(GFX_AUTODETECT_WINDOWED, DEF_SCREEN_W,
191 DEF_SCREEN_H, 0, 0);
192 if (err) {
193 allegro_message(allegro_error);
194 exit(1);
197 install_timer();
198 install_keyboard();
200 buffer = create_bitmap(SCREEN_W, SCREEN_H);
201 if (!buffer) {
202 allegro_message(allegro_error);
203 exit(1);
206 // load all ships
207 for (i = 0; i < SHIP_MAX; i++) {
208 ships[i] = load_bitmap(ships_pics[i], NULL);
209 if (!ships[i]) {
210 allegro_message(allegro_error);
211 exit(1);
215 player_init(&player_user);
216 player_init(&player_comp);
218 game_srand_init();
222 * game_install_mouse(): install the mouse handling registering
223 * a callback
225 static void game_install_mouse(void (*callback)(int flags))
227 mouse_callback = callback;
228 install_mouse();
232 * game_remove_mouse(): remove a mouse handler and its
233 * registered callback
235 static void game_remove_mouse(void)
237 show_mouse(NULL);
238 remove_mouse();
239 mouse_callback = NULL;
243 * game_deinit(): free all allocated memory
245 static void game_deinit(void)
247 int i;
249 clear_keybuf();
251 for (i = 0; i < SHIP_MAX; i++)
252 destroy_bitmap(ships[i]);
254 destroy_bitmap(buffer);
258 * Ship related functions
262 * ship_draw(): Draw a ship in the double buffer
264 static void ship_draw(int ship, int x, int y)
266 if (ship < SHIP_FIRST || ship > SHIP_LAST)
267 die("%s: unknown ship code: %d\n", __FUNCTION__, ship);
269 draw_sprite(buffer, ships[ship], x, y);
273 * ship_is_hidden(): Return true is ship must not be visible
274 * in the screen
276 static int ship_is_hidden(int ship)
278 switch (ship) {
279 case SHIP_SUB:
280 case SHIP_DEST:
281 case SHIP_ARCC:
282 return 1;
285 return 0;
289 * ship_is_hit(): Return true is ship is of the hit type
291 static int ship_is_hit(int ship)
293 switch (ship) {
294 case SHIP_WATER_HIT:
295 case SHIP_SUB_HIT:
296 case SHIP_DEST_HIT:
297 case SHIP_ARCC_HIT:
298 return 1;
301 return 0;
305 * ship_get_size(): Return the size of a given ship, if the
306 * ship doesn't exist return -1
308 static int ship_get_size(int ship)
310 switch (ship) {
311 case SHIP_SUB:
312 return 1;
313 case SHIP_DEST:
314 return 2;
315 case SHIP_ARCC:
316 return 3;
319 return -1;
323 * Player object methods
327 * player_str(): Return a string which describes the player_object
328 * human name
330 static const char *player_str(const struct player_object *p)
332 return (p == &player_user ? "user" : "computer");
336 * player_ship_nr_def(): Set the default number of ships
338 static void player_ship_nr_def(struct player_object *p)
340 p->ship_nr = SHIP_DEF_NR;
344 * player_init(): Initialize a player object
346 static void player_init(struct player_object *p)
348 int x, y;
350 for (y = 0; y < MAP_ARRAY_H; y++)
351 for (x = 0; x < MAP_ARRAY_W; x++)
352 p->map_array[y][x] = 0;
354 player_ship_nr_def(p);
358 * player_array_dump(): Dump low-level array contents
360 static void player_array_dump(struct player_object *p)
362 int *map, i, j;
364 map = &p->map_array[0][0];
366 printf("%s map:\n", player_str(p));
367 for (i = 0; i < MAP_ARRAY_H; i++) {
368 for (j = 0; j < MAP_ARRAY_W; j++, map++)
369 printf("%d ", *map);
370 putchar('\n');
372 putchar('\n');
373 fflush(stdout);
377 * player_ship_nr(): Return the number of ships the
378 * object currently has
380 static int player_ship_nr(const struct player_object *p)
382 return p->ship_nr;
386 * player_ship_nr_dec(): Decrement by 1 the number of ships the
387 * object currently has
389 static void player_ship_nr_dec(struct player_object *p)
391 p->ship_nr--;
395 * player_array_set(): set 'ship' in the (x, y) positions
396 * of the provided player's map array.
398 * If the position was really set (ie, the previous element
399 * was SHIP_WATER) we return 1, otherwise return 0.
401 static int player_array_set(struct player_object *p,
402 int ship, int x, int y)
404 if (p->map_array[y][x] == SHIP_WATER) {
405 p->map_array[y][x] = ship;
406 return 1;
409 return 0;
413 * player_ship_set(): set 'ship' in the (x, y) positions
414 * of the provided player's map array.
416 * If the position was really set (ie, the previous element
417 * stored in there was different) we return 1 and decrement
418 * ship_nr, otherwise just return 0
420 static int player_ship_set(struct player_object *p,
421 int ship, int x, int y)
423 if (player_array_set(p, ship, x, y)) {
424 player_ship_nr_dec(p);
425 return 1;
428 return 0;
432 * player_ship_complex_set(): Wrapper for player_ship_set() that
433 * supports loading ships with more than one part. Detects
434 * collisions and other problems.
436 * Return 1 if the position was set, 0 otherwise
438 static int player_ship_complex_set(struct player_object *p,
439 int ship, int x, int y)
441 int parts, i;
443 parts = ship_get_size(ship);
444 if (parts <= 0)
445 die("ship: %d has %d parts\n", ship, parts);
447 // Do not allow ships to cross the map
448 if ((x + parts) > MAP_ARRAY_W)
449 return 0;
451 // Check if a collision is possible: left to right
452 for (i = 0; i < parts; i++)
453 if (p->map_array[y][x + i] != SHIP_WATER)
454 return 0;
456 // Okay, just set it then
457 for (i = 0; i < parts; i++)
458 player_ship_set(p, ship, x + i, y);
460 return 1;
465 * player_map_draw(): Draw a player's map in the screen
467 * Note that we're smart, we know how to hide ships
469 static void player_map_draw(struct player_object *p,
470 int start_x, int start_y, int hide)
472 int *map, y, x;
474 map = &p->map_array[0][0];
476 for (y = start_y; y < (MAP_ARRAY_H + start_y); y++)
477 for (x = start_x; x < (MAP_ARRAY_W + start_x); x++, map++) {
478 if (hide && ship_is_hidden(*map)) {
479 ship_draw(SHIP_WATER,x * SHIP_W,y * SHIP_H);
480 continue;
483 ship_draw(*map, x * SHIP_W, y * SHIP_H);
486 show_mouse(buffer);
490 * player_shoot(): Standard shooting function
492 * Return 1 if the shoot was valid, 0 otherwise
494 static int player_shoot(struct player_object *p, int x, int y)
496 int *ship;
498 ship = &p->map_array[y][x];
499 if (*ship < SHIP_FIRST || *ship > SHIP_LAST) {
500 die("%s: unknow ship code from '%s': %d\n", __FUNCTION__,
501 player_str(p));
504 if (ship_is_hit(*ship))
505 return 0;
507 *ship += 1; // SHIP_*_HIT
509 if (*ship != SHIP_WATER_HIT)
510 player_ship_nr_dec(p);
512 return 1;
516 * Map functions
520 * map_to_array(): Transforms (x, y) map positions into
521 * (x, y) low-level array positions
523 static void map_to_array(int mx, int my, int *ax, int *ay)
525 *ax = ROUNDDOWN(mx, SHIP_W) / SHIP_W;
526 *ay = ROUNDDOWN(my, SHIP_H) / SHIP_H;
530 * map_clear(): Clear the main map
532 static void map_clear(void)
534 clear_bitmap(buffer);
538 * User (player) related functions
542 * mouse_set_user_map(): Limit the mouse pointer in the user
543 * map range
545 static void mouse_set_user_map(void)
547 set_mouse_range(0, 0, MAP_ARRAY_W * SHIP_W - 1,
548 SCREEN_H - SHIP_H - 1);
552 * user_setup_mouse_callback(): Mouse callback used by the
553 * user setup code
555 static void user_setup_mouse_callback(int event)
557 int ship_nr, x, y;
559 if (event != MOUSE_FLAG_LEFT_DOWN)
560 return;
562 ship_nr = player_ship_nr(&player_user);
563 if (!ship_nr)
564 return;
566 map_to_array(mouse_x, mouse_y, &x, &y);
568 if (ship_nr > 7)
569 player_ship_set(&player_user, SHIP_SUB, x, y);
570 else if (ship_nr > 3)
571 player_ship_complex_set(&player_user, SHIP_DEST, x, y);
573 player_ship_complex_set(&player_user, SHIP_ARCC, x, y);
577 * user_setup(): Setup user map and array
579 static void user_setup(void)
581 player_array_dump(&player_user);
582 mouse_set_user_map();
584 game_install_mouse(user_setup_mouse_callback);
586 for (;;) {
587 if (!player_ship_nr(&player_user))
588 break;
590 if (player_ship_nr(&player_user) < 0) {
591 die("%s: user ships: %d\n", __FUNCTION__,
592 player_ship_nr(&player_user));
595 if (key[KEY_ESC])
596 die("%s: user abort\n", __FUNCTION__);
598 map_clear();
599 player_map_draw(&player_user, 0, 0, 0);
600 draw_sprite(screen, buffer, 0, 0);
603 game_remove_mouse();
604 player_array_dump(&player_user);
606 // Reset user's ship number, since it was set
607 // to zero by the setup code
608 player_ship_nr_def(&player_user);
612 * user_shoot(): fire!!
614 static int user_shoot(int x, int y)
616 // XXX: Not sure why is this -1 thing needed...
617 x--;
618 y--;
619 return player_shoot(&player_comp, x, y);
622 static void comp_shoot(void);
625 * user_shoot_mouse_callback(): Mouse callback used by the
626 * user's shoot code
628 static void user_shoot_mouse_callback(int event)
630 int ret, x, y;
632 if (event != MOUSE_FLAG_LEFT_DOWN)
633 return;
635 if (!player_ship_nr(&player_comp))
636 return;
638 if (!player_ship_nr(&player_user))
639 return;
641 map_to_array(mouse_x, mouse_y, &x, &y);
643 ret = user_shoot(x, y);
644 if (!ret) {
645 // If the shoot was invalid, we let the user
646 // try it again
647 return;
650 if (!player_ship_nr(&player_comp))
651 return;
654 * Okay, user's shoot was valid. Now it's the
655 * computer's turn.
657 * It's a bit kludge to call the computer's
658 * shoot function from the user one, but the computer
659 * can only shoot just after the user.
661 comp_shoot();
665 * Computer (player) related functions
669 * mouse_set_comp_map(): Limit the mouse pointer in the
670 * computer map range
672 static void mouse_set_comp_map(void)
674 set_mouse_range(MAP_ARRAY_W * SHIP_W + SHIP_W - 1, 0,
675 SCREEN_W - 1, SCREEN_H - SHIP_H - 1);
680 * comp_setup(): Setup computer's array
682 static void comp_setup(void)
684 int ship_nr, x, y;
686 while (player_ship_nr(&player_comp)) {
688 game_srand(&x, &y);
690 ship_nr = player_ship_nr(&player_comp);
691 if (ship_nr > 7)
692 player_ship_set(&player_comp, SHIP_SUB, x, y);
693 else if (ship_nr > 3)
694 player_ship_complex_set(&player_comp, SHIP_DEST,x,y);
695 else
696 player_ship_complex_set(&player_comp, SHIP_ARCC,x,y);
699 player_array_dump(&player_comp);
701 // Reset computer's ship number, since it was set
702 // to zero by the setup code
703 player_ship_nr_def(&player_comp);
707 * comp_shoot(): fire!!
709 static void comp_shoot(void)
711 int ret, x, y;
713 for (;;) {
714 game_srand(&x, &y);
715 ret = player_shoot(&player_user, x, y);
716 if (ret)
717 break;
721 int main(void)
723 game_init();
725 user_setup();
726 comp_setup();
728 game_install_mouse(user_shoot_mouse_callback);
729 mouse_set_comp_map();
731 while (!key[KEY_ESC] && !key[KEY_ENTER]) {
732 map_clear();
733 player_map_draw(&player_user, 0, 0, 0);
734 player_map_draw(&player_comp, MAP_ARRAY_W + 1, 0, 1);
736 draw_sprite(screen, buffer, 0, 0);
738 if (!player_ship_nr(&player_comp)) {
739 game_remove_mouse();
740 game_print("USER'S VICTORY!!");
743 if (!player_ship_nr(&player_user)) {
744 game_remove_mouse();
745 game_print("COMPUTER'S VICTORY!!");
749 game_deinit();
750 return 0;
752 END_OF_MAIN()