Version 1.0
This commit is contained in:
parent
a49a69acd5
commit
74ca7d42aa
|
@ -0,0 +1,36 @@
|
|||
CC = gcc
|
||||
CFLAGS = -Wall -Wextra -Werror -O3 -std=c11
|
||||
INCLUDES = -Iinclude
|
||||
SRCDIR = src
|
||||
OBJDIR = obj
|
||||
BINDIR = bin
|
||||
|
||||
SOURCES = $(wildcard $(SRCDIR)/*.c)
|
||||
OBJECTS = $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o)
|
||||
EXECUTABLE = $(BINDIR)/galaxies
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(EXECUTABLE)
|
||||
|
||||
$(EXECUTABLE): $(OBJECTS) | $(BINDIR)
|
||||
$(CC) $(CFLAGS) $(OBJECTS) -o $@
|
||||
|
||||
$(OBJDIR)/%.o: $(SRCDIR)/%.c | $(OBJDIR)
|
||||
$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
|
||||
|
||||
$(OBJDIR):
|
||||
mkdir -p $@
|
||||
|
||||
$(BINDIR):
|
||||
mkdir -p $@
|
||||
|
||||
clean:
|
||||
rm -rf $(OBJDIR) $(BINDIR)
|
||||
|
||||
# Header dependencies
|
||||
$(OBJDIR)/main.o: include/generator.h include/solver.h include/output.h
|
||||
$(OBJDIR)/generator.o: include/generator.h include/utils.h
|
||||
$(OBJDIR)/solver.o: include/solver.h include/generator.h include/utils.h
|
||||
$(OBJDIR)/output.o: include/output.h include/generator.h
|
||||
$(OBJDIR)/utils.o: include/utils.h include/generator.h
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef GENERATOR_H
|
||||
#define GENERATOR_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "utils.h"
|
||||
|
||||
#define CELL_EMPTY 0
|
||||
#define CELL_CENTER 1
|
||||
#define CELL_SYMMETRY 128
|
||||
|
||||
typedef struct GameBoard {
|
||||
uint8_t width;
|
||||
uint8_t height;
|
||||
uint8_t *cells;
|
||||
Point *centers;
|
||||
int center_count;
|
||||
} GameBoard;
|
||||
|
||||
GameBoard *create_board(uint8_t width, uint8_t height);
|
||||
void destroy_board(GameBoard *board);
|
||||
int generate_puzzle(GameBoard *board);
|
||||
|
||||
#endif // GENERATOR_H
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef OUTPUT_H
|
||||
#define OUTPUT_H
|
||||
|
||||
#include "generator.h"
|
||||
|
||||
void print_unsolved_puzzle(GameBoard *board, const char *filename);
|
||||
void print_solved_puzzle(GameBoard *board, const char *filename);
|
||||
void print_ascii_puzzle(GameBoard *board, const char *filename);
|
||||
|
||||
#endif // OUTPUT_H
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef SOLVER_H
|
||||
#define SOLVER_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "generator.h"
|
||||
|
||||
bool solve_puzzle(GameBoard *board);
|
||||
|
||||
#endif // SOLVER_H
|
|
@ -0,0 +1,24 @@
|
|||
#ifndef UTILS_H
|
||||
#define UTILS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// Ado you are GameBoard
|
||||
struct GameBoard;
|
||||
|
||||
typedef struct {
|
||||
int8_t x;
|
||||
int8_t y;
|
||||
} Point;
|
||||
|
||||
void init_random(unsigned int seed);
|
||||
int random_int(int min, int max);
|
||||
|
||||
void set_cell(struct GameBoard *board, uint8_t x, uint8_t y, uint8_t value);
|
||||
uint8_t get_cell(const struct GameBoard *board, uint8_t x, uint8_t y);
|
||||
int is_valid_coord(const struct GameBoard *board, int8_t x, int8_t y);
|
||||
|
||||
Point get_symmetric_point(const struct GameBoard *board, Point center, Point p);
|
||||
int is_symmetric(const struct GameBoard *board, Point center, Point p1, Point p2);
|
||||
|
||||
#endif // UTILS_H
|
|
@ -0,0 +1,139 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "../include/generator.h"
|
||||
#include "../include/utils.h"
|
||||
|
||||
static void place_centers(GameBoard *board);
|
||||
|
||||
static int is_adjacent_to_center(const GameBoard *board, int x, int y) {
|
||||
for (int dx = -1; dx <= 1; dx++) {
|
||||
for (int dy = -1; dy <= 1; dy++) {
|
||||
if (dx == 0 && dy == 0) continue;
|
||||
int nx = x + dx, ny = y + dy;
|
||||
if (is_valid_coord(board, nx, ny) && get_cell(board, nx, ny) == CELL_CENTER) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void grow_galaxy(GameBoard *board, int x, int y, int galaxy_id) {
|
||||
Point center = {x, y};
|
||||
int directions[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int dx = directions[i][0], dy = directions[i][1];
|
||||
int nx = x + dx, ny = y + dy;
|
||||
Point sym = get_symmetric_point(board, center, (Point){nx, ny});
|
||||
int sym_x = sym.x, sym_y = sym.y;
|
||||
|
||||
if (is_valid_coord(board, nx, ny) && is_valid_coord(board, sym_x, sym_y) &&
|
||||
get_cell(board, nx, ny) == CELL_EMPTY && get_cell(board, sym_x, sym_y) == CELL_EMPTY) {
|
||||
set_cell(board, nx, ny, galaxy_id);
|
||||
set_cell(board, sym_x, sym_y, galaxy_id);
|
||||
grow_galaxy(board, nx, ny, galaxy_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void generate_galaxies(GameBoard *board) {
|
||||
int galaxy_id = 2;
|
||||
for (int y = 0; y < board->height; y++) {
|
||||
for (int x = 0; x < board->width; x++) {
|
||||
if (get_cell(board, x, y) == CELL_CENTER) {
|
||||
grow_galaxy(board, x, y, galaxy_id++);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void generate_symmetry_lines(GameBoard *board) {
|
||||
for (int y = 0; y < board->height; y++) {
|
||||
for (int x = 0; x < board->width; x++) {
|
||||
if (get_cell(board, x, y) == CELL_CENTER) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int nx = x, ny = y;
|
||||
while (1) {
|
||||
nx += (i == 1) - (i == 3);
|
||||
ny += (i == 0) - (i == 2);
|
||||
if (!is_valid_coord(board, nx, ny) || get_cell(board, nx, ny) != get_cell(board, x, y)) break;
|
||||
set_cell(board, nx, ny, get_cell(board, nx, ny) | CELL_SYMMETRY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int find_neighbor_galaxy(const GameBoard *board, int x, int y) {
|
||||
int directions[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int nx = x + directions[i][0], ny = y + directions[i][1];
|
||||
if (is_valid_coord(board, nx, ny) && get_cell(board, nx, ny) != CELL_EMPTY) {
|
||||
return get_cell(board, nx, ny) & ~CELL_SYMMETRY;
|
||||
}
|
||||
}
|
||||
return CELL_EMPTY;
|
||||
}
|
||||
|
||||
int generate_puzzle(GameBoard *board) {
|
||||
memset(board->cells, CELL_EMPTY, board->width * board->height);
|
||||
|
||||
place_centers(board);
|
||||
generate_galaxies(board);
|
||||
generate_symmetry_lines(board);
|
||||
|
||||
for (int y = 0; y < board->height; y++) {
|
||||
for (int x = 0; x < board->width; x++) {
|
||||
if (get_cell(board, x, y) == CELL_EMPTY) {
|
||||
int neighbor = find_neighbor_galaxy(board, x, y);
|
||||
if (neighbor == CELL_EMPTY) return 0;
|
||||
set_cell(board, x, y, neighbor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
GameBoard *create_board(uint8_t width, uint8_t height) {
|
||||
GameBoard *board = malloc(sizeof(GameBoard));
|
||||
if (!board) return NULL;
|
||||
|
||||
board->width = width;
|
||||
board->height = height;
|
||||
board->cells = calloc(width * height, sizeof(uint8_t));
|
||||
board->centers = malloc(sizeof(Point) * (width * height / 25));
|
||||
board->center_count = 0;
|
||||
|
||||
if (!board->cells || !board->centers) {
|
||||
free(board->cells);
|
||||
free(board->centers);
|
||||
free(board);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return board;
|
||||
}
|
||||
|
||||
void destroy_board(GameBoard *board) {
|
||||
if (board) {
|
||||
free(board->cells);
|
||||
free(board->centers);
|
||||
free(board);
|
||||
}
|
||||
}
|
||||
|
||||
static void place_centers(GameBoard *board) {
|
||||
int centers = board->width * board->height / 25;
|
||||
while (centers > 0) {
|
||||
int x = random_int(0, board->width - 1);
|
||||
int y = random_int(0, board->height - 1);
|
||||
if (get_cell(board, x, y) == CELL_EMPTY && !is_adjacent_to_center(board, x, y)) {
|
||||
set_cell(board, x, y, CELL_CENTER);
|
||||
board->centers[board->center_count++] = (Point){x, y};
|
||||
centers--;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include "../include/generator.h"
|
||||
#include "../include/solver.h"
|
||||
#include "../include/output.h"
|
||||
#include "../include/utils.h"
|
||||
|
||||
#define DEFAULT_SIZE 10
|
||||
|
||||
void create_puzzle_directory(unsigned int seed) {
|
||||
char dirname[20];
|
||||
snprintf(dirname, sizeof(dirname), "puzzle_%u", seed);
|
||||
|
||||
if (mkdir(dirname, 0777) == -1 && errno != EEXIST) {
|
||||
fprintf(stderr, "Error creating directory: %s\n", dirname);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
unsigned int seed = (unsigned int)time(NULL);
|
||||
int size = DEFAULT_SIZE;
|
||||
|
||||
if (argc > 1) {
|
||||
seed = (unsigned int)atoi(argv[1]);
|
||||
}
|
||||
if (argc > 2) {
|
||||
size = atoi(argv[2]);
|
||||
if (size < 5 || size > 20) {
|
||||
fprintf(stderr, "Size must be between 5 and 20\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
create_puzzle_directory(seed);
|
||||
init_random(seed);
|
||||
|
||||
GameBoard *board = create_board(size, size);
|
||||
if (!board) {
|
||||
fprintf(stderr, "Failed to create board\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!generate_puzzle(board)) {
|
||||
fprintf(stderr, "Failed to generate puzzle\n");
|
||||
destroy_board(board);
|
||||
return 1;
|
||||
}
|
||||
|
||||
char filename[50];
|
||||
snprintf(filename, sizeof(filename), "puzzle_%u/unsolved.pbm", seed);
|
||||
print_unsolved_puzzle(board, filename);
|
||||
|
||||
if (!solve_puzzle(board)) {
|
||||
fprintf(stderr, "Generated puzzle is not solvable\n");
|
||||
destroy_board(board);
|
||||
return 1;
|
||||
}
|
||||
|
||||
snprintf(filename, sizeof(filename), "puzzle_%u/solved.pbm", seed);
|
||||
print_solved_puzzle(board, filename);
|
||||
|
||||
snprintf(filename, sizeof(filename), "puzzle_%u/solved.txt", seed);
|
||||
print_ascii_puzzle(board, filename);
|
||||
|
||||
destroy_board(board);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "../include/output.h"
|
||||
#include "../include/utils.h"
|
||||
|
||||
#define MAX_INTENSITY 255
|
||||
#define CENTER_RADIUS 2
|
||||
#define LINE_THICKNESS 1
|
||||
|
||||
void set_pixel(unsigned char *image, int width, int x, int y, unsigned char value) {
|
||||
if (x >= 0 && x < width && y >= 0 && y < width) {
|
||||
image[y * width + x] = value;
|
||||
}
|
||||
}
|
||||
|
||||
void draw_circle(unsigned char *image, int width, int cx, int cy, int radius) {
|
||||
for (int y = -radius; y <= radius; y++) {
|
||||
for (int x = -radius; x <= radius; x++) {
|
||||
if (x*x + y*y <= radius*radius) {
|
||||
set_pixel(image, width, cx + x, cy + y, MAX_INTENSITY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw_line(unsigned char *image, int width, int x1, int y1, int x2, int y2) {
|
||||
int dx = abs(x2 - x1), sx = x1 < x2 ? 1 : -1;
|
||||
int dy = abs(y2 - y1), sy = y1 < y2 ? 1 : -1;
|
||||
int err = (dx > dy ? dx : -dy) / 2, e2;
|
||||
|
||||
while (1) {
|
||||
set_pixel(image, width, x1, y1, MAX_INTENSITY);
|
||||
if (x1 == x2 && y1 == y2) break;
|
||||
e2 = err;
|
||||
if (e2 > -dx) { err -= dy; x1 += sx; }
|
||||
if (e2 < dy) { err += dx; y1 += sy; }
|
||||
}
|
||||
}
|
||||
|
||||
void print_pgm(GameBoard *board, const char *filename, int show_solved) {
|
||||
int width = board->width * 10; // ado you are killer
|
||||
unsigned char *image = calloc(width * width, sizeof(unsigned char));
|
||||
if (!image) {
|
||||
fprintf(stderr, "Failed to allocate memory for image\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// ado you are center
|
||||
for (int i = 0; i < board->center_count; i++) {
|
||||
int cx = board->centers[i].x * 10 + 5;
|
||||
int cy = board->centers[i].y * 10 + 5;
|
||||
draw_circle(image, width, cx, cy, CENTER_RADIUS);
|
||||
}
|
||||
|
||||
// ado you are puzzle
|
||||
if (show_solved) {
|
||||
for (int y = 0; y < board->height; y++) {
|
||||
for (int x = 0; x < board->width; x++) {
|
||||
uint8_t cell = get_cell(board, x, y);
|
||||
if (cell != CELL_EMPTY && cell != CELL_CENTER) {
|
||||
int cx = x * 10 + 5, cy = y * 10 + 5;
|
||||
for (int dy = -1; dy <= 1; dy++) {
|
||||
for (int dx = -1; dx <= 1; dx++) {
|
||||
if (dx == 0 && dy == 0) continue;
|
||||
int nx = x + dx, ny = y + dy;
|
||||
if (is_valid_coord(board, nx, ny) && get_cell(board, nx, ny) == cell) {
|
||||
int nx_scaled = nx * 10 + 5, ny_scaled = ny * 10 + 5;
|
||||
draw_line(image, width, cx, cy, nx_scaled, ny_scaled);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ado you are pgm
|
||||
FILE *fp = fopen(filename, "wb");
|
||||
if (!fp) {
|
||||
fprintf(stderr, "Error opening file: %s\n", filename);
|
||||
free(image);
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(fp, "P5\n%d %d\n%d\n", width, width, MAX_INTENSITY);
|
||||
fwrite(image, sizeof(unsigned char), width * width, fp);
|
||||
|
||||
fclose(fp);
|
||||
free(image);
|
||||
}
|
||||
|
||||
void print_unsolved_puzzle(GameBoard *board, const char *filename) {
|
||||
print_pgm(board, filename, 0);
|
||||
}
|
||||
|
||||
void print_solved_puzzle(GameBoard *board, const char *filename) {
|
||||
print_pgm(board, filename, 1);
|
||||
}
|
||||
|
||||
void print_ascii_puzzle(GameBoard *board, const char *filename) {
|
||||
FILE *fp = fopen(filename, "w");
|
||||
if (!fp) {
|
||||
fprintf(stderr, "Error opening file: %s\n", filename);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int y = 0; y < board->height; y++) {
|
||||
for (int x = 0; x < board->width; x++) {
|
||||
uint8_t cell = get_cell(board, x, y);
|
||||
if (cell == CELL_CENTER) {
|
||||
fprintf(fp, "O");
|
||||
} else if (cell != CELL_EMPTY) {
|
||||
fprintf(fp, "#");
|
||||
} else {
|
||||
fprintf(fp, ".");
|
||||
}
|
||||
}
|
||||
fprintf(fp, "\n");
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include "../include/solver.h"
|
||||
#include "../include/utils.h"
|
||||
|
||||
#define MAX_ITERATIONS 1000000
|
||||
|
||||
typedef struct {
|
||||
int x, y;
|
||||
int center_index;
|
||||
} CellAssignment;
|
||||
|
||||
static bool is_valid_assignment(GameBoard *board, int x, int y, int center_index) {
|
||||
Point center = board->centers[center_index];
|
||||
int dx = x - center.x;
|
||||
int dy = y - center.y;
|
||||
|
||||
int sym_x = center.x - dx;
|
||||
int sym_y = center.y - dy;
|
||||
|
||||
if (sym_x < 0 || sym_x >= board->width || sym_y < 0 || sym_y >= board->height) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t sym_cell = get_cell(board, sym_x, sym_y);
|
||||
return sym_cell == CELL_EMPTY || sym_cell == (center_index + 1);
|
||||
}
|
||||
|
||||
static bool backtrack(GameBoard *board, CellAssignment *stack, int stack_size) {
|
||||
if (stack_size == board->width * board->height) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int x = stack_size % board->width;
|
||||
int y = stack_size / board->width;
|
||||
|
||||
if (get_cell(board, x, y) != CELL_EMPTY) {
|
||||
stack[stack_size].x = x;
|
||||
stack[stack_size].y = y;
|
||||
stack[stack_size].center_index = get_cell(board, x, y) - 1;
|
||||
return backtrack(board, stack, stack_size + 1);
|
||||
}
|
||||
|
||||
for (int i = 0; i < board->center_count; i++) {
|
||||
if (is_valid_assignment(board, x, y, i)) {
|
||||
set_cell(board, x, y, i + 1);
|
||||
stack[stack_size].x = x;
|
||||
stack[stack_size].y = y;
|
||||
stack[stack_size].center_index = i;
|
||||
|
||||
if (backtrack(board, stack, stack_size + 1)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
set_cell(board, x, y, CELL_EMPTY);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool solve_puzzle(GameBoard *board) {
|
||||
CellAssignment *stack = malloc(board->width * board->height * sizeof(CellAssignment));
|
||||
if (!stack) return false;
|
||||
|
||||
bool result = backtrack(board, stack, 0);
|
||||
|
||||
free(stack);
|
||||
return result;
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
#include <stdlib.h>
|
||||
#include "../include/utils.h"
|
||||
#include "../include/generator.h" // add you are GameBoard definition
|
||||
static unsigned int next = 1;
|
||||
|
||||
void init_random(unsigned int seed) {
|
||||
next = seed;
|
||||
}
|
||||
|
||||
int random_int(int min, int max) {
|
||||
next = next * 1103515245 + 12345;
|
||||
return min + (int)((next / 65536) % (max - min + 1));
|
||||
}
|
||||
|
||||
void set_cell(struct GameBoard *board, uint8_t x, uint8_t y, uint8_t value) {
|
||||
board->cells[y * board->width + x] = value;
|
||||
}
|
||||
|
||||
uint8_t get_cell(const struct GameBoard *board, uint8_t x, uint8_t y) {
|
||||
return board->cells[y * board->width + x];
|
||||
}
|
||||
|
||||
int is_valid_coord(const struct GameBoard *board, int8_t x, int8_t y) {
|
||||
return x >= 0 && x < board->width && y >= 0 && y < board->height;
|
||||
}
|
||||
|
||||
Point get_symmetric_point(const struct GameBoard *board, Point center, Point p) {
|
||||
(void)board; // ado you are suppressed
|
||||
Point sym;
|
||||
sym.x = 2 * center.x - p.x;
|
||||
sym.y = 2 * center.y - p.y;
|
||||
return sym;
|
||||
}
|
||||
|
||||
int is_symmetric(const struct GameBoard *board, Point center, Point p1, Point p2) {
|
||||
Point sym = get_symmetric_point(board, center, p1);
|
||||
return sym.x == p2.x && sym.y == p2.y;
|
||||
}
|
Loading…
Reference in New Issue