306 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			306 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * Created by snapshot112 on 2/10/2025
 | |
|  */
 | |
| 
 | |
| 
 | |
| #include "grid.h"
 | |
| 
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <stdio.h>
 | |
| 
 | |
| /*
 | |
|  * The grid type this program is build around.
 | |
|  */
 | |
| typedef struct grid_data {
 | |
|     char *locations;
 | |
|     int height;
 | |
|     int width;
 | |
|     state state;
 | |
| } grid;
 | |
| 
 | |
| /*
 | |
|  * Translate x and y coordinates to a location index on the grid.
 | |
|  */
 | |
| static int internal_location(const grid *gp, const int x, const int y) {
 | |
|     return y * (gp->width + 1) + x;
 | |
| }
 | |
| 
 | |
| grid *grid_create_from_string(const char* input) {
 | |
|     int width = 0;
 | |
|     int height = 0;
 | |
|     const size_t len = strlen(input) / sizeof(char);
 | |
|     if (input == NULL || len == 0) {
 | |
|         printf("invalid input\n");
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     for (int i = 0; input[i] != '\n'; i++) {
 | |
|         width++;
 | |
|     }
 | |
| 
 | |
|     height = (int)len / (width + 1);
 | |
| 
 | |
|     for (int i = 0; i < height; i = i + width + 1) {
 | |
|         if ((int)strcspn(&input[i], "\n") != width) {
 | |
|             printf("line %d was not %d wide\n", i, width);
 | |
|             return NULL;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     const int grid_size = (width + 1) * height + 1;
 | |
| 
 | |
|     grid *gp = malloc(sizeof(grid));
 | |
| 
 | |
|     if (gp == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     gp->locations = malloc(grid_size * sizeof(char));
 | |
|     gp->height = height;
 | |
|     gp->width = width;
 | |
|     gp->state = STATE_BEGIN;
 | |
| 
 | |
|     strcpy(gp->locations, input);
 | |
| 
 | |
|     return gp;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Sets a grids width and height
 | |
|  *
 | |
|  * Input:
 | |
|  * fh: The stream to read the grid from.
 | |
|  * gp: A pointer to the grid whose width and height you want to set.
 | |
|  *
 | |
|  * Side effects:
 | |
|  * the grid gets its width and height set
 | |
|  *
 | |
|  * Output:
 | |
|  * 1 if the file width and height seem to match with its size
 | |
|  * 0 otherwise
 | |
|  */
 | |
| static int get_grid_sizes(FILE *fh, grid *gp) {
 | |
|     while (getc(fh) != '\n') {
 | |
|         if (feof(fh)) {
 | |
|             return 0;
 | |
|         }
 | |
|         gp->width++;
 | |
|     }
 | |
| 
 | |
|     if (gp->width == 0) {
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     fseek(fh, 0, SEEK_END);
 | |
| 
 | |
|     // Get file size (- 1 for the blank newline and EOF at the end)
 | |
|     if (ftell(fh) % (gp->width + 1) != 0) {
 | |
|         // Not all lines are the same width
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     gp->height = (int)ftell(fh) / (int)sizeof(char) / (gp->width + 1);
 | |
|     fseek(fh, 0, SEEK_SET);
 | |
| 
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| grid *grid_create_from_file(FILE *fh) {
 | |
|     if (fh == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     grid temp_grid = {
 | |
|         NULL,
 | |
|         0,
 | |
|         0,
 | |
|         STATE_BEGIN
 | |
|     };
 | |
| 
 | |
|     // Sets the width and height of the grid.
 | |
|     if (get_grid_sizes(fh, &temp_grid) != 1) {
 | |
|         // Unlogical file structure.
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     const int grid_size = (temp_grid.width + 1) * temp_grid.height + 1;
 | |
| 
 | |
|     temp_grid.locations = malloc(grid_size * sizeof(char));
 | |
|     if (temp_grid.locations == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     // This makes the strncat() work.
 | |
|     temp_grid.locations[0] = '\0';
 | |
| 
 | |
|     char *line = malloc((temp_grid.width + 2) * sizeof(char));
 | |
|     if (line == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     for (int i = 0; i < temp_grid.height; i++) {
 | |
|         if (fgets(line, temp_grid.width + 2, fh) == NULL) {
 | |
|             free(temp_grid.locations);
 | |
|             free(line);
 | |
|             return NULL;
 | |
|         }
 | |
| 
 | |
|         // Validate that the line length is correct
 | |
|         if ((int)strcspn(line, "\n") != temp_grid.width) {
 | |
|             free(temp_grid.locations);
 | |
|             free(line);
 | |
|             return NULL;
 | |
|         }
 | |
| 
 | |
|         // Width is without the newline at the end.
 | |
|         strncat(temp_grid.locations, line, temp_grid.width + 1);
 | |
|     }
 | |
| 
 | |
|     free(line);
 | |
| 
 | |
|     grid *return_grid = malloc(sizeof(temp_grid));
 | |
|     if (return_grid == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     memcpy(return_grid, &temp_grid, sizeof(temp_grid));
 | |
| 
 | |
|     return return_grid;
 | |
| }
 | |
| 
 | |
| state grid_fetch_state(const grid *gp) {
 | |
|     if (gp != NULL) {
 | |
|         return gp->state;
 | |
|     }
 | |
|     return STATE_VERLOREN;
 | |
| }
 | |
| 
 | |
| void grid_put_state(grid *gp, const state t) {
 | |
|     if (gp != NULL) {
 | |
|         switch (t) {
 | |
|             case STATE_BEGIN:
 | |
|                 gp->state = STATE_BEGIN;
 | |
|                 break;
 | |
|             case STATE_AAN_HET_SPELEN:
 | |
|                 gp->state = STATE_AAN_HET_SPELEN;
 | |
|                 break;
 | |
|             case STATE_GEWONNEN:
 | |
|                 gp->state = STATE_GEWONNEN;
 | |
|                 break;
 | |
|             case STATE_VERLOREN:
 | |
|                 gp->state = STATE_VERLOREN;
 | |
|                 break;
 | |
|             case STATE_QUIT:
 | |
|                 gp->state = STATE_QUIT;
 | |
|                 break;
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| void grid_cleanup(grid *gp) {
 | |
|     if (gp != NULL) {
 | |
|         if (gp->locations != NULL)
 | |
|         {
 | |
|             free(gp->locations);
 | |
|         }
 | |
|         free(gp);
 | |
|     }
 | |
| }
 | |
| 
 | |
| int grid_width(const grid *gp) {
 | |
|     if (gp == NULL) {
 | |
|         return 0;
 | |
|     }
 | |
|     return gp->width;
 | |
| }
 | |
| 
 | |
| int grid_height(const grid *gp) {
 | |
|     if (gp == NULL) {
 | |
|         return 0;
 | |
|     }
 | |
|     return gp->height;
 | |
| }
 | |
| 
 | |
| int grid_contains(const grid *gp, const int x, const int y) {
 | |
|     if (gp != NULL && gp->locations != NULL) {
 | |
|         if (x >= 0 && y >= 0 && x < gp->width && y < gp->height)
 | |
|         {
 | |
|             return 1;
 | |
|         }
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| char grid_fetch(const grid *gp, const int x, const int y) {
 | |
|     if (gp != NULL && gp->locations != NULL && grid_contains(gp, x, y) == 1) {
 | |
|         return gp->locations[internal_location(gp, x, y)];
 | |
|     }
 | |
|     return '\0';
 | |
| }
 | |
| 
 | |
| int grid_put(grid *gp, const int x, const int y, const char c) {
 | |
|     if (gp != NULL && gp->locations != NULL && grid_contains(gp, x, y) == 1) {
 | |
|         gp->locations[internal_location(gp, x, y)] = c;
 | |
|         return 1;
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| char *grid_fetch_row(const grid *gp, const int y) {
 | |
| 	if (gp != NULL && gp->locations != NULL && grid_contains(gp, 0, y) == 1) {
 | |
| 	    // we're going to remove the newline so this is long enough
 | |
| 		char *row = malloc((gp->width + 1) * sizeof(char));
 | |
| 		memcpy(row, &gp->locations[internal_location(gp, 0, y)], gp->width * sizeof(char));
 | |
| 	    row[gp->width] = '\0';
 | |
| 		return row;
 | |
| 	}
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| grid *grid_copy(const grid *gp) {
 | |
| 	if (gp != NULL && gp->locations != NULL) {
 | |
| 	    const size_t grid_memory = ((gp->width + 1) * gp->height + 1) * sizeof(char);
 | |
| 
 | |
|         char *locations = malloc(grid_memory);
 | |
|         if (locations == NULL) {
 | |
|             return NULL;
 | |
|         }
 | |
| 
 | |
| 	    grid *new_grid = malloc(sizeof(*gp));
 | |
| 	    if (new_grid == NULL) {
 | |
| 	        return NULL;
 | |
| 	    }
 | |
| 
 | |
| 	    memcpy(locations, gp->locations, grid_memory);
 | |
| 
 | |
| 	    memcpy(new_grid, gp, sizeof(*gp));
 | |
| 
 | |
| 	    new_grid->locations = locations;
 | |
| 	    return new_grid;
 | |
|     }
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| void grid_find(const grid *gp, const char c, int *x, int *y) {
 | |
|     if (gp == NULL || gp->locations == NULL) {
 | |
|         *x = -1;
 | |
|         *y = -1;
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     const char search[2] = {c};
 | |
| 
 | |
|     const int char_index = (int)strcspn(gp->locations, search);
 | |
| 
 | |
|     if (gp->locations[char_index] == '\0')
 | |
|     {
 | |
|         *x = -1;
 | |
|         *y = -1;
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     *x = char_index % (gp->width + 1);
 | |
|     *y = char_index / (gp->width + 1);
 | |
| }
 |