diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1530978 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.o \ No newline at end of file diff --git a/README.md b/README.md index 091f7e7..ad899ed 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,19 @@ -# gameoflife -Une implémentation simpliste du jeu de la vie en langage C. +#GameOfLife +A simple implementation of the game of life in C language. -## Todo -- [x] Première implémentation dans la console -- [ ] Possibilité de changer le nombre de cellules -- [x] Possibilité de charger un fichier -- [x] Portage graphique avec SDL -- [x] Mise en pause de la simulation et possibilité de cliquer sur chaque cellule pour toggle l'état d'une cellule - - [x] Mise en pause avec la touche SPACE de la simulation - - [x] Pouvoir cliquer sur une cellule pour toggle l'état de la cell à la génération actuelle -- [ ] Parallélisation des calculs avec CUDA? +## Doables +- [x] First implementation in console +- [ ] Possibility to change number of cells +- [x] Possibility to load file +- [x] Graphical porting with SDL +- [x] Possibility to pause simulation and toggle cell state by clicking on each cell +- [x] Pausing simulation with SPACE key +- [x] Ability to click on cell to toggle cell state in current generation +- [ ] Parallelization of calculations with CUDA? -## Licence +## License BSD-3 +## Contributors +[CallMePixelMan](https://github.com/CallMePixelMan) +[पचाकुटेक गुदा बास](https://github.com/pacakutekagudabasa) diff --git a/src/Makefile b/src/Makefile index 8119d90..81038ce 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,14 +1,99 @@ -CC=gcc -CFLAGS=-Wall -Wextra -fsanitize=address,leak,undefined -CLIBS=-lSDL2 -lm +# Game of Life Enhanced - Makefile +# गेम ऑफ लाइफ एन्हांस्ड - बिल्ड सिस्टम +# +# यह Makefile enhanced Game of Life को compile और manage करने के लिए +# बनाई गई है। इसमें सभी dependencies, build targets, और utility commands +# शामिल हैं। -all: main +# Compiler और compiler flags +CC = gcc +CFLAGS = -Wall -Wextra -std=c99 -O2 # Warning flags और optimization +LIBS = -lSDL2 -lm # SDL2 और math libraries -%.o: %.c - $(CC) $(CFLAGS) -c $^ +# Source files और object files +SRCS = main.c board.c state.c rules.c # सभी source files +OBJS = $(SRCS:.c=.o) # Corresponding object files +TARGET = gameoflife # Final executable का नाम + +# Default target - सबसे पहले यह run होता है +all: $(TARGET) -main: board.o state.o main.c - $(CC) $(CFLAGS) -o $@ $^ $(CLIBS) +# Main executable build करने के लिए target +# सभी object files को link करके final executable बनाता है +$(TARGET): $(OBJS) + $(CC) $(OBJS) -o $(TARGET) $(LIBS) +# Object files build करने के लिए generic rule +# हर .c file को corresponding .o file में compile करता है +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +# Build files को clean करने के लिए target +# सभी generated files (object files और executable) को delete करता है clean: - rm *.o main + rm -f $(OBJS) $(TARGET) + +# SDL2 dependencies install करने के लिए target (Ubuntu/Debian) +# Development libraries install करता है जो compilation के लिए जरूरी हैं +install-deps: + sudo apt-get update + sudo apt-get install libsdl2-dev + +# Game को build करके run करने के लिए target +# पहले build करता है फिर execute करता है +run: $(TARGET) + ./$(TARGET) + +# Sample pattern file के साथ game run करने के लिए target +# पहले sample file create करता है फिर उसके साथ game run करता है +run-sample: $(TARGET) + ./$(TARGET) sample.txt + +# Sample pattern file create करने के लिए target +# एक glider pattern के साथ example file बनाता है +sample: + @echo "Creating sample.txt with a glider pattern..." + @echo "Sample glider pattern बनाया जा रहा है..." + @echo "0000000000000000" > sample.txt + @echo "0000000000000000" >> sample.txt + @echo "0000000000000000" >> sample.txt + @echo "0000000000000000" >> sample.txt + @echo "0000100000000000" >> sample.txt + @echo "0000010000000000" >> sample.txt + @echo "0001110000000000" >> sample.txt + @echo "0000000000000000" >> sample.txt + @echo "0000000000000000" >> sample.txt + @echo "0000000000000000" >> sample.txt + @echo "Sample glider pattern created in sample.txt" + @echo "Sample glider pattern sample.txt में बनाया गया" + +# Help information display करने के लिए target +# सभी available targets और controls की जानकारी provide करता है +help: + @echo "Game of Life Enhanced - Build System" + @echo "गेम ऑफ लाइफ एन्हांस्ड - बिल्ड सिस्टम" + @echo "" + @echo "Targets / टारगेट्स:" + @echo " all - Build the game (default) / गेम build करें" + @echo " clean - Remove build files / build files हटाएं" + @echo " install-deps - Install SDL2 development libraries / SDL2 dev libraries install करें" + @echo " run - Build and run the game / गेम build करके run करें" + @echo " run-sample - Build and run with sample pattern / sample pattern के साथ run करें" + @echo " sample - Create a sample pattern file / sample pattern file बनाएं" + @echo " help - Show this help / यह help दिखाएं" + @echo "" + @echo "Controls in game / गेम में controls:" + @echo " SPACE - Pause/Resume / पॉज़/रिज्यूम" + @echo " R - Reload from file / फाइल से reload करें" + @echo " C - Clear board / बोर्ड clear करें" + @echo " G - Generate random / रैंडम generate करें" + @echo " T - Switch rule set / rule set बदलें" + @echo " H - Help in game / गेम में help" + @echo " ESC/Q - Quit / बाहर निकलें" + @echo " Mouse Click - Toggle cell (when paused) / cell toggle करें (pause में)" + @echo " Mouse Drag - Paint alive cells (when paused) / जीवित cells paint करें" + @echo " Ctrl+Drag - Paint dead cells (when paused) / मृत cells paint करें" + +# Phony targets - ये actual files नहीं हैं बल्कि commands हैं +# Make को बताता है कि ये targets file names नहीं हैं +.PHONY: all clean install-deps run run-sample sample help diff --git a/src/board.c b/src/board.c index 7cc6545..19d756e 100644 --- a/src/board.c +++ b/src/board.c @@ -1,3 +1,13 @@ +/** + * @file board.c + * @brief गेम ऑफ लाइफ के लिए बोर्ड मैनेजमेंट implementation + * @author Game of Life Enhanced + * @date 2025 + * + * यह फाइल बोर्ड के सभी operations को implement करती है। + * Performance optimization के लिए 1D array का उपयोग किया गया है। + */ + #include #include @@ -9,110 +19,227 @@ #define MIN(x, y) ((x) < (y) ? x : y) #define MAX(x, y) ((x) > (y) ? x : y) -// Initialise la board. -struct Board *board_init(size_t height, size_t width) { - struct Board *board = malloc(sizeof(struct Board)); - - // Ajout des valeurs. +/** + * @brief नया बोर्ड initialize करता है और memory allocate करता है + * + * यह function एक नया Board struct create करता है और सभी cells को + * zero (मृत) state में initialize करता है। + * + * @param height बोर्ड की ऊंचाई (rows की संख्या) + * @param width बोर्ड की चौड़ाई (columns की संख्या) + * @return सफल होने पर Board pointer, memory allocation fail होने पर NULL + */ +Board *board_init(size_t height, size_t width) { + Board *board = malloc(sizeof(Board)); + if (!board) { + return NULL; + } + // बोर्ड dimensions set करें board->height = height; board->width = width; - // Init de toutes les cellules. - board->cells = calloc(sizeof(int **), height); - for (size_t i = 0; i < height; i++) { - board->cells[i] = calloc(sizeof(int *), width); - } + // सभी cells को zero (मृत) state में initialize करें + board->cells = calloc(sizeof(void *), height * width); return board; } -// Affiche la grille du jeu de la vie dans le terminal. -void board_print(struct Board *board) { - for (size_t x = 0; x < board->height; x++) { - for (size_t y = 0; y < board->width; y++) { - char *c = board->cells[x][y] ? "██" : " "; - printf("%s", c); +/** + * @brief बोर्ड को terminal में visual format में print करता है + * + * यह function बोर्ड को terminal में Unicode blocks का उपयोग करके + * print करता है। जीवित cells ██ और मृत cells " " के रूप में दिखाई जाती हैं। + * + * @param board print करने वाला बोर्ड + * @return सफल होने पर 0, memory allocation fail होने पर -1 + */ +int board_print(Board *board) { + // एक line के लिए buffer allocate करें (प्रति cell 2 chars + null terminator) + char *line = malloc(board->width * 2 + 1); + + if (line == NULL) { + return -1; + } + + for (size_t i = 0; i < board->width * board->height; i++) { + // 1D index को 2D coordinates में convert करें + size_t x = i / board->width; + size_t y = i % board->width; + COPY_CELL(&line[y * 2], board->cells[x * board->width + y]); + // जब row का अंत आ जाए तो line print करें + if (y == board->width - 1) { + line[board->width * 2] = '\0'; // Null terminate + printf("%s\n", line); } - printf("\n"); } + + free(line); + return 0; } -// Libère toute la mémoire présente pour la board. -void board_free(struct Board *board) { - // Libération de la mémoire pour le tableau. - for (size_t x = 0; x < board->height; x++) { - free(board->cells[x]); - } +/** + * @brief बोर्ड की सारी allocated memory को free करता है + * + * यह function बोर्ड के cells array और बोर्ड struct दोनों की + * memory को safely free करता है। + * + * @param board free करने वाला बोर्ड + * @return सफल होने पर 0, NULL pointer होने पर -1 + */ +int board_free(Board *board) { + if (board == NULL) return -1; + + // cells array की memory free करें free(board->cells); - - // Libération de la mémoire de la struct. + + // बोर्ड struct की memory free करें free(board); + + return 0; } -// Renvoie si une cellule est vivante à la prochaine génération. -int is_cell_alive_next_gen(struct Board *board, size_t x, size_t y) { - // On compte toutes les cellules en vie autour de la cellule (x, y) +/** + * @brief specified rules के अनुसार cell अगली generation में जीवित रहेगी या नहीं check करता है + * + * यह function दिए गए cell के आसपास के neighbors count करता है और + * current rules के अनुसार determine करता है कि cell next generation में + * जीवित रहेगी या मरेगी। + * + * @param board current बोर्ड state + * @param rules apply करने वाले game rules + * @param x cell का x coordinate (row) + * @param y cell का y coordinate (column) + * @param result परिणाम store करने के लिए pointer (1=जीवित रहेगी, 0=मरेगी) + * @return सफल होने पर 0, invalid parameters होने पर -1 + */ +int is_cell_alive_next_gen(struct Board *board, Rules *rules, size_t x, size_t y, int *result) { + if (board == NULL || rules == NULL || result == NULL || x >= board->height || y >= board->width) return -1; + + // cell के आसपास के जीवित neighbors count करें int count = 0; for (size_t i = MAX((int)x-1, 0); i <= MIN(x+1, board->height - 1); i++) { for (size_t j = MAX((int)y-1, 0); j <= MIN(y+1, board->width - 1); j++) { - // Ne compte pas la cell actuelle. + // current cell को count न करें if (i == x && j == y) continue; - // Incremente le conteur en fonction de si la cellule est actuellement vivante ou pas. - count += board->cells[i][j]; - - // Si on depasse les 4, on sait directement que la cellule est morte. - if (count > 3) return 0; // true + // neighbor जीवित है तो count increment करें + // 2D coordinates को 1D index में convert करें + size_t index = i * board->width + j; + count += board->cells[index]; } } - // Une cellule meurt si elle a moins de 3 ou plus de 4 voisins. - int cell_is_dead = board->cells[x][y] == 0; - if (cell_is_dead) { - return count == 3; - } else { - return count == 2 || count == 3; - } + // rules apply करके determine करें कि cell जीवित रहेगी या मरेगी + size_t current_index = x * board->width + y; + return rules_apply(rules, board->cells[current_index], count, result); } -// Produit la future itération de le board dans la board out. -// -// Permet d'allouer que 2 boards et de swap les pointeurs pour calculer chaque nouvelle itération de manière -// propre en mémoire. -void board_next(struct Board *board, struct Board *out) { - for (size_t x = 0; x < board->height; x++) { - for (size_t y = 0; y < board->width; y++) { - out->cells[x][y] = is_cell_alive_next_gen(board, x, y); - } +/** + * @brief specified rules का उपयोग करके अगली generation का बोर्ड generate करता है + * + * यह function current बोर्ड state को input के रूप में लेता है और + * दिए गए rules के अनुसार next generation को output बोर्ड में calculate करता है। + * Double buffering technique का उपयोग किया गया है। + * + * @param board current generation का बोर्ड + * @param out next generation store करने के लिए output बोर्ड + * @param rules apply करने वाले game rules + * @return सफल होने पर 0, error होने पर -1 + */ +int board_next(Board *board, Board *out, Rules *rules) { + if (board == NULL || out == NULL || rules == NULL) return -1; + if (board->width != out->width || board->height != out->height) return -1; + + // 1D array में single loop - better performance + for (size_t i = 0; i < board->width * board->height; i++) { + // 1D index को 2D coordinates में convert करें neighbor checking के लिए + size_t x = i / board->width; + size_t y = i % board->width; + + int cell_result; + int status = is_cell_alive_next_gen(board, rules, x, y, &cell_result); + if (status == -1) return -1; // Error from is_cell_alive_next_gen + + out->cells[i] = cell_result; } + + return 0; } -// Renvoie un nombre aléatoire entre min et max. +/** + * @brief min और max के बीच random number generate करता है + * @param min minimum value (inclusive) + * @param max maximum value (inclusive) + * @return min और max के बीच का random integer + */ static int randint(int min, int max) { - return random() % (max + 1 - min) + min; + return rand() % (max + 1 - min) + min; } -// Initialise la grille de jeu avec des valeurd aléatoires. -void board_random_fill(struct Board *board) { - for (size_t x = 0; x < board->height; x++) { - for (size_t y = 0; y < board->width; y++) { - board->cells[x][y] = randint(0, 4) == 0; - } +/** + * @brief बोर्ड को random values से fill करता है + * + * यह function बोर्ड के सभी cells को randomly जीवित या मृत state में + * set करता है। जीवित होने की probability approximately 20% है। + * + * @param board fill करने वाला बोर्ड + * @return सफल होने पर 0, NULL pointer होने पर -1 + */ +int board_random_fill(Board *board) { + if (board == NULL) return -1; + + // 1D array में single loop + for (size_t i = 0; i < board->width * board->height; i++) { + board->cells[i] = randint(0, 4) == 0; // 20% chance of being alive + } + + return 0; +} + +/** + * @brief बोर्ड के सभी cells को clear करता है (सभी को मृत बनाता है) + * + * यह function बोर्ड को reset करने के लिए उपयोग किया जाता है। + * सभी cells को 0 (मृत) state में set कर देता है। + * + * @param board clear करने वाला बोर्ड + * @return सफल होने पर 0, NULL pointer होने पर -1 + */ +int board_clear(Board *board) { + if (board == NULL) return -1; + + for (size_t i = 0; i < board->width * board->height; i++) { + board->cells[i] = 0; } + + return 0; } -// Charge le contenu dans une board. -// -// Précondition: Le fichier doit avoir les mêmes dimensions de la board. Sinon UB. -int board_from_file(char *filename, struct Board *board) { - FILE *file; +/** + * @brief text file से बोर्ड pattern load करता है + * + * यह function text file से pattern read करके बोर्ड में load करता है। + * File format: '0' = मृत cell, कोई भी अन्य character = जीवित cell। + * प्रत्येक line एक row represent करती है। + * + * @param filename load करने वाली file का नाम + * @param board target बोर्ड जहाँ pattern load करना है + * @return सफल होने पर 0, file error या bounds error होने पर -1 + */ +int board_from_file(char *filename, Board *board) { + if (filename == NULL || board == NULL) return -1; + + FILE *file = NULL; file = fopen(filename, "r"); - // Si on ne peut pas ouvrir le fichier. - if (file == NULL) return 1; + // अगर file open नहीं हो सकी + if (file == NULL) return -1; - // Lecture de chaque char. - char c; + // पहले बोर्ड को clear करें + board_clear(board); + + // प्रत्येक character read करें + char c = 0; size_t x = 0, y = 0; while ((c = fgetc(file)) != EOF) { @@ -122,13 +249,20 @@ int board_from_file(char *filename, struct Board *board) { continue; } - // Tous les caractères sont considérés comme true sauf le 0. - board->cells[x][y] = c != '0'; + // bounds check करें + if (x >= board->height || y >= board->width) { + fclose(file); + return -1; + } + + // 2D coordinates को 1D index में convert करें + size_t index = x * board->width + y; + // '0' को छोड़कर सभी characters को जीवित cell माना जाता है + board->cells[index] = c != '0'; y++; } - // Fermeture du fichier et renvoie de la valeur. + // file close करें और success return करें fclose(file); return 0; } - diff --git a/src/board.h b/src/board.h index 3d54214..8f883b4 100644 --- a/src/board.h +++ b/src/board.h @@ -1,12 +1,101 @@ -struct Board { - int **cells; - size_t height; - size_t width; -}; - -struct Board *board_init(size_t height, size_t width); -void board_print(struct Board *board); -void board_free(struct Board *board); -void board_next(struct Board *board, struct Board *out); -void board_random_fill(struct Board *board); -int board_from_file(char *filename, struct Board *board); +/** + * @file board.h + * @brief गेम ऑफ लाइफ के लिए बोर्ड मैनेजमेंट हेडर फाइल + * @author Game of Life Enhanced + * @date 2025 + * + * यह फाइल बोर्ड स्ट्रक्चर और इसके सभी ऑपरेशन्स को डिफाइन करती है। + * बोर्ड एक 1D array के रूप में implemented है बेहतर performance के लिए। + */ + +#ifndef BOARD_H +#define BOARD_H + +#include // For memcpy in COPY_CELL macro +#include "rules.h" // Include rules system + +/** + * @brief गेम बोर्ड स्ट्रक्चर जो सभी cells को store करता है + * + * यह स्ट्रक्चर गेम के grid को represent करता है। Memory efficiency के लिए + * 1D array का उपयोग किया गया है instead of 2D array। + */ +typedef struct Board { + char *cells; /**< सेल्स का 1D array (0=मृत, 1=जीवित) */ + size_t height; /**< बोर्ड की ऊंचाई */ + size_t width; /**< बोर्ड की चौड़ाई */ +} Board; + +/** + * @brief सेल को terminal में print करने के लिए macro + * @param dest गंतव्य buffer + * @param is_filled सेल जीवित है या नहीं (1=जीवित, 0=मृत) + */ +#define COPY_CELL(dest, is_filled) \ + memcpy(dest, (is_filled) ? "██" : " ", (is_filled) ? 6 : 2) + +/** + * @brief नया बोर्ड initialize करता है + * @param height बोर्ड की ऊंचाई + * @param width बोर्ड की चौड़ाई + * @return सफल होने पर Board pointer, असफल होने पर NULL + */ +Board *board_init(size_t height, size_t width); + +/** + * @brief बोर्ड को terminal में print करता है + * @param board प्रिंट करने वाला बोर्ड + * @return सफल होने पर 0, असफल होने पर -1 + */ +int board_print(Board *board); + +/** + * @brief बोर्ड की memory को free करता है + * @param board free करने वाला बोर्ड + * @return सफल होने पर 0, असफल होने पर -1 + */ +int board_free(Board *board); + +/** + * @brief rules के अनुसार सेल अगली generation में जीवित रहेगी या नहीं check करता है + * @param board current बोर्ड + * @param rules apply करने वाले rules + * @param x सेल का x coordinate + * @param y सेल का y coordinate + * @param result परिणाम store करने के लिए pointer (1=जीवित, 0=मृत) + * @return सफल होने पर 0, असफल होने पर -1 + */ +int is_cell_alive_next_gen(struct Board *board, Rules *rules, size_t x, size_t y, int *result); + +/** + * @brief specified rules का उपयोग करके अगली generation generate करता है + * @param board current बोर्ड + * @param out output बोर्ड जहाँ next generation store होगी + * @param rules apply करने वाले rules + * @return सफल होने पर 0, असफल होने पर -1 + */ +int board_next(Board *board, Board *out, Rules *rules); + +/** + * @brief बोर्ड को random values से fill करता है + * @param board fill करने वाला बोर्ड + * @return सफल होने पर 0, असफल होने पर -1 + */ +int board_random_fill(Board *board); + +/** + * @brief file से बोर्ड load करता है + * @param filename load करने वाली file का नाम + * @param board target बोर्ड + * @return सफल होने पर 0, असफल होने पर -1 + */ +int board_from_file(char *filename, Board *board); + +/** + * @brief बोर्ड के सभी cells को clear करता है (सभी को मृत बनाता है) + * @param board clear करने वाला बोर्ड + * @return सफल होने पर 0, असफल होने पर -1 + */ +int board_clear(Board *board); + +#endif // BOARD_H diff --git a/src/main.c b/src/main.c index a0635cd..bb8cd50 100644 --- a/src/main.c +++ b/src/main.c @@ -1,3 +1,14 @@ +/** + * @file main.c + * @brief Enhanced Game of Life का main implementation + * @author Game of Life Enhanced + * @date 2025 + * + * यह फाइल Conway's Game of Life का enhanced version implement करती है। + * इसमें configurable rules, interactive controls, file I/O, और drag-to-paint + * functionality शामिल है। SDL2 का उपयोग करके graphics rendering की गई है। + */ + #include #include @@ -6,47 +17,135 @@ #include "board.h" #include "state.h" +#include "rules.h" +/** + * @brief प्रत्येक cell का size pixels में + */ #define PIXEL_SIZE 10 + +/** + * @brief दो variables को swap करने के लिए macro + * @param type variables का data type + * @param x पहला variable + * @param y दूसरा variable + */ #define SWAP(type, x, y) \ type temp = x; \ x = y; \ y = temp; -// Fixe la couleur du renderer SDL a noir ou blanc en fonction de la valeur de la cell. -// -// Toutes les valeurs sont expectées d'être == 1 ou == 0. -void set_pixel_color(SDL_Renderer *renderer, int cell) { - SDL_SetRenderDrawColor(renderer, 255 * cell, 255 * cell, 255 * cell, 255); +/** + * @brief Available rule sets की कुल संख्या + */ +#define NUM_RULE_SETS 4 + +/** + * @brief Predefined rule creation functions का global array + * + * यह array सभी available rule sets के creation functions को store करता है। + * Runtime पर rule switching के लिए उपयोग किया जाता है। + */ +Rules* (*rule_creators[NUM_RULE_SETS])(void) = { + rules_conway, /**< Conway's Game of Life */ + rules_highlife, /**< HighLife variant */ + rules_day_night, /**< Day & Night rules */ + rules_maze /**< Maze generation rules */ +}; + +/** + * @brief SDL renderer का color cell की value के अनुसार set करता है + * + * यह function cell की state (जीवित/मृत) के अनुसार renderer का color + * black (मृत) या white (जीवित) set करता है। + * + * @param renderer SDL renderer pointer + * @param cell cell की value (0=मृत, 1=जीवित) + * @return सफल होने पर 0, error होने पर -1 + */ +int set_pixel_color(SDL_Renderer *renderer, int cell) { + if (renderer == NULL) return -1; + if (cell != 0 && cell != 1) { + return -1; + } + return (SDL_SetRenderDrawColor(renderer, 255 * cell, 255 * cell, 255 * cell, 255) == 0) ? 0 : -1; } -void board_pixel_draw(SDL_Renderer *renderer, struct Board *board, int x, int y) { - set_pixel_color(renderer, board->cells[x][y]); +/** + * @brief एक specific cell को screen पर draw करता है + * + * यह function board के specific coordinates पर स्थित cell को + * SDL renderer का उपयोग करके screen पर rectangle के रूप में draw करता है। + * + * @param renderer SDL renderer pointer + * @param board source board + * @param x cell का x coordinate (row) + * @param y cell का y coordinate (column) + * @return सफल होने पर 0, error होने पर -1 + * + * @note SDL coordinates board coordinates से inverted हैं + */ +int board_pixel_draw(SDL_Renderer *renderer, Board *board, int x, int y) { + if (renderer == NULL || board == NULL) return -1; + if (x < 0 || y < 0 || x >= (int)board->height || y >= (int)board->width) return -1; + + // 2D coordinates को 1D index में convert करें + size_t index = x * board->width + y; + + if (set_pixel_color(renderer, board->cells[index]) != 0) return -1; - // On demande un pixel de plus pour faire un petit offset vers le coin inférieur droit - // pour faire une petite marge. - // - // NOTE: Les coordonnées X et Y sont inversés car SDL n'utilise pas la même convention - // pour les axes que celle que j'ai choisie pour indexer dans la matrice de ma board. + // Border के लिए 1 pixel offset add करें + // NOTE: SDL coordinates board coordinates से inverted हैं struct SDL_Rect pixel = { (y + 1) * PIXEL_SIZE, (x + 1) * PIXEL_SIZE, PIXEL_SIZE, PIXEL_SIZE }; - SDL_RenderFillRect(renderer, &pixel); + + return (SDL_RenderFillRect(renderer, &pixel) == 0) ? 0 : -1; } -void board_draw(SDL_Renderer *renderer, struct Board *board) { - for (size_t x = 0; x < board->height; x++) { - for (size_t y = 0; y < board->width; y++) { - board_pixel_draw(renderer, board, x, y); - } +/** + * @brief पूरे board को screen पर draw करता है + * + * यह function board के सभी cells को iterate करके प्रत्येक को + * screen पर draw करता है। यह main rendering function है। + * + * @param renderer SDL renderer pointer + * @param board draw करने वाला board + * @return सफल होने पर 0, error होने पर -1 + */ +int board_draw(SDL_Renderer *renderer, Board *board) { + if (renderer == NULL || board == NULL) return -1; + + // 1D array में single loop - better performance + for (size_t i = 0; i < board->width * board->height; i++) { + // 1D index को 2D coordinates में convert करें drawing के लिए + size_t x = i / board->width; + size_t y = i % board->width; + + if (board_pixel_draw(renderer, board, x, y) != 0) return -1; } + + return 0; } +/** + * @brief SDL window create करता है + * + * यह function SDL को initialize करके specified dimensions के साथ + * एक नई window create करता है। + * + * @param title window का title + * @param width window की चौड़ाई pixels में + * @param height window की ऊंचाई pixels में + * @return सफल होने पर SDL_Window pointer, error होने पर NULL + */ SDL_Window *create_window(char *title, unsigned int width, unsigned int height) { + if (title == NULL) return NULL; + if (SDL_Init(SDL_INIT_VIDEO) < 0) { return NULL; @@ -62,9 +161,19 @@ SDL_Window *create_window(char *title, unsigned int width, unsigned int height) ); } - +/** + * @brief SDL renderer create करता है + * + * यह function दी गई window के लिए SDL renderer create करता है + * जो graphics operations के लिए उपयोग किया जाता है। + * + * @param window target SDL window + * @return सफल होने पर SDL_Renderer pointer, error होने पर NULL + */ SDL_Renderer *create_renderer(SDL_Window *window) { + if (window == NULL) return NULL; + const int FIRST_RENDERING_DRIVER = -1; return SDL_CreateRenderer( @@ -74,109 +183,341 @@ SDL_Renderer *create_renderer(SDL_Window *window) ); } -// Fonction récupérant les event SDL. Utilisé pour savoir s'il faut fermer le programme. -void process_events(struct State *state, struct Board *board) { - SDL_Event e; - SDL_PollEvent(&e); +/** + * @brief File से board load करता है और errors को handle करता है + * + * यह function specified file से pattern load करके board में set करता है। + * User को appropriate feedback भी provide करता है। + * + * @param filename load करने वाली file का नाम + * @param board target board + * @return सफल होने पर 0, error होने पर -1 + */ +int load_board_from_file(const char *filename, Board *board) { + if (!filename || !board) return -1; - switch (e.type) { - case SDL_QUIT: - state->keep_alive = 0; - break; + printf("Loading board from file: %s\n", filename); + if (board_from_file((char*)filename, board) != 0) { + printf("Error loading file: %s\n", filename); + return -1; + } + printf("Board loaded successfully from: %s\n", filename); + return 0; +} - case SDL_KEYUP: - if (e.key.keysym.sym == SDLK_SPACE) { - state->pause = !state->pause; - } - break; +/** + * @brief Mouse coordinates को board coordinates में convert करके cell paint करता है + * + * यह function mouse के current position को board coordinates में convert करता है + * और state के अनुसार cell को paint करता है (जीवित या मृत)। + * + * @param mouse_x mouse का x coordinate + * @param mouse_y mouse का y coordinate + * @param board target board + * @param state current game state (paint mode determine करने के लिए) + * @return सफल होने पर 0, out of bounds या error होने पर -1 + */ +int paint_cell_at_mouse(int mouse_x, int mouse_y, Board *board, State *state) { + if (!board || !state) return -1; + + // Mouse coordinates को board coordinates में convert करें + // 1-pixel border offset को account करें + int board_y = mouse_x / PIXEL_SIZE - 1; + int board_x = mouse_y / PIXEL_SIZE - 1; + + // Bounds check करें + if (board_x < 0 || board_y < 0 || board_x >= (int)board->height || board_y >= (int)board->width) { + return -1; + } + + // 2D coordinates को 1D index में convert करें + size_t index = board_x * board->width + board_y; + + // Paint mode के अनुसार cell set करें + board->cells[index] = state->drag_paint_mode; + + return 0; +} - case SDL_MOUSEBUTTONDOWN: - if (state->pause && e.button.button == SDL_BUTTON_LEFT) { - // Encore une fois, les axes sont inversés entre SDL et la convention - // que j'utilise pour la grille de cellules. - // - // Aussi, comme j'ai ajouté une marge de 1 pixel autour de ma grille, - // on doit calculer la position du pixel en déduisant l'offset. - int y = e.button.x / PIXEL_SIZE - 1; - int x = e.button.y / PIXEL_SIZE - 1; - - // On ignore les clics sur la bordure. - if (x < 0 || y < 0 || x >= (int)board->height || y >= (int)board->width) return; - - board->cells[x][y] = !board->cells[x][y]; - } - break; +/** + * @brief Console में help text print करता है + * + * यह function सभी available controls और current rules की जानकारी + * user को provide करता है। + * + * @param rules current active rules + */ +void print_help(Rules *rules) { + printf("\n=== Game of Life Controls ===\n"); + printf("SPACE - Pause/Unpause simulation\n"); + printf("R - Reload from file (if loaded from file)\n"); + printf("C - Clear board\n"); + printf("G - Generate random board\n"); + printf("T - Switch rule set\n"); + printf("H - Show this help\n"); + printf("ESC/Q - Quit game\n"); + printf("Mouse Click - Toggle cell (when paused)\n"); + printf("Mouse Drag - Paint alive cells (when paused)\n"); + printf("Ctrl+Drag - Paint dead cells (when paused)\n"); + printf("\nCurrent Rules: "); + rules_print(rules); + printf("=============================\n"); +} + +/** + * @brief SDL events को process करता है और appropriate actions perform करता है + * + * यह function सभी SDL events (keyboard, mouse) को handle करता है और + * game state को accordingly update करता है। यह main input handling function है। + * + * @param state current game state + * @param board current board + * @param current_rules current active rules (switching के लिए modifiable) + * @return सफल होने पर 0, error होने पर -1 + */ +int process_events(State *state, Board *board, Rules **current_rules) { + if (state == NULL || board == NULL || current_rules == NULL) return -1; + + SDL_Event e; + while (SDL_PollEvent(&e)) { + switch (e.type) { + case SDL_QUIT: + state->keep_alive = 0; + break; + + case SDL_KEYDOWN: + switch (e.key.keysym.sym) { + case SDLK_SPACE: + // Simulation को pause/unpause करें + state->pause = !state->pause; + printf("Game %s\n", state->pause ? "PAUSED" : "RESUMED"); + break; + + case SDLK_r: + // File से reload करें अगर available है + if (state->loaded_filename) { + if (load_board_from_file(state->loaded_filename, board) == 0) { + state->pause = 1; // Reload के बाद pause करें + } + } else { + printf("No file to reload from. Load a file first.\n"); + } + break; + + case SDLK_c: + // Board को clear करें + board_clear(board); + printf("Board cleared\n"); + state->pause = 1; + break; + + case SDLK_g: + // Random board generate करें + board_random_fill(board); + printf("Random board generated\n"); + state->pause = 1; + break; + + case SDLK_t: + // Rule set को switch करें + rules_free(*current_rules); + state->current_rule_index = (state->current_rule_index + 1) % NUM_RULE_SETS; + *current_rules = rule_creators[state->current_rule_index](); + printf("Switched to rule set: "); + rules_print(*current_rules); + break; + + case SDLK_h: + // Help show करें + print_help(*current_rules); + break; + + case SDLK_ESCAPE: + case SDLK_q: + // Game quit करें + state->keep_alive = 0; + break; + } + break; + + case SDL_MOUSEBUTTONDOWN: + if (state->pause && e.button.button == SDL_BUTTON_LEFT) { + // Ctrl key check करके paint mode determine करें + SDL_Keymod keymod = SDL_GetModState(); + state->drag_paint_mode = !(keymod & KMOD_CTRL); // Ctrl = मृत paint, no Ctrl = जीवित paint + + state->is_dragging = true; + + // Initial cell paint करें + paint_cell_at_mouse(e.button.x, e.button.y, board, state); + } + break; + + case SDL_MOUSEBUTTONUP: + if (e.button.button == SDL_BUTTON_LEFT) { + state->is_dragging = false; + } + break; + + case SDL_MOUSEMOTION: + if (state->pause && state->is_dragging) { + // Dragging के दौरान painting continue करें + paint_cell_at_mouse(e.motion.x, e.motion.y, board, state); + } + break; + } } + + return 0; } +/** + * @brief Main function - program का entry point + * + * यह function game को initialize करता है, main loop run करता है, + * और cleanup operations perform करता है। Command line arguments को + * handle करके file loading भी support करता है। + * + * @param argc command line arguments की संख्या + * @param argv command line arguments का array + * @return program exit status (0=success, non-zero=error) + */ int main(int argc, char **argv) { int error_code = 0; - // Set des dimentions. + // Board dimensions set करें const size_t height = 64; const size_t width = height; - // Défintition de la taille de la fenêtre SDL. - // - // On demande 2 pixels de largeur de plus pour faire des jolis petits bords. :) + // SDL window का size define करें + // Border के लिए 2 extra pixels add करें const int window_width = PIXEL_SIZE * (height + 2); const int window_height = PIXEL_SIZE * (width + 2); - // Création des boards. - struct Board *front = board_init(height, width); - struct Board *back = board_init(height, width); + // Rules initialize करें (default Conway's Life) + Rules *current_rules = rules_conway(); + if (!current_rules) { + printf("Error initializing rules\n"); + return 1; + } - // Chargement d'un fichier en fonction des arguments. + // Boards create करें (double buffering के लिए) + Board *front = board_init(height, width); + Board *back = board_init(height, width); + + if (front == NULL || back == NULL) { + printf("Erreur lors de l'allocation des boards\n"); + error_code = 1; + goto cleanup; + } + + // Game state create करें + struct State *state = NULL; + if (state_init(&state) != 0) + { + printf("Erreur lors de l'initialisation du state\n"); + error_code = 1; + goto cleanup; + } + + // Command line arguments के अनुसार file load करें या random generate करें if (argc > 1) { char *filename = argv[1]; - printf("Chargement de la grille contenue dans le fichier '%s'\n", filename); - - if (board_from_file(filename, front)) - { - printf("Erreur lors de la lecture du fichier. Arrêt du programme.\n"); - error_code = 1; - goto cleanup; + + if (load_board_from_file(filename, front) != 0) { + printf("Erreur lors de la lecture du fichier. Générant une grille aléatoire à la place.\n"); + srand(time(NULL)); + board_random_fill(front); + } else { + // Reloading के लिए filename store करें + state_set_filename(state, filename); } } else { printf("Chargement d'une grille aléatoire\n"); - srandom(time(NULL)); - board_random_fill(front); + srand(time(NULL)); + if (board_random_fill(front) != 0) { + printf("Erreur lors du remplissage aléatoire. Arrêt du programme.\n"); + error_code = 1; + goto cleanup; + } } - // Init de SDL. - SDL_Window *window = create_window("Game Of Life", window_height, window_width); + // SDL initialize करें + SDL_Window *window = create_window("Game Of Life - Enhanced", window_height, window_width); + if (window == NULL) { + printf("Erreur lors de la création de la fenêtre SDL: %s\n", SDL_GetError()); + error_code = 1; + goto cleanup; + } + SDL_Renderer *renderer = create_renderer(window); + if (renderer == NULL) { + printf("Erreur lors de la création du renderer SDL: %s\n", SDL_GetError()); + error_code = 1; + goto cleanup_window; + } - // Creation du state. - struct State *state = state_init(); + // Initial help print करें + print_help(current_rules); + + // Paused state में start करें + state->pause = 1; + printf("Starting paused. Press SPACE to begin simulation.\n"); - // Boucle principale. + // Main game loop while (state->keep_alive) { - process_events(state, front); + if (process_events(state, front, ¤t_rules) != 0) { + printf("Erreur lors du traitement des événements\n"); + error_code = 1; + break; + } - board_draw(renderer, front); + // Screen को black color से clear करें + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); + + if (board_draw(renderer, front) != 0) { + printf("Erreur lors du dessin de la board\n"); + error_code = 1; + break; + } + SDL_RenderPresent(renderer); - // On ne calcule pas la prochaine frame si le jeu est en pause. - if (state->pause) continue; + // अगर pause है तो next generation calculate न करें + if (state->pause) { + SDL_Delay(50); // Paused state में CPU usage reduce करें + continue; + } + + // Current rules के साथ next generation calculate करें + if (board_next(front, back, current_rules) != 0) { + printf("Erreur lors du calcul de la prochaine génération\n"); + error_code = 1; + break; + } - // Calcul de la prochaine intération dans l'autre buffer. - board_next(front, back); - SWAP(struct Board *, front, back); + SWAP(Board *, front, back); - // Attente avant d'afficher la prochain génération. + // Next generation display करने से पहले wait करें SDL_Delay(50); } + printf("Game ended. Goodbye!\n"); + +cleanup_renderer: + state_free(state); + rules_free(current_rules); SDL_DestroyRenderer(renderer); + +cleanup_window: SDL_DestroyWindow(window); SDL_Quit(); - free(state); cleanup: - board_free(front); - board_free(back); + if (front != NULL) board_free(front); + if (back != NULL) board_free(back); return error_code; } diff --git a/src/rules.c b/src/rules.c new file mode 100644 index 0000000..a6bb627 --- /dev/null +++ b/src/rules.c @@ -0,0 +1,196 @@ +/** + * @file rules.c + * @brief गेम ऑफ लाइफ के लिए configurable rules system का implementation + * @author Game of Life Enhanced + * @date 2025 + * + * यह फाइल विभिन्न cellular automata rules को implement करती है। + * Bit mask technique का उपयोग करके fast rule checking प्रदान करती है। + */ + +#include +#include +#include +#include "rules.h" + +/** + * @brief custom rules initialize करता है given birth और survival conditions के साथ + * + * यह function एक generic rule set create करता है जो किसी भी birth/survival + * combination को support करता है। Bit mask technique का उपयोग करके + * O(1) time में rule checking possible हो जाती है। + * + * @param name rule set का descriptive नाम + * @param birth_counts birth के लिए valid neighbor counts का array + * @param birth_len birth_counts array की length + * @param survival_counts survival के लिए valid neighbor counts का array + * @param survival_len survival_counts array की length + * @return सफल होने पर Rules pointer, memory allocation fail होने पर NULL + */ +Rules *rules_init(const char *name, const int *birth_counts, int birth_len, + const int *survival_counts, int survival_len) { + Rules *rules = malloc(sizeof(Rules)); + if (!rules) return NULL; + + // bit masks को zero से initialize करें + rules->birth_rules = 0; + rules->survival_rules = 0; + + // birth rules के लिए appropriate bits set करें + for (int i = 0; i < birth_len; i++) { + if (birth_counts[i] >= 0 && birth_counts[i] <= MAX_NEIGHBORS) { + rules->birth_rules |= (1 << birth_counts[i]); + } + } + + // survival rules के लिए appropriate bits set करें + for (int i = 0; i < survival_len; i++) { + if (survival_counts[i] >= 0 && survival_counts[i] <= MAX_NEIGHBORS) { + rules->survival_rules |= (1 << survival_counts[i]); + } + } + + // rule set का नाम copy करें + strncpy(rules->name, name ? name : "Custom", sizeof(rules->name) - 1); + rules->name[sizeof(rules->name) - 1] = '\0'; + + return rules; +} + +/** + * @brief Classic Conway's Game of Life rules create करता है + * + * John Conway द्वारा 1970 में invented original Game of Life rules: + * - कोई भी मृत cell जिसके exactly 3 जीवित neighbors हैं वो जीवित हो जाती है (birth) + * - कोई भी जीवित cell जिसके 2 या 3 जीवित neighbors हैं वो जीवित रहती है (survival) + * - बाकी सभी cases में cell मर जाती है या मृत रहती है + * + * @return Conway's Life rules का pointer + */ +Rules *rules_conway(void) { + int birth[] = {3}; + int survival[] = {2, 3}; + return rules_init("Conway's Life (B3/S23)", birth, 1, survival, 2); +} + +/** + * @brief HighLife cellular automaton rules create करता है + * + * Nathan Thompson द्वारा discovered ये rules Conway's Life को extend करते हैं: + * - Birth: 3 या 6 neighbors + * - Survival: 2 या 3 neighbors + * + * इसमें self-replicating patterns possible हैं जो Conway's Life में नहीं हैं। + * + * @return HighLife rules का pointer + */ +Rules *rules_highlife(void) { + int birth[] = {3, 6}; + int survival[] = {2, 3}; + return rules_init("HighLife (B36/S23)", birth, 2, survival, 2); +} + +/** + * @brief Day & Night cellular automaton rules create करता है + * + * ये symmetric rules हैं जहाँ birth और survival conditions similar हैं: + * - Birth: 3, 6, 7, या 8 neighbors + * - Survival: 3, 4, 6, 7, या 8 neighbors + * + * इसमें patterns और उनके inverse दोनों stable होते हैं। + * + * @return Day & Night rules का pointer + */ +Rules *rules_day_night(void) { + int birth[] = {3, 6, 7, 8}; + int survival[] = {3, 4, 6, 7, 8}; + return rules_init("Day & Night (B3678/S34678)", birth, 4, survival, 5); +} + +/** + * @brief Maze generation rules create करता है + * + * ये rules maze-like structures generate करते हैं: + * - Birth: exactly 3 neighbors (Conway's के समान) + * - Survival: 1, 2, 3, 4, या 5 neighbors (बहुत permissive) + * + * Higher survival range के कारण complex, interconnected patterns बनते हैं। + * + * @return Maze rules का pointer + */ +Rules *rules_maze(void) { + int birth[] = {3}; + int survival[] = {1, 2, 3, 4, 5}; + return rules_init("Maze (B3/S12345)", birth, 1, survival, 5); +} + +/** + * @brief given rules के अनुसार cell का next state determine करता है + * + * यह function cell की current state और उसके neighbors की count के + * अनुसार decide करता है कि cell next generation में जीवित रहेगी या मरेगी। + * Bit mask technique का उपयोग करके O(1) time में result मिलता है। + * + * @param rules apply करने वाले rules + * @param current_state cell की current state (1=जीवित, 0=मृत) + * @param neighbor_count जीवित neighbors की संख्या (0-8) + * @param result next state store करने के लिए pointer (1=जीवित रहेगी, 0=मरेगी) + * @return सफल होने पर 0, invalid parameters होने पर -1 + */ +int rules_apply(Rules *rules, int current_state, int neighbor_count, int *result) { + if (!rules || !result || neighbor_count < 0 || neighbor_count > MAX_NEIGHBORS) { + return -1; + } + + if (current_state) { + // cell जीवित है - survival rules check करें + *result = (rules->survival_rules & (1 << neighbor_count)) != 0; + } else { + // cell मृत है - birth rules check करें + *result = (rules->birth_rules & (1 << neighbor_count)) != 0; + } + + return 0; +} + +/** + * @brief rules की allocated memory को free करता है + * + * यह function safely rules structure की memory को release करता है। + * NULL pointer को handle करता है। + * + * @param rules free करने वाले rules (NULL safe) + */ +void rules_free(Rules *rules) { + if (rules) { + free(rules); + } +} + +/** + * @brief rules का detailed description console में print करता है + * + * यह function rule set का नाम और सभी active birth/survival conditions को + * human-readable format में display करता है। Debugging और user information + * के लिए उपयोगी है। + * + * @param rules print करने वाले rules (NULL safe) + */ +void rules_print(Rules *rules) { + if (!rules) return; + + printf("Rules: %s\n", rules->name); + printf("Birth conditions (neighbor count): "); + for (int i = 0; i <= MAX_NEIGHBORS; i++) { + if (rules->birth_rules & (1 << i)) { + printf("%d ", i); + } + } + printf("\nSurvival conditions (neighbor count): "); + for (int i = 0; i <= MAX_NEIGHBORS; i++) { + if (rules->survival_rules & (1 << i)) { + printf("%d ", i); + } + } + printf("\n"); +} diff --git a/src/rules.h b/src/rules.h new file mode 100644 index 0000000..892b785 --- /dev/null +++ b/src/rules.h @@ -0,0 +1,117 @@ +/** + * @file rules.h + * @brief गेम ऑफ लाइफ के लिए configurable rules system का हेडर + * @author Game of Life Enhanced + * @date 2025 + * + * यह फाइल विभिन्न प्रकार के cellular automata rules को define और manage करने + * के लिए बनाई गई है। Bit mask technique का उपयोग करके efficient rule checking + * प्रदान करती है। + */ + +#ifndef RULES_H +#define RULES_H + +#include + +/** + * @brief एक standard grid में maximum neighbors की संख्या (8-connected grid में) + */ +#define MAX_NEIGHBORS 8 + +/** + * @brief Game rules को represent करने वाला structure + * + * यह structure bit mask technique का उपयोग करके birth और survival conditions + * को efficiently store करता है। प्रत्येक bit एक specific neighbor count के लिए + * rule को represent करता है। + */ +typedef struct Rules { + uint16_t birth_rules; /**< Birth conditions का bit mask (index = neighbor count, bit = rule active) */ + uint16_t survival_rules; /**< Survival conditions का bit mask */ + char name[64]; /**< Rule set का descriptive नाम */ +} Rules; + +/** + * @brief birth और survival neighbor counts के साथ rules initialize करता है + * @param name rule set का नाम (descriptive name) + * @param birth_counts birth के लिए valid neighbor counts का array + * @param birth_len birth_counts array का length + * @param survival_counts survival के लिए valid neighbor counts का array + * @param survival_len survival_counts array का length + * @return सफल होने पर Rules pointer, memory allocation fail होने पर NULL + */ +Rules *rules_init(const char *name, const int *birth_counts, int birth_len, + const int *survival_counts, int survival_len); + +/** + * @brief Classic Conway's Game of Life rules create करता है (B3/S23) + * + * Conway के original rules: + * - Birth: exactly 3 neighbors + * - Survival: 2 या 3 neighbors + * + * @return Conway's Life rules का pointer + */ +Rules *rules_conway(void); + +/** + * @brief HighLife variant rules create करता है (B36/S23) + * + * HighLife rules में replicator patterns possible हैं: + * - Birth: 3 या 6 neighbors + * - Survival: 2 या 3 neighbors + * + * @return HighLife rules का pointer + */ +Rules *rules_highlife(void); + +/** + * @brief Day & Night rules create करता है (B3678/S34678) + * + * Symmetric rules जहाँ empty space filled space की तरह behave करती है: + * - Birth: 3, 6, 7, या 8 neighbors + * - Survival: 3, 4, 6, 7, या 8 neighbors + * + * @return Day & Night rules का pointer + */ +Rules *rules_day_night(void); + +/** + * @brief Maze generation rules create करता है (B3/S12345) + * + * ये rules maze-like structures create करते हैं: + * - Birth: exactly 3 neighbors + * - Survival: 1, 2, 3, 4, या 5 neighbors + * + * @return Maze rules का pointer + */ +Rules *rules_maze(void); + +/** + * @brief rules के अनुसार check करता है कि cell अगली generation में जीवित होगी या नहीं + * @param rules apply करने वाले rules + * @param current_state cell की current state (1=जीवित, 0=मृत) + * @param neighbor_count जीवित neighbors की संख्या + * @param result परिणाम store करने के लिए pointer (1=जीवित रहेगी, 0=मरेगी) + * @return सफल होने पर 0, invalid parameters होने पर -1 + */ +int rules_apply(Rules *rules, int current_state, int neighbor_count, int *result); + +/** + * @brief rules की memory को free करता है + * @param rules free करने वाले rules + */ +void rules_free(Rules *rules); + +/** + * @brief rules का description console में print करता है + * + * यह function rule set का नाम और सभी birth/survival conditions को + * human-readable format में print करता है। + * + * @param rules print करने वाले rules + */ +void rules_print(Rules *rules); + +#endif // RULES_H diff --git a/src/state.c b/src/state.c index 701cd16..a9ad304 100644 --- a/src/state.c +++ b/src/state.c @@ -1,11 +1,106 @@ -#include +/** + * @file state.c + * @brief गेम state management का implementation + * @author Game of Life Enhanced + * @date 2025 + * + * यह फाइल game state के lifecycle management को handle करती है। + * Memory management और safe initialization/cleanup operations provide करती है। + */ +#include +#include #include "state.h" -struct State *state_init() { - struct State *state = malloc(sizeof(struct State)); - state->keep_alive = 1; - state->pause = 0; +/** + * @brief नया State structure create और initialize करता है + * + * यह function memory allocate करके एक नया State structure create करता है + * और सभी fields को sensible default values के साथ initialize करता है। + * Game initially paused state में start होता है। + * + * @param state_ptr double pointer जहाँ allocated State का address store होगा + * @return सफल होने पर 0, NULL pointer या memory allocation fail होने पर -1 + * + * @note Caller को state_free() call करना चाहिए जब State की जरूरत न हो + */ +int state_init(State **state_ptr) { + if (!state_ptr) { + return -1; // NULL pointer error handle करें + } + if (!(*state_ptr = malloc(sizeof(State)))) { + return -1; // Memory allocation failure handle करें + } + + // सभी fields को default values के साथ initialize करें + (*state_ptr)->keep_alive = true; // Game initially running state में + (*state_ptr)->pause = false; // Simulation initially running + (*state_ptr)->loaded_filename = NULL; // कोई file initially loaded नहीं + (*state_ptr)->current_rule_index = 0; // Default Conway's rules (index 0) + (*state_ptr)->is_dragging = false; // User initially drag नहीं कर रहा + (*state_ptr)->drag_paint_mode = true; // Default में alive cells paint करें + + return 0; +} + +/** + * @brief State structure और उसकी सारी allocated memory को free करता है + * + * यह function State struct और उसके सभी dynamically allocated members + * की memory को safely release करता है। Memory leaks को prevent करता है। + * + * @param state_ptr free करने वाला State pointer + * @return सफल होने पर 0, NULL pointer होने पर -1 + * + * @note यह function NULL pointer को safely handle करता है + */ +int state_free(State *state_ptr) { + if (!state_ptr) { + return -1; // NULL pointer error handle करें + } + + // पहले filename की memory free करें अगर allocated है + if (state_ptr->loaded_filename) { + free(state_ptr->loaded_filename); + } + + // फिर State structure की memory free करें + free(state_ptr); + return 0; +} - return state; +/** + * @brief State में loaded filename को set या update करता है + * + * यह function current loaded file का नाम State में store करता है + * ताकि बाद में reload functionality use कर सकें। यह safely previous + * filename की memory को free करता है और नई string के लिए memory allocate करता है। + * + * @param state target State structure + * @param filename set करने वाला filename (NULL होने पर current clear हो जाता है) + * @return सफल होने पर 0, NULL state या memory allocation fail होने पर -1 + * + * @note अगर filename NULL है तो current filename clear हो जाता है + * @note यह function string की copy बनाता है, original को modify नहीं करता + */ +int state_set_filename(State *state, const char *filename) { + if (!state) return -1; + + // पहले existing filename की memory free करें अगर कोई है + if (state->loaded_filename) { + free(state->loaded_filename); + state->loaded_filename = NULL; + } + + // नई filename set करें अगर provided है + if (filename) { + // नई string के लिए memory allocate करें + state->loaded_filename = malloc(strlen(filename) + 1); + if (!state->loaded_filename) return -1; // Memory allocation fail + + // filename को copy करें + strcpy(state->loaded_filename, filename); + } + + return 0; } diff --git a/src/state.h b/src/state.h index e5f8f7f..2846e44 100644 --- a/src/state.h +++ b/src/state.h @@ -1,6 +1,82 @@ -struct State { - int keep_alive; - int pause; -}; +/** + * @file state.h + * @brief गेम state management के लिए हेडर फाइल + * @author Game of Life Enhanced + * @date 2025 + * + * यह फाइल game की current state को track करने के लिए structures और + * functions को define करती है। इसमें pause state, loaded files, और + * user interaction modes शामिल हैं। + */ -struct State *state_init(); +#ifndef STATE_H +#define STATE_H + +#include + +/** + * @brief 8-bit boolean type definition space efficiency के लिए + */ +typedef uint8_t bool8; + +/** + * @brief boolean true value (1) + */ +#define true 1 + +/** + * @brief boolean false value (0) + */ +#define false 0 + +/** + * @brief Game की current state को represent करने वाला structure + * + * यह structure game के execution के दौरान सभी important state information + * को track करता है, including user preferences और current modes। + */ +typedef struct State { + bool8 keep_alive; /**< Game loop चालू रखना है या बंद करना है */ + bool8 pause; /**< Simulation pause है या चल रहा है */ + char *loaded_filename; /**< Currently loaded file का नाम (reloading के लिए) */ + int current_rule_index; /**< Current active rule set का index */ + bool8 is_dragging; /**< User mouse drag कर रहा है या नहीं */ + bool8 drag_paint_mode; /**< Drag करते समय क्या paint करना है (1=जीवित, 0=मृत) */ +} State; + +/** + * @brief नया State structure initialize करता है + * + * यह function State structure के लिए memory allocate करता है और + * सभी fields को default values के साथ initialize करता है। + * + * @param state_ptr double pointer जहाँ allocated State का address store होगा + * @return सफल होने पर 0, memory allocation fail होने पर -1 + */ +int state_init(State **state_ptr); + +/** + * @brief State structure की memory को free करता है + * + * यह function State struct और उसके सभी allocated members (जैसे filename) + * की memory को safely release करता है। + * + * @param state_ptr free करने वाला State pointer + * @return सफल होने पर 0, NULL pointer होने पर -1 + */ +int state_free(State *state_ptr); + +/** + * @brief State में loaded filename को set या update करता है + * + * यह function state में current loaded file का नाम store करता है + * ताकि बाद में reload operation perform कर सकें। पहले से stored filename + * को properly free करता है। + * + * @param state target State structure + * @param filename set करने वाला filename (NULL होने पर current filename clear हो जाता है) + * @return सफल होने पर 0, memory allocation fail होने पर -1 + */ +int state_set_filename(State *state, const char *filename); + +#endif // STATE_H