/* * Biloba * Copyright (C) 2004-2005 Guillaume Demougeot, Colin Leroy * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ /** * Biloba - Q1 2005 * Game by Guillaume Demougeot * Code by Colin Leroy * * This file contains all the rules related code. */ #include #include #include #include "utils.h" #include "tile.h" #include "pawn.h" #include "player.h" #include "logic.h" #include "arrow.h" #include "board.h" #include "font.h" #include "net.h" #include "msg.h" int compute_eats(PawnColor color) { int x = 0; int y = 0; int eaten = 0; LList *eaten_pawns = NULL, *cur = NULL; if (game_ended()) return FALSE; for (x = 0; x < MAX_TILES_X; x++) { for(y = 0; y < MAX_TILES_Y; y++) { Tile *tile = tile_get(x, y); LList *surrounding_pawns = NULL; LList *cur; if (tile->pawn == NULL) continue; if (tile->pawn->color != color) continue; surrounding_pawns = pawn_get_surroundings(tile); cur = surrounding_pawns; while (cur) { Pawn *opawn = (Pawn *)cur->data; if (pawn_try_and_eat(tile->pawn, opawn)) { eaten ++; eaten_pawns = llist_append(eaten_pawns, opawn); } cur = cur->next; } llist_free(surrounding_pawns); } } for (cur = eaten_pawns; cur; cur = cur->next) { Pawn *to_eat = (Pawn *)cur->data; if (!to_eat->eaten) pawn_eat(to_eat); } llist_free(eaten_pawns); return eaten; } int game_ended(void) { int i; int everyone_has_two = TRUE; int dead = 0; if (!game_inited()) return FALSE; for (i = 0; i < game_num_players(); i++) { int cnt = pawn_count((PawnColor)i); if (cnt == 0) dead++; if (cnt != 2) everyone_has_two = FALSE; } if (dead >= game_num_players() - 1) /* Only zero or one alive */ return TRUE; else if (game_num_players() - dead == 2) /* Only two alive */ return everyone_has_two; else return FALSE; } static void set_winner(void) { char *winner = NULL; int i; int winnernum = -1; for (i = 0; i < game_num_players(); i++) { int cnt = pawn_count((PawnColor)i); if (cnt >= 2 && winnernum == -1) { winnernum = i; } else if (cnt >= 2 && winnernum != -1) { winnernum = -1; break; /* this is a draw */ } net_close(); } if (winnernum == -1) { winner = strdup(get_msg(M_DRAW)); } else { Player *p = player_get((PawnColor)(winnernum), FALSE, (InputSystemMethod)NULL); if (p->name != NULL && strlen(p->name) > 0) { winner = malloc(strlen(p->name) + strlen(get_msg(M_WINS)) + 1); strcpy(winner, p->name); } else { char num[2] = {winnernum+1+'0', 0}; winner = malloc(strlen(get_msg(M_PLAYER_WINS))+1); strcpy(winner, get_msg(M_PLAYER)); strcat(winner, num); } strcat(winner, get_msg(M_WINS)); } clear_text(20/* max len, probably */, 20 + 50, YS - 50); draw_message(winner, 20 + 50, YS - 50, FALSE); free(winner); SDL_Delay(1500); } void play_game(void) { int cur_player = -1; set_playing(TRUE); next_player: while (!game_ended()) { Player *player = NULL; Tile *start = NULL, *end = NULL; LList *surr = NULL, *cur = NULL; int on_center = FALSE; Tile *initial_start = NULL; int skip_turn = FALSE; LList *eatables = NULL; cur_player++; if (cur_player >= game_num_players()) cur_player = 0; #ifdef DEBUG printf("playing: %d\n", cur_player); #endif player = player_get((PawnColor)cur_player, FALSE, (InputSystemMethod)NULL); assert(player != NULL); board_set_player(player); SDL_Delay(500); while (compute_eats(player->color) > 0) { ask_replacements(player); skip_turn = TRUE; } if (pawn_count(player->color) == 0) continue; /* next player */ if (pawn_count(player->color) <= 2 && (eatables = player_can_eat_soon(player, FALSE, NULL)) == NULL) { LList *pawns = pawn_get_all(player->color); LList *cur = pawns; while (cur) { pawn_eat((Pawn *)cur->data); cur = cur->next; } #ifdef DEBUG printf("Player %d lost\n", cur_player); #endif continue; /* next player */ } #ifdef DEBUG printf("pawn_count %d, can_eat %d\n", pawn_count(player->color), llist_length(eatables)); #endif llist_free(eatables); eatables = NULL; if (skip_turn) { continue; } rechoose: while (!start || !start->pawn) { start = player_select_tile(player, NULL); if (start && start->pawn && start->pawn->color != player->color) start = NULL; if (pawn_count(player->color) == 0) { /* must have quit! */ goto next_player; } if (game_ended()) goto end_loop; } #ifdef DEBUG printf("got start tile (%d, %d)\n", start->pos_x, start->pos_y); #endif tile_highlight(start, 128); while (!end || !llist_find(surr, end)) { LList *allowed = NULL; llist_free(surr); surr = tile_get_accessible_surroundings(start); if (on_center) { assert(initial_start); surr = llist_remove(surr, initial_start); allowed = llist_append(allowed, start->pawn); } tile_highlight(start, 128); for (cur = surr; cur ; cur = cur->next) { tile_draw((Tile *)cur->data); tile_draw_arrow(start, (Tile *)cur->data); } end = player_select_tile(player, allowed); llist_free(allowed); allowed = NULL; if (pawn_count(player->color) == 0) { /* must have quit! */ goto next_player; } if (game_ended()) goto end_loop; /* remove arrows and highlight */ tile_highlight(start, 0); for (cur = surr; cur ; cur = cur->next) tile_draw((Tile *)cur->data); if (end && (start == end || end->pawn != NULL)) { if (!on_center) { start = NULL; initial_start = NULL; } end = NULL; goto rechoose; } } #ifdef DEBUG printf("got end tile (%d, %d)\n", end->pos_x, end->pos_y); #endif llist_free(surr); assert(start != NULL); assert(end != NULL); assert(start->pawn != NULL); assert(end->pawn == NULL); tile_highlight(start, 0); pawn_move_to(start->pawn, end); tile_draw(start); tile_draw(end); if (end->type == TILE_CENTER) { /* get new end */ on_center = TRUE; surr = tile_get_accessible_surroundings(start); initial_start = start; start = end; goto rechoose; } else { on_center = FALSE; initial_start = NULL; } while (compute_eats(player->color) > 0) { ask_replacements(player); } SDL_UpdateRect(screen, 0, 0, 0, 0); } end_loop: set_playing(FALSE); set_winner(); } void ask_replacements(Player *player) { LList *pawns = pawn_get_replacement_pending_pawns(player->color); LList *cur = pawns; if (arrow_draw_all(player)) goto again; if (pawns) { Tile *start = NULL, *end = NULL; Pawn *moved_pawn = NULL; while (!start || !start->pawn) { start = player_select_tile(player, pawns); if (start && start->pawn && !llist_find(pawns, start->pawn)) start = NULL; if (pawn_count(player->color) == 0) { /* must have quit! */ return; } if (game_ended()) return; } #ifdef DEBUG printf("got start tile (%d, %d)\n", start->pos_x, start->pos_y); #endif while (!end || !llist_find(start->pawn->just_ate_on, end)) { LList *allowed = llist_append(NULL, start->pawn); tile_highlight(start, 128); for (cur = start->pawn->just_ate_on; cur ; cur = cur->next) { tile_draw((Tile *)cur->data); } arrow_draw_all(player); end = player_select_tile(player, allowed); llist_free(allowed); allowed = NULL; if (pawn_count(player->color) == 0) { /* must have quit! */ return; } if (game_ended()) return; tile_draw(start); if (start == end || end->pawn != NULL) { goto again; } } #ifdef DEBUG printf("got end tile (%d, %d)\n", end->pos_x, end->pos_y); #endif assert(start && start->pawn && llist_find(pawns, start->pawn)); assert(end && !end->pawn); moved_pawn = start->pawn; pawn_move_to(moved_pawn, end); if (tile_get_next_in_row(start, end)) tile_draw(tile_get_next_in_row(start, end)); /* repaint */ cur = pawns; while (cur) { Pawn *pawn = (Pawn *)cur->data; LList *to_tiles = pawn->just_ate_on; while(to_tiles) { Tile *to_tile = (Tile *)to_tiles->data; Tile *third = tile_get_next_in_row(pawn_get_tile(pawn), to_tile); tile_draw(pawn_get_tile(pawn)); tile_draw(to_tile); if (third) tile_draw(third); to_tiles = to_tiles->next; } cur = cur->next; } llist_free(moved_pawn->just_ate_on); moved_pawn->just_ate_on = NULL; pawn_remove_just_eaten(end); SDL_UpdateRect(screen, 0, 0, 0, 0); cur = pawns; again: llist_free(pawns); ask_replacements(player); return; } } void stop_game(void) { int i, j; if (!game_inited()) { notify_quit(); return; } game_suspend(TRUE); for (j = SDL_ALPHA_TRANSPARENT; j <= SDL_ALPHA_OPAQUE; j++) { for (i = 0; i < game_num_players(); i++) { LList *pawns = pawn_get_all((PawnColor)i); for (; pawns; pawns = pawns->next) { Pawn *pawn = (Pawn *)pawns->data; pawn_highlight(pawn, j); if (j == SDL_ALPHA_OPAQUE) pawn->eaten = TRUE; } } } for (i = 0; i < game_num_players(); i++) { Player *player = player_get((PawnColor)i, FALSE, (InputSystemMethod)NULL); if (player->method != INPUT_NETWORK) { #ifdef DEBUG printf("sending end game for %d\n", i); #endif net_end_game(i); } } game_suspend(FALSE); notify_quit(); } void end_game(void) { int i; if (!game_inited()) return; game_suspend(TRUE); for (i = 0; i < game_num_players(); i++) { LList *pawns = pawn_get_all((PawnColor)i); for (; pawns; pawns = pawns->next) { Pawn *pawn = (Pawn *)pawns->data; if (!pawn->eaten) pawn_eat_fast(pawn, TRUE); } pawn_show_eaten(); } for (i = 0; i < game_num_players(); i++) { Player *player = player_get((PawnColor)i, FALSE, (InputSystemMethod)NULL); if (player->method != INPUT_NETWORK) { #ifdef DEBUG printf("sending end game for %d\n", i); #endif net_end_game(i); } } game_suspend(FALSE); net_close(); } int can_move_on_center(void) { if (tile_get(3,3)->pawn == NULL || tile_get(3,4)->pawn == NULL || tile_get(3,5)->pawn == NULL || tile_get(4,3)->pawn == NULL || tile_get(4,5)->pawn == NULL || tile_get(5,3)->pawn == NULL || tile_get(5,4)->pawn == NULL || tile_get(5,5)->pawn == NULL) return TRUE; else return FALSE; }