diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b909c8c --- /dev/null +++ b/Makefile @@ -0,0 +1,25 @@ +CC=gcc # compilateur +CFLAGS=-Wall -std=c99 # options de compilation +LDFLAGS=-L. # options de link +LDLIBS=-lgame # bibliothèques +CPPFLAGS= # options de preprocessing +AR= ar + +all: game_text + +libgame.a: game_aux.o game.o + $(AR) rcs $@ $^ + +game_text: game_text.o libgame.a + $(CC) $(LDFLAGS) $^ -o $@ $(LDLIBS) + +.PHONY: clean dep + +include dep + +# génération des dépendances +dep: + $(CC) -MM *.c > dep + +clean: + rm -f dep game_aux.o *~ game_text game_text.o libgame.a \ No newline at end of file diff --git a/game.h b/game.h new file mode 100644 index 0000000..309b6fe --- /dev/null +++ b/game.h @@ -0,0 +1,314 @@ +/** + * @file game.h + * @brief Basic Game Functions. + * @details See @ref index for further details. + * @copyright University of Bordeaux. All rights reserved, 2021. + **/ + +#ifndef __GAME_H__ +#define __GAME_H__ +#include + +/** + * @brief Standard unsigned integer type. + **/ +typedef unsigned int uint; + +/** + * @brief Size of the default game grid. + **/ +#define DEFAULT_SIZE 7 + +/** + * @brief Different squares used in the game. + * @details The value of a square is an integer represented on one byte, with a + * low-order half-byte that codes for the square state (blank, black wall, light + * bulb or mark) and a high-order half-byte that codes for the additionnal + * square flags (lighted or error). While each square has a single state, it can + * have zero, one or several flags (combined with bitwise operations). For + * instance, a light bulb can have both the lighted and error flags. In this + * case, the raw square value is S_LIGHTBULB | F_LIGHTED | F_ERROR, using the + * bitwise OR operator (|). For more details on bitwise operations, see + * https://en.wikipedia.org/wiki/Bitwise_operations_in_C. + **/ +typedef enum { + /* states */ + S_BLANK = 0, /**< a blank square */ + S_LIGHTBULB = 1, /**< a light bulb */ + S_MARK = 2, /**< a marked square (as not-a-light) */ + S_BLACK = 8, /**< black wall base code */ + S_BLACK0 = S_BLACK, /**< a numbered black wall (with 0 adjacent lights) */ + S_BLACK1, /**< a numbered black wall (with 1 adjacent light) */ + S_BLACK2, /**< a numbered black wall (with 2 adjacent lights) */ + S_BLACK3, /**< a numbered black wall (with 3 adjacent lights) */ + S_BLACK4, /**< a numbered black wall (with 4 adjacent lights) */ + S_BLACKU, /**< an unnumbered black wall (any number of adjacent lights) */ + /* flags */ + F_LIGHTED = 16, /**< lighted flag */ + F_ERROR = 32 /**< error flag */ +} square; + +/** state mask used in square enum */ +#define S_MASK 0x0F + +/** flag mask used in square enum */ +#define F_MASK 0xF0 + +/** square mask used in square enum */ +#define A_MASK 0xFF + +/** + * @brief The structure pointer that stores the game state. + **/ +typedef struct game_s *game; + +/** + * @brief The structure constant pointer that stores the game state. + * @details That means that it is not possible to modify the game using this + * pointer. + **/ +typedef const struct game_s *cgame; + +/** + * @brief Creates a new game with default size and initializes it. + * @param squares an array describing the initial square values (row-major + * storage) + * @pre @p squares must be an initialized array of default size squared. + * @return the created game + **/ +game game_new(square *squares); + +/** + * @brief Creates a new empty game with defaut size. + * @details All squares are initialized with blank squares. + * @return the created game + **/ +game game_new_empty(void); + +/** + * @brief Duplicates a game. + * @param g the game to copy + * @return the copy of the game + * @pre @p g must be a valid pointer toward a game structure. + **/ +game game_copy(cgame g); + +/** + * @brief Tests if two games are equal (same states and same flags). + * @param g1 the first game + * @param g2 the second game + * @return true if the two games are equal, false otherwise + * @pre @p g1 must be a valid pointer toward a game structure. + * @pre @p g2 must be a valid pointer toward a game structure. + **/ +bool game_equal(cgame g1, cgame g2); + +/** + * @brief Deletes the game and frees the allocated memory. + * @param g the game to delete + * @pre @p g must be a valid pointer toward a game structure. + **/ +void game_delete(game g); + +/** + * @brief Sets the value of a given square. + * @details This function is useful for initializing the squares of an empty + * game. + * @param g the game + * @param i row index + * @param j column index + * @param s the square value + * @pre @p g must be a valid pointer toward a game structure. + * @pre @p i < game height + * @pre @p j < game width + * @pre @p s must be a valid square value. + **/ +void game_set_square(game g, uint i, uint j, square s); + +/** + * @brief Gets the raw value of a given square. + * @param g the game + * @param i row index + * @param j column index + * @pre @p g must be a valid pointer toward a game structure. + * @pre @p i < game height + * @pre @p j < game width + * @return the square value + **/ +square game_get_square(cgame g, uint i, uint j); + +/** + * @brief Gets the state of a given square. + * @details See description of enum square. + * @param g the game + * @param i row index + * @param j column index + * @pre @p g must be a valid pointer toward a game structure. + * @pre @p i < game height + * @pre @p j < game width + * @return the square state + **/ +square game_get_state(cgame g, uint i, uint j); + +/** + * @brief Gets the flags of a given square. + * @details See description of enum square. + * @param g the game + * @param i row index + * @param j column index + * @pre @p g must be a valid pointer toward a game structure. + * @pre @p i < game height + * @pre @p j < game width + * @return the square flags + **/ +square game_get_flags(cgame g, uint i, uint j); + +/** + * @brief Test if a given square is blank. + * @param g the game + * @param i row index + * @param j column index + * @pre @p g must be a valid pointer toward a game structure. + * @pre @p i < game height + * @pre @p j < game width + * @return true if the square is blank + **/ +bool game_is_blank(cgame g, uint i, uint j); + +/** + * @brief Test if a given square is a lightbulb. + * @param g the game + * @param i row index + * @param j column index + * @pre @p g must be a valid pointer toward a game structure. + * @pre @p i < game height + * @pre @p j < game width + * @return true if the square is a lightbulb + **/ +bool game_is_lightbulb(cgame g, uint i, uint j); + +/** + * @brief Test if a given square is black (whether or not it is numbered). + * @param g the game + * @param i row index + * @param j column index + * @pre @p g must be a valid pointer toward a game structure. + * @pre @p i < game height + * @pre @p j < game width + * @return true if the square is black + **/ +bool game_is_black(cgame g, uint i, uint j); + +/** + * @brief Get the number of lightbulbs expected against a black wall. + * @param g the game + * @param i row index + * @param j column index + * @pre @p g must be a valid pointer toward a game structure. + * @pre @p i < game height + * @pre @p j < game width + * @return the back wall number, or -1 if it is unumbered + **/ +int game_get_black_number(cgame g, uint i, uint j); + +/** + * @brief Test if a given square is marked (as not-a-light). + * @param g the game + * @param i row index + * @param j column index + * @pre @p g must be a valid pointer toward a game structure. + * @pre @p i < game height + * @pre @p j < game width + * @return true if the square is marked + **/ +bool game_is_marked(cgame g, uint i, uint j); + +/** + * @brief Test if a given square is lighted. + * @param g the game + * @param i row index + * @param j column index + * @pre @p g must be a valid pointer toward a game structure. + * @pre @p i < game height + * @pre @p j < game width + * @return true if the square is lighted + **/ +bool game_is_lighted(cgame g, uint i, uint j); + +/** + * @brief Test if a given square has an error flag + * @details An error can only occur on an numbered black wall or on a light + * bulb. + * @param g the game + * @param i row index + * @param j column index + * @pre @p g must be a valid pointer toward a game structure. + * @pre @p i < game height + * @pre @p j < game width + * @return true if the square has an error flag + **/ +bool game_has_error(cgame g, uint i, uint j); + + +/** + * @brief Checks if a given move is legal. + * @details This function checks that it is possible to play a move at a given + * position in the grid. More precisely, a move is said to be legal: 1) if the + * coordinates (i,j) are inside the grid, 2) if @p s is either a blank, light + * bulb or mark square, and 3) if the current square at (i,j) is not a black + * square. + * @param g the game + * @param i row index + * @param j column index + * @param s the square value + * @pre @p g must be a valid pointer toward a game structure. + * @return false if the move is not legal. + **/ +bool game_check_move(cgame g, uint i, uint j, square s); + +/** + * @brief Plays a move in a given square. + * @details The grid flags are updated consequently after each move. + * @param g the game + * @param i row index + * @param j column index + * @param s the square value + * @pre @p g must be a valid pointer toward a game structure. + * @pre @p i < game height + * @pre @p j < game width + * @pre @p s must be either BLANK, LIGHTBULB or MARK. + * @pre The game square at position (i,j) must not be a black square. + **/ +void game_play_move(game g, uint i, uint j, square s); + +/** + * @brief Update all grid flags. + * @details This is a low-level function, which is not intended to be used + * directly by end-user during the game. + * @param g the game + * @pre @p g must be a valid pointer toward a game structure. + **/ +void game_update_flags(game g); + +/** + * @brief Checks if the game is won. + * @param g the game + * @details This function checks that all the game rules (decribed on @ref + * index) are satisfied, ie. that all the blank squares are ligthed, without any + * errors. + * @return true if the game ended successfully, false otherwise + * @pre @p g must be a valid pointer toward a game structure. + **/ +bool game_is_over(cgame g); + +/** + * @brief Restarts a game. + * @details All the game is reset to its initial state. In particular, all the + * squares except black walls are reset to blank, and all flags are cleared. + * @param g the game + * @pre @p g must be a valid pointer toward a game structure. + **/ +void game_restart(game g); + +#endif // __GAME_H__ diff --git a/game.o b/game.o new file mode 100644 index 0000000..413f9e0 Binary files /dev/null and b/game.o differ diff --git a/game_aux.c b/game_aux.c new file mode 100644 index 0000000..37d2375 --- /dev/null +++ b/game_aux.c @@ -0,0 +1,96 @@ +#ifndef __GAME_AUX_H__ +#define __GAME_AUX_H__ +#include +#include +#include "game.h" + +int charPrintCell(square sqState){ + if (sqState == S_BLANK){ + return ' '; + } + if ((sqState == S_LIGHTBULB) || (sqState == (S_LIGHTBULB|F_ERROR)) | (sqState == (S_LIGHTBULB|F_LIGHTED)) | (sqState == (S_LIGHTBULB|F_ERROR|F_LIGHTED))){ + return '*'; + } + if ((sqState == S_MARK) || (sqState == (S_MARK|F_ERROR)) | (sqState == (S_MARK|F_LIGHTED))){ + return '-'; + } + if (sqState == S_BLACK0 || sqState == (S_BLACK0|F_ERROR)){ + return '0'; + } + if (sqState == S_BLACK1 || sqState == (S_BLACK1|F_ERROR)){ + return '1'; + } + if (sqState == S_BLACK2 || sqState == (S_BLACK2|F_ERROR)){ + return '2'; + } + if (sqState == S_BLACK3 || sqState == (S_BLACK3|F_ERROR)){ + return '3'; + } + if (sqState == S_BLACK4 || sqState == (S_BLACK4|F_ERROR)){ + return '4'; + } + if (sqState == S_BLACKU || sqState == (S_BLACKU|F_ERROR)){ + return 'w'; + } + if (sqState == F_LIGHTED){ + return '.'; + } + return -1; +} + +void game_print(cgame g){ + if (g != NULL){ + uint i; + printf(" 0"); + for (i = 1; i < DEFAULT_SIZE; i++){ + printf("%u",i); + } + printf("\n -"); + for (i = 1; i < DEFAULT_SIZE; i++){ + printf("-"); + } + printf("\n"); + for (i = 0; i < DEFAULT_SIZE; i++){ + printf("%u |",i); + for (uint j = 0; j < DEFAULT_SIZE; j++){ + printf("%c",charPrintCell(game_get_square(g,i,j))); + } + printf("|\n"); + } + printf(" -"); + for (i = 1; i < DEFAULT_SIZE; i++){ + printf("-"); + } + printf("\n"); + } +} +game game_default(void){ + game g = game_new_empty(); + game_set_square(g,0,2,S_BLACK1); + game_set_square(g,1,2,S_BLACK2); + game_set_square(g,2,5,S_BLACKU); + game_set_square(g,2,6,S_BLACK2); + game_set_square(g,4,0,S_BLACK1); + game_set_square(g,4,1,S_BLACKU); + game_set_square(g,5,4,S_BLACK2); + game_set_square(g,6,4,S_BLACKU); + return g; +} + +game game_default_solution(void){ + game g = game_default(); + game_set_square(g,0,0,S_LIGHTBULB); + game_set_square(g,1,1,S_LIGHTBULB); + game_set_square(g,0,3,S_LIGHTBULB); + game_set_square(g,1,6,S_LIGHTBULB); + game_set_square(g,2,2,S_LIGHTBULB); + game_set_square(g,3,6,S_LIGHTBULB); + game_set_square(g,4,4,S_LIGHTBULB); + game_set_square(g,5,0,S_LIGHTBULB); + game_set_square(g,5,5,S_LIGHTBULB); + game_set_square(g,6,1,S_LIGHTBULB); + game_update_flags(g); + return g; +} + +#endif \ No newline at end of file diff --git a/game_aux.h b/game_aux.h new file mode 100644 index 0000000..294f997 --- /dev/null +++ b/game_aux.h @@ -0,0 +1,38 @@ +/** + * @file game_aux.h + * @brief Auxiliarry Game Functions. + * @details See @ref index for further details. + * @copyright University of Bordeaux. All rights reserved, 2021. + **/ + +#ifndef __GAME_AUX_H__ +#define __GAME_AUX_H__ +#include +#include "game.h" + +/** + * @brief Prints a game as text on the standard output stream. + * @details The different squares are respectively displayed as text, based on a + * square-character mapping table, as described in @ref index. + * @param g the game + * @pre @p g must be a valid pointer toward a game structure. + **/ +void game_print(cgame g); + +/** + * @brief Creates the default game. + * @details See the description of the default game in @ref index. + * @details https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/lightup.html#7x7:b1f2iB2g1Bi2fBb + * @return the created game + **/ +game game_default(void); + +/** + * @brief Creates the default game solution. + * @details See the description of the default game in @ref index. + * @details https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/lightup.html#7x7:b1f2iB2g1Bi2fBb + * @return the created game + **/ +game game_default_solution(void); + +#endif // __GAME_AUX_H__ diff --git a/game_text.c b/game_text.c new file mode 100644 index 0000000..f4c555a --- /dev/null +++ b/game_text.c @@ -0,0 +1,103 @@ +#include +#include +#include +#include "game.h" +#include "game_aux.h" + +bool checkerr(game g, char taberr[]){ + if (taberr == NULL || g == NULL){ + return -1; + } + uint err = 0; + taberr[0] = '\0'; + for (int i=0; i < DEFAULT_SIZE; i++){ + for (int j=0;j < DEFAULT_SIZE; j++){ + //check each case + if (game_has_error(g,i,j)){ + // if error append taberr with coo+, + taberr[err] = i+48; + err++; + taberr[err] = ':'; + err++; + taberr[err] = j+48; + err++; + taberr[err] = ' '; + err++; + } + } + } + // if taberr changes print + if (taberr[0] != '\0'){ + taberr[err] = '\0'; + return true; + } + return false; +} + +void printhelp(void){ + printf("Liste des différentes commandes :\n"); + printf("h : Affiche cette aide.\n"); + printf("r : Réinitialise la partie.\n"); + printf("q : Quitter la partie (abandon).\n"); + printf("l : Place une ampoule (*light bulb*) dans la case (,).\n"); + printf("m : Place une marque (*mark*) dans la case (,).\n"); + printf("b : Place placer une case vide (*blank*) à la position (,).\n"); +} + +int main(void) { + // Create new game + game g = game_default(); + // test if game is over + uint i; + uint j; + char taberr[DEFAULT_SIZE*DEFAULT_SIZE*4]; + char c; + // print game + game_print(g); + while (!game_is_over(g)){ + // print errors + if (checkerr(g,taberr)){ + printf("Il y a un problème case: %s\n",taberr); + } + printf("Entrez une commande (h pour afficher l'aide): "); + if (scanf(" %c",&c)){ + if (c == 'h'){ + printhelp(); + }else if (c == 'r'){ + game_restart(g); + printf("Partie réinitialisé avec success !\n"); + game_update_flags(g); + game_print(g); + }else if (c == 'q'){ + printf("shame\n"); + return EXIT_SUCCESS; + }else if (c == 'l' || c == 'm' || c == 'b'){ + if (scanf(" %u %u",&i,&j) == 2){ + if (i < DEFAULT_SIZE && j < DEFAULT_SIZE && !game_is_black(g,i,j)){ + if (c == 'l'){ + game_set_square(g,i,j,S_LIGHTBULB); + game_update_flags(g); + game_print(g); + }else if (c == 'm'){ + game_set_square(g,i,j,S_MARK); + game_update_flags(g); + game_print(g); + }else if (c == 'b') { + game_set_square(g,i,j,S_BLANK); + game_update_flags(g); + game_print(g); + } + }else { + printf("Erreur: (,) invalide.\n"); + } + }else { + printf("Commande inconnue.\n"); + } + }else{ + printf("Commande inconnue.\n"); + } + } + } + printf("congratulation\n"); + return EXIT_SUCCESS; +} \ No newline at end of file