mirror of
https://github.com/IllusionMan1212/cudoku.git
synced 2026-10-24 02:12:25 +00:00
generate valid and unique board
This commit is contained in:
@@ -260,3 +260,132 @@ void toggle_selection(Cudoku *game) {
|
|||||||
if (!game->has_won)
|
if (!game->has_won)
|
||||||
game->should_draw_selection = !game->should_draw_selection;
|
game->should_draw_selection = !game->should_draw_selection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void remove_arr_element(int *arr, int index, int size) {
|
||||||
|
for (int i = index; i < size - 1; i++) {
|
||||||
|
arr[i] = arr[i + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get_candidates(int board[9][9], int row, int col, int candidates[], int *size) {
|
||||||
|
// use this as a set to check if a number is already in the row, col, or box
|
||||||
|
// 0 is no clash, 1 is clash
|
||||||
|
int clashingDigits[9] = {0};
|
||||||
|
// check row
|
||||||
|
for (int i = 0; i < 9; i++) {
|
||||||
|
if (board[row][i] != 0) {
|
||||||
|
clashingDigits[board[row][i] - 1] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check col
|
||||||
|
for (int i = 0; i < 9; i++) {
|
||||||
|
if (board[i][col] != 0) {
|
||||||
|
clashingDigits[board[i][col] - 1] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check box
|
||||||
|
int box_row = floor(row / 3.f) * 3;
|
||||||
|
int box_col = floor(col / 3.f) * 3;
|
||||||
|
for (int i = box_row; i < box_row + 3; i++) {
|
||||||
|
for (int j = box_col; j < box_col + 3; j++) {
|
||||||
|
if (board[i][j] != 0) {
|
||||||
|
clashingDigits[board[i][j] - 1] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 9; i++) {
|
||||||
|
if (clashingDigits[i] != 1) {
|
||||||
|
candidates[(*size)++] = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool backtracker(Cudoku *game, int start_row, int start_col) {
|
||||||
|
// if we went thru all the cols but not all the rows
|
||||||
|
if (start_col >= 9 && start_row < 9 - 1) {
|
||||||
|
start_row++;
|
||||||
|
start_col = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we went thru the entire board
|
||||||
|
if (start_row >= 8 && start_col >= 9) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we're at diagonal box 1, 5 or 9, skip it
|
||||||
|
if (start_row < 3) {
|
||||||
|
if (start_col < 3) {
|
||||||
|
start_col = 3;
|
||||||
|
}
|
||||||
|
} else if (start_row < 6) {
|
||||||
|
if (start_col == (floor(start_row / 3.f) * 3)) {
|
||||||
|
start_col += 3;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (start_col == 6) {
|
||||||
|
start_row++;
|
||||||
|
start_col = 0;
|
||||||
|
if (start_row >= 9) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int num_candidates = 0;
|
||||||
|
int candidates[9] = {0};
|
||||||
|
get_candidates(game->board, start_row, start_col, candidates, &num_candidates);
|
||||||
|
|
||||||
|
// try every candidate until we find one that uniquely solves the board.
|
||||||
|
while (num_candidates) {
|
||||||
|
uint rand_idx = rand() % num_candidates;
|
||||||
|
uint candidate = candidates[rand_idx];
|
||||||
|
remove_arr_element(candidates, rand_idx, num_candidates);
|
||||||
|
num_candidates--;
|
||||||
|
game->board[start_row][start_col] = candidate;
|
||||||
|
if (backtracker(game, start_row, start_col + 1)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
game->board[start_row][start_col] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if no candidates, we backtrack and try something else.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void generate_random_board(Cudoku *game) {
|
||||||
|
int size = 9;
|
||||||
|
// fill diagonal boxes 1, 5, 9
|
||||||
|
int digits[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||||
|
for (int row = 0; row < 9; row++) {
|
||||||
|
if (row % 3 == 0) {
|
||||||
|
for (int l = 0; l < 9; l++) {
|
||||||
|
digits[l] = l + 1;
|
||||||
|
}
|
||||||
|
size = 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int k = floor(row / 3.f) * 3;
|
||||||
|
for (int j = k; j < k + 3; j++) {
|
||||||
|
int rand_idx = rand() % size;
|
||||||
|
int num = digits[rand_idx];
|
||||||
|
remove_arr_element(digits, rand_idx, size--);
|
||||||
|
game->board[row][j] = num;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill the rest of the boxes
|
||||||
|
backtracker(game, 0, 3);
|
||||||
|
|
||||||
|
// copy the board to the solution
|
||||||
|
for (int i = 0; i < 9; i++) {
|
||||||
|
for (int j = 0; j < 9; j++) {
|
||||||
|
game->solution[i][j] = game->board[i][j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ typedef struct Vec2 {
|
|||||||
|
|
||||||
typedef struct Cudoku {
|
typedef struct Cudoku {
|
||||||
int board[9][9];
|
int board[9][9];
|
||||||
|
int solution[9][9];
|
||||||
bool has_won;
|
bool has_won;
|
||||||
Vec2 selection;
|
Vec2 selection;
|
||||||
bool should_draw_selection;
|
bool should_draw_selection;
|
||||||
@@ -29,3 +30,4 @@ void do_selection(Cudoku *game, int x, int y, int width, int height, float x_sca
|
|||||||
void set_selected_number(Cudoku *game, int number);
|
void set_selected_number(Cudoku *game, int number);
|
||||||
void move_selection(Cudoku *game, int x, int y);
|
void move_selection(Cudoku *game, int x, int y);
|
||||||
void toggle_selection(Cudoku *game);
|
void toggle_selection(Cudoku *game);
|
||||||
|
void generate_random_board(Cudoku *game);
|
||||||
|
|||||||
@@ -105,8 +105,6 @@ int main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Cudoku game = {0};
|
|
||||||
|
|
||||||
int res = init_x11(&display, &window);
|
int res = init_x11(&display, &window);
|
||||||
if (res < 0) return 1;
|
if (res < 0) return 1;
|
||||||
|
|
||||||
@@ -119,6 +117,19 @@ int main(int argc, char *argv[]) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
srand(time(NULL));
|
||||||
|
|
||||||
|
Cudoku game = {0};
|
||||||
|
generate_random_board(&game);
|
||||||
|
|
||||||
|
for (int i = 0; i < 9; i++) {
|
||||||
|
for (int j = 0; j < 9; j++) {
|
||||||
|
printf("%d | ", game.board[i][j]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
unsigned int font_vao, font_vbo;
|
unsigned int font_vao, font_vbo;
|
||||||
prepare_font(&font_vao, &font_vbo);
|
prepare_font(&font_vao, &font_vbo);
|
||||||
Shader font_shader = create_shader("shaders/font_v.vert", "shaders/font_f.frag");
|
Shader font_shader = create_shader("shaders/font_v.vert", "shaders/font_f.frag");
|
||||||
@@ -139,16 +150,6 @@ int main(int argc, char *argv[]) {
|
|||||||
Shader win_shader = create_shader("shaders/board_v.vert", "shaders/quad.frag");
|
Shader win_shader = create_shader("shaders/board_v.vert", "shaders/quad.frag");
|
||||||
unsigned int win_vao = prepare_win_overlay();
|
unsigned int win_vao = prepare_win_overlay();
|
||||||
|
|
||||||
srand(time(NULL));
|
|
||||||
|
|
||||||
for (int i = 0; i < 9; i++) {
|
|
||||||
for (int j = 0; j < 9; j++) {
|
|
||||||
printf("%d | ", game.board[i][j]);
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
bool quit = false;
|
bool quit = false;
|
||||||
while (!quit) {
|
while (!quit) {
|
||||||
while (XPending(display)) {
|
while (XPending(display)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user