From 88f92e8f2d59d6d56b54f6561c0bfcdb8e8ed9c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophyl=20Hoguet?= Date: Mon, 7 Mar 2022 03:50:54 +0100 Subject: [PATCH] comments --- game_tools.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 106 insertions(+), 9 deletions(-) diff --git a/game_tools.c b/game_tools.c index f33b4b9..2701ee2 100644 --- a/game_tools.c +++ b/game_tools.c @@ -94,6 +94,13 @@ void game_save(cgame g, char* filename) { fclose(f); } +/** + * @brief Test if there is any error on the entire game + * + * @param g the game to analyze + * @return true if there is 1 or more error on the game g + * @return false if there is no one error on the game g + */ bool game_has_error_general(cgame g) { for (uint i = 0; i < game_nb_rows(g); i++) { for (uint j = 0; j < game_nb_cols(g); j++) { @@ -104,6 +111,22 @@ bool game_has_error_general(cgame g) { return false; } +/** + * @brief Change the array diagToTest who must containt coordinate i,j of a case else -1 if the cases are not on the same row / column take the cross point of + * the two points + * // clang off + * 0 1 2 + * 0 a - b + * 1 x 2 y + * 2 c z d + * // clang on + * in this case the diagToTest must containt (zi,zj,xi,xj,yi,yj,?,?) (order doesn't matter) coordinates [2,1,0,1,1,0,-1,-1] and the function change this + * coordinate of the points in the array to the cross points of these points in this case the array should look like this : [2,0,2,2,?,?,?,?] + * ([ci,cj,di,dj,?,?,?,?]) and return the number of changed elements on diagToTest + * @param diagToTest the tab to test and change + * @param size the size of diagToTest + * @return uint the number of element on the changed diagToTest + */ uint coordinateCrossPointMaker(int* diagToTest, uint size) { int tab[size]; uint cpt = 0; @@ -122,6 +145,20 @@ uint coordinateCrossPointMaker(int* diagToTest, uint size) { return cpt; } +/** + * @brief try to add a s square on blank cases in the directions of the array dir (who must have be fill with coef (-1 ; 0 ; 1) of directions like this : + * [i,j,i,j,i,j,i,j]) to the i,j case in a 1 to max_gap distance from the i,j case if force is set to true place the square on lighted blank too (if there is an + * error undo all moves and return 0) + * + * @param g the game to change + * @param i the i coordinate of the case to add around + * @param j the j coordinate of the case to add around + * @param s the square to add around the i j case + * @param max_gap the max distance to add the s square around the i j case + * @param dir the array containing coef of directions + * @param force true => place the s square on lighted blank too else only on blank + * @return uint - the number of placed square (if there is an error undo all moves and return 0) + */ uint addAround(game g, uint i, uint j, square s, uint max_gap, int* dir, bool force) { uint move_played = 0; for (uint gap_position = 1; gap_position <= max_gap; gap_position++) { @@ -153,6 +190,21 @@ uint addAround(game g, uint i, uint j, square s, uint max_gap, int* dir, bool fo return move_played; } +/** + * @brief look the cases around the i j case of the game g from 1 case distance to max_gap distance and fill not_empty, lightbulb, mark, blank_not_lighted + * variables with the number of occurence found. dirDiagoToEdit is an optional param who contain the coordinates of the blank squares + * + * @param g the game + * @param i the i coordinate of the case to check around + * @param j the j coordinate of the case to check around + * @param max_gap the max distance to check around the i j coordinate + * @param not_empty the variable to edit with the number of not empty case around i j + * @param lightbulb the variable to edit with the number of lightbulb around i j + * @param mark the variable to edit with the number of mark around i j + * @param blank_not_lighted the variable to edit with the number of blank_not_lighted around i j + * @param dirDiagoToEdit (optional) must be allocated with the maximum possible coordinate (4*2*gap_distance) will be edit with the coordinate of all blank + * squares around i j + */ void checkAround(cgame g, uint i, uint j, uint max_gap, int* not_empty, int* lightbulb, int* mark, int* blank_not_lighted, int* dirDiagoToEdit) { int dir[] = {1, 0, -1, 0, 0, 1, 0, -1}; for (uint gap_position = 1; gap_position <= max_gap; gap_position++) { @@ -185,7 +237,8 @@ void checkAround(cgame g, uint i, uint j, uint max_gap, int* not_empty, int* lig } } /** - * @brief analyze the game and return the number of move played / special number + * @brief analyze the game and play moves that are logical / mandatory. It stops only when it don't play any move and return the number of move played / special + * number * * @param g the game to analyze * @return -1 if game_is_over ; -2 if the game has no sol ; else the number of move played @@ -199,6 +252,11 @@ int game_analyze(game g) { int dirLine[] = {1, 0, -1, 0, 0, 1, 0, -1}; int dirDiago[8] = {-1, -1, -1, -1, -1, -1, -1, -1}; uint tmp = 0; + /** + * @brief if the case is a blank (not lighted) test if there is only one blank square (itself) in all the directions if yes set a lightbulb (if + * the lightbulb create an error the game is unsolvable => undo all moves and return -2) + * + */ if (game_get_square(g, i, j) == S_BLANK) { int lb = 0, not_empty = 0, mark = 0, blank = 0; checkAround(g, i, j, max(game_nb_cols(g), game_nb_rows(g)), ¬_empty, &lb, &mark, &blank, NULL); @@ -215,6 +273,12 @@ int game_analyze(game g) { return -2; } } + /** + * @brief if the square is a wall between 1 and 4 test if there is the number of lightbulb already placed => place marks on all the blanks + * (if there is an error the game is unsolvable) square around the wall, else test if there is the number of blank (not lighted) needed by + * the wall around it => set lightbulb around the wall (if there is an error the game is unsolvable) + * + */ } else if (game_get_state(g, i, j) >= S_BLACK1 && game_get_state(g, i, j) < S_BLACKU) { int lb = 0, not_empty = 0, mark = 0, blank = 0; checkAround(g, i, j, 1, ¬_empty, &lb, &mark, &blank, dirDiago); @@ -232,7 +296,12 @@ int game_analyze(game g) { move_played += tmp; if (game_is_over(g)) return -1; - } else if (blank == game_get_black_number(g, i, j) - lb + 1) { + } + /** + * @brief if this statement is true that means a single lightbulb (set on diagonals of the wall) can obfuscate two blanks around the wall + * and produce an error so we can place mark on these cases + */ + else if (blank == game_get_black_number(g, i, j) - lb + 1) { uint cpt = coordinateCrossPointMaker(dirDiago, 8); for (uint cpt_index = 0; cpt_index < cpt; cpt_index += 2) { if (game_get_square(g, dirDiago[cpt_index], dirDiago[cpt_index + 1]) == S_BLANK) { @@ -241,12 +310,17 @@ int game_analyze(game g) { } } } - } else if (game_get_state(g, i, j) == S_BLACK0) { + } // we can set marks all around a wall0 because it must not have lightbulb right next to it + else if (game_get_state(g, i, j) == S_BLACK0) { move_played += addAround(g, i, j, S_MARK, 1, dirLine, true); if (game_is_over(g)) return -1; - - } else if (game_get_state(g, i, j) == S_MARK && !game_is_lighted(g, i, j)) { + } /** + * @brief if the mark is not lighted and there is only one blank in all the directions we can place a lightbulb (if there is an error the game + * is unsolvable) + * + */ + else if (game_get_state(g, i, j) == S_MARK && !game_is_lighted(g, i, j)) { int lb = 0, not_empty = 0, mark = 0, blank = 0; checkAround(g, i, j, max(game_nb_cols(g), game_nb_rows(g)), ¬_empty, &lb, &mark, &blank, NULL); if (blank == 1) @@ -269,6 +343,17 @@ int game_analyze(game g) { return total_move_played; } +/** + * @brief the brutforce fonction set a lightbulb on each case (recursive call while deep > 1) if the lightbulb produce an error undo it and go to the next + * coordinate on each new lightbulb who isn't producing error call game_analyze to see if it can help / say if the game is unsolvable (and set a mark on this + * case) + * + * @param g the game to brutforce + * @param deep the deepness of the brutforce + * @param move_played the number of moves played (to undo in case of unsolvable game) + * @return true if the game is solved + * @return false if the game is unsolvable + */ bool aux_game_solve(game g, uint deep, int* move_played) { for (uint i = 0; i < game_nb_rows(g); i++) { for (uint j = 0; j < game_nb_cols(g); j++) { @@ -306,17 +391,29 @@ bool aux_game_solve(game g, uint deep, int* move_played) { return false; } +uint total_number_of_blank_cases(game g) { + uint cpt = 0; + for (uint i = 0; i < game_nb_cols(g); i++) { + for (uint j = 0; j < game_nb_rows(g); j++) { + if (game_get_state(g, i, j) == S_BLANK) + cpt++; + } + } + return cpt; +} + bool game_solve(game g) { - // TODELETE - // game_print(g); + // first analyze of the game to reduce the number of brutforce needed int total_move_played = game_analyze(g); - // to keep the first analyze + // give move_played and not total_move_played to keep the first analyze int move_played = 0; if (total_move_played == -1) return true; + // if the game is unsolvalbe at the first analyze it's unsolvable even with brutforce if (total_move_played == -2) return false; - for (uint deep = 1; deep < game_nb_cols(g) * game_nb_rows(g); deep++) { + // else try brutforce with deepness going from 1 to the total number of blank cases on the game + for (uint deep = 1; deep < total_number_of_blank_cases(g); deep++) { total_move_played += move_played; if (aux_game_solve(g, deep, &move_played)) { return true;