260 lines
8.3 KiB
C
260 lines
8.3 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include "game_ext.h"
|
|
#include "game_private.h"
|
|
|
|
// JWRAPPING and IWRAPPING have a "+ game_nb_xxxx(g)" to avoid falling in negative when we are at 0 for exemple
|
|
#define JWRAPPING (j + game_nb_cols(g) + gap_position * tab[index_tab + 1]) % game_nb_cols(g)
|
|
#define IWRAPPING (i + game_nb_rows(g) + gap_position * tab[index_tab]) % game_nb_rows(g)
|
|
#define JNORMAL j + gap_position* tab[index_tab + 1]
|
|
#define INORMAL i + gap_position* tab[index_tab]
|
|
|
|
game game_new(square* squares) {
|
|
return game_new_ext(7, 7, squares, false);
|
|
}
|
|
|
|
game game_new_empty(void) {
|
|
return game_new_empty_ext(7, 7, false);
|
|
}
|
|
|
|
game game_copy(cgame g1) {
|
|
game g2 = game_new_empty_ext(g1->height, g1->width, g1->wrapping);
|
|
for (uint i = 0; i < g2->height; i++) {
|
|
for (uint j = 0; j < g2->width; j++) {
|
|
g2->tab_cell[i][j] = g1->tab_cell[i][j];
|
|
}
|
|
}
|
|
// delete first allocated hist (by game new) of g2
|
|
history_delete_entire_history(g2->hist);
|
|
g2->hist = history_copy(g1->hist);
|
|
return g2;
|
|
}
|
|
|
|
bool game_equal(cgame g1, cgame g2) {
|
|
// check if the two games have the same options
|
|
if (g1->height != g2->height || g1->width != g2->width || g1->wrapping != g2->wrapping)
|
|
return false;
|
|
// check each cell and seek any difference between g1 and g2
|
|
for (uint i = 0; i < g1->height; i++) {
|
|
for (uint j = 0; j < g1->width; j++) {
|
|
if (g1->tab_cell[i][j] != g2->tab_cell[i][j]) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void game_delete(game g) {
|
|
// free each tab of tab_cell
|
|
for (uint i = 0; i < game_nb_rows(g); i++) {
|
|
free(g->tab_cell[i]);
|
|
g->tab_cell[i] = NULL;
|
|
}
|
|
free(g->tab_cell);
|
|
g->tab_cell = NULL;
|
|
history_delete_entire_history(g->hist);
|
|
g->hist = NULL;
|
|
free(g);
|
|
g = NULL;
|
|
}
|
|
|
|
void game_set_square(game g, uint i, uint j, square s) {
|
|
g->tab_cell[i][j] = s;
|
|
return;
|
|
}
|
|
|
|
square game_get_square(cgame g, uint i, uint j) {
|
|
return g->tab_cell[i][j];
|
|
}
|
|
|
|
square game_get_state(cgame g, uint i, uint j) {
|
|
square temp = g->tab_cell[i][j];
|
|
square list_flag[] = {F_ERROR, F_LIGHTED};
|
|
for (uint flag_index = 0; flag_index < sizeof(list_flag) / sizeof(list_flag[0]); flag_index++) {
|
|
if (temp >= list_flag[flag_index]) {
|
|
temp -= list_flag[flag_index];
|
|
}
|
|
}
|
|
return temp;
|
|
}
|
|
|
|
square game_get_flags(cgame g, uint i, uint j) {
|
|
square temp = g->tab_cell[i][j];
|
|
square list_flag[] = {(F_ERROR | F_LIGHTED), F_ERROR, F_LIGHTED};
|
|
for (uint flag_index = 0; flag_index < sizeof(list_flag) / sizeof(list_flag[0]); flag_index++) {
|
|
if (temp >= list_flag[flag_index]) {
|
|
return list_flag[flag_index];
|
|
}
|
|
}
|
|
return (S_BLANK);
|
|
}
|
|
|
|
bool game_is_blank(cgame g, uint i, uint j) {
|
|
return game_get_state(g, i, j) == S_BLANK;
|
|
}
|
|
|
|
bool game_is_lightbulb(cgame g, uint i, uint j) {
|
|
return game_get_state(g, i, j) == S_LIGHTBULB;
|
|
}
|
|
|
|
bool game_is_black(cgame g, uint i, uint j) {
|
|
return game_get_state(g, i, j) >= S_BLACK0 && game_get_state(g, i, j) <= S_BLACKU;
|
|
}
|
|
|
|
int game_get_black_number(cgame g, uint i, uint j) {
|
|
if (game_get_state(g, i, j) == S_BLACKU)
|
|
return -1;
|
|
return game_get_state(g, i, j) - S_BLACK;
|
|
}
|
|
|
|
bool game_is_marked(cgame g, uint i, uint j) {
|
|
return game_get_state(g, i, j) == S_MARK;
|
|
}
|
|
|
|
bool game_is_lighted(cgame g, uint i, uint j) {
|
|
return game_get_flags(g, i, j) >= F_LIGHTED;
|
|
}
|
|
|
|
bool game_has_error(cgame g, uint i, uint j) {
|
|
return g->tab_cell[i][j] >= F_ERROR;
|
|
}
|
|
|
|
bool game_check_move(cgame g, uint i, uint j, square s) {
|
|
return (i < game_nb_rows(g) && j < game_nb_cols(g) && (s == S_BLANK || s == S_MARK || s == S_LIGHTBULB) && !game_is_black(g, i, j));
|
|
}
|
|
|
|
void game_play_move(game g, uint i, uint j, square s) {
|
|
// shouldn't happen because we initalized in game new
|
|
if (g->hist == NULL) {
|
|
g->hist = history_create_empty();
|
|
g->hist = history_insert_first(g->hist, game_get_state(g, i, j), i, j);
|
|
} else {
|
|
// to avoid redo after play
|
|
g->hist = history_delete_all_after(g->hist, g->hist);
|
|
// add new played element to hist and get the last element of hist
|
|
g->hist = history_next(history_insert_after(g->hist, g->hist, game_get_state(g, i, j), i, j));
|
|
}
|
|
game_set_square(g, i, j, s);
|
|
game_update_flags(g);
|
|
}
|
|
|
|
void delete_flags(game g) {
|
|
// delete prev flags
|
|
for (uint i = 0; i < game_nb_rows(g); i++) {
|
|
for (uint j = 0; j < game_nb_cols(g); j++) {
|
|
game_set_square(g, i, j, game_get_state(g, i, j));
|
|
}
|
|
}
|
|
}
|
|
|
|
void addF_LIGHTED(game g, uint i, uint j) {
|
|
square flags = game_get_flags(g, i, j);
|
|
if (flags == S_BLANK)
|
|
game_set_square(g, i, j, game_get_state(g, i, j) | F_LIGHTED);
|
|
else if (flags == F_ERROR) {
|
|
game_set_square(g, i, j, game_get_state(g, i, j) | F_LIGHTED | F_ERROR);
|
|
}
|
|
}
|
|
|
|
void update_flags_lightbulb(game g, uint i, uint j) {
|
|
// add F_LIGHTED to the current lightbulb
|
|
addF_LIGHTED(g, i, j);
|
|
// add F_LIGHTED on the 4 directions, when reaching a wall by following a direction, remove it from tab, when reaching another lightbulb, put a
|
|
// F_ERROR on the initial lightbulb
|
|
// tab[] = { i, j, i, j, i, j, i, j}
|
|
int tab[] = {-1, 0, 1, 0, 0, -1, 0, 1};
|
|
for (uint gap_position = 1; gap_position < max(game_nb_cols(g), game_nb_rows(g)); gap_position++) {
|
|
// index_tab => pair = i
|
|
// index_tab => odd = j
|
|
for (uint index_tab = 0; index_tab < sizeof(tab) / sizeof(tab[0]); index_tab += 2) {
|
|
// big if to check if INORMAL and JNORMAL are correct or if wrapping is true and IWRAPPING and JWRAPPING are not the initial case
|
|
if (/* test if both tab are not 0*/ tab[index_tab] != tab[index_tab + 1] &&
|
|
(/* normal check */ (JNORMAL < game_nb_cols(g) && INORMAL < game_nb_rows(g)) ||
|
|
/* wrapping check + not the inital case*/ (game_is_wrapping(g) && !(IWRAPPING == i && JWRAPPING == j)))) {
|
|
if (game_is_black(g, IWRAPPING, JWRAPPING)) {
|
|
tab[index_tab] = 0;
|
|
tab[index_tab + 1] = 0;
|
|
} else {
|
|
// if we found another lightbulb on the line
|
|
if (game_is_lightbulb(g, IWRAPPING, JWRAPPING))
|
|
// update initial lightbulb on F_ERROR
|
|
game_set_square(g, i, j, (S_LIGHTBULB | F_LIGHTED | F_ERROR));
|
|
addF_LIGHTED(g, IWRAPPING, JWRAPPING);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void update_flags_walls(game g, uint i, uint j) {
|
|
int lightbulb = 0;
|
|
int not_empty = 0;
|
|
int tab[] = {-1, 0, 1, 0, 0, -1, 0, 1};
|
|
for (uint index_tab = 0; index_tab < (sizeof(tab) / sizeof(tab[0])); index_tab += 2) {
|
|
uint gap_position = 1;
|
|
if (/* normal check*/ (JNORMAL < game_nb_cols(g) && INORMAL < game_nb_rows(g)) ||
|
|
/*wrapping check*/ (game_is_wrapping(g) &&
|
|
/*not the same case*/ !(IWRAPPING == i && JWRAPPING == j))) {
|
|
if (game_is_lightbulb(g, IWRAPPING, JWRAPPING))
|
|
lightbulb++;
|
|
else if (!game_is_blank(g, IWRAPPING, JWRAPPING) || game_is_lighted(g, IWRAPPING, JWRAPPING))
|
|
not_empty++;
|
|
} else {
|
|
not_empty++;
|
|
}
|
|
}
|
|
if (lightbulb > game_get_black_number(g, i, j)) {
|
|
game_set_square(g, i, j, (game_get_state(g, i, j) | F_ERROR));
|
|
}
|
|
// look if there is enough empty cell around the wall (4 for the four directions)
|
|
else if (not_empty > abs(game_get_black_number(g, i, j) - 4)) {
|
|
game_set_square(g, i, j, (game_get_state(g, i, j) | F_ERROR));
|
|
}
|
|
}
|
|
|
|
void game_update_flags(game g) {
|
|
delete_flags(g);
|
|
// update flags lighted and lightbulb error
|
|
for (uint i = 0; i < game_nb_rows(g); i++) {
|
|
for (uint j = 0; j < game_nb_cols(g); j++) {
|
|
if (game_is_lightbulb(g, i, j)) {
|
|
update_flags_lightbulb(g, i, j);
|
|
}
|
|
}
|
|
}
|
|
// update flags wall (need to wait all FLIGHTED being updated)
|
|
for (uint i = 0; i < game_nb_rows(g); i++) {
|
|
for (uint j = 0; j < game_nb_cols(g); j++) {
|
|
if (game_is_black(g, i, j) && game_get_black_number(g, i, j) >= 0) {
|
|
update_flags_walls(g, i, j);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool game_is_over(cgame g) {
|
|
for (uint i = 0; i < game_nb_rows(g); i++) {
|
|
for (uint j = 0; j < game_nb_cols(g); j++) {
|
|
if ((game_get_state(g, i, j) == S_BLANK && game_get_flags(g, i, j) == S_BLANK) ||
|
|
(game_get_state(g, i, j) == S_MARK && game_get_flags(g, i, j) == S_BLANK) || game_get_flags(g, i, j) == F_ERROR ||
|
|
game_get_flags(g, i, j) == (F_LIGHTED | F_ERROR))
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void game_restart(game g) {
|
|
for (uint i = 0; i < game_nb_rows(g); i++) {
|
|
for (uint j = 0; j < game_nb_cols(g); j++) {
|
|
if (game_get_state(g, i, j) == S_MARK || game_is_lightbulb(g, i, j))
|
|
game_set_square(g, i, j, S_BLANK);
|
|
}
|
|
}
|
|
game_update_flags(g);
|
|
history_delete_entire_history(g->hist);
|
|
// insert f_error on first cell of histo. initalized to avoid bug on first move undo
|
|
g->hist = history_insert_first(history_create_empty(), F_ERROR, 0, 0);
|
|
}
|