first commit

This commit is contained in:
retoor 2025-01-10 22:53:10 +01:00
commit e42e873456
41 changed files with 21766 additions and 0 deletions

30
Makefile Normal file
View File

@ -0,0 +1,30 @@
all: build_and_run
build_and_run:
gcc sudoku.c -o sudoku -Wall -Wextra
./sudoku
gen1:
gcc sudoku_gen1.c -o sudoku_gen1 -Wall -Wextra -Ofast
./sudoku_gen1
solve: solve.c rlib.h
gcc solve.c -Ofast -o solve
$(MAKE) solve_auto
solve_manual:
./solve
solve_auto:
./solve auto
coverage:
gcc -pg -fprofile-arcs -ftest-coverage -g -o sudoku sudoku2.c
./sudoku
lcov --capture --directory . --output-file sudoku.coverage.info
genhtml sudoku.coverage.info --output-directory sudoku.coverage
@rm -f *.gcda 2>/dev/null
@rm -f *.gcno 2>/dev/null
@rm -f sudoku.coverage.info 2>/dev/null
@rm -f gmon.out 2>/dev/null
google-chrome sudoku.coverage/index.html

41
NOTES.md Normal file
View File

@ -0,0 +1,41 @@
The difficulty of a Sudoku puzzle is determined by several factors beyond just how many numbers are initially provided. While a puzzle with fewer clues tends to be harder, the complexity really comes down to the logical strategies required to solve it. Here are the main factors that make a Sudoku puzzle challenging:
1. Required Advanced Solving Techniques
Simple puzzles can be solved with basic methods like eliminating possibilities and filling in cells where only one number can fit. However, harder puzzles require more advanced strategies, such as:
Naked pairs/triples: A situation where two (or three) cells in a row, column, or block can only contain the same two (or three) possible numbers, allowing elimination from other cells.
Hidden pairs/triples: Two (or three) numbers that are the only candidates for two (or three) cells in a region, even if there are other candidates in those cells.
X-Wing, Swordfish, and more: These are pattern-based techniques involving the placement of candidates across rows and columns, which allow for the elimination of possibilities in other areas.
Chains and loops: Advanced methods like “coloring” or "forcing chains," which involve following chains of numbers across multiple rows, columns, and grids to find contradictions or certainties.
The need for these more advanced techniques often separates easy puzzles from hard ones.
2. Clue Distribution
Its not just how many numbers are given, but where those numbers are placed. If the clues are evenly distributed across the grid, the puzzle tends to be easier because it gives solvers multiple ways to start filling in numbers. In contrast, if clues are clustered in one area or sparse in key rows/columns, it creates a bottleneck, forcing solvers to work through more complex deductions to make progress.
3. Minimal Clues
A valid Sudoku puzzle has at least 17 clues, as puzzles with fewer than 17 numbers do not necessarily have a unique solution. Puzzles that approach this minimal clue count are generally more challenging because fewer numbers are available to help start solving, requiring deeper reasoning early in the process.
4. Interaction Between Regions
In easier puzzles, the clues in one part of the grid can directly help solve other parts of the grid, creating a cascading effect that makes the solution more straightforward. Harder puzzles often present isolated regions where the interaction between rows, columns, and blocks is less obvious. This forces the solver to use cross-region techniques and more sophisticated logic to make progress.
5. Symmetry
Many harder Sudoku puzzles exhibit symmetry, meaning the pattern of given clues looks the same if the grid is rotated or reflected. Symmetrical puzzles tend to be more challenging because their structure limits the number of easy initial steps, forcing solvers to employ more advanced strategies right from the start.
6. Depth of Deduction
The deeper the reasoning chains a solver has to follow to fill in a cell, the harder the puzzle. For instance:
In easy puzzles, you might be able to place a number by simple elimination or scanning a row or column.
In harder puzzles, you might need to explore multiple layers of possibilities, keeping track of candidate numbers in a larger set of cells and deducing how they affect each other.
7. Uniqueness of Solutions
A puzzle where its easier to guess numbers (and still find a solution) tends to be easier. Puzzles that are hard require logical deduction at every step and leave no room for guessing. Hard puzzles often include configurations where making an incorrect assumption leads to contradictions much later in the solving process, forcing you to backtrack.
8. Human Solvability vs. Computer Solvability
Some puzzles may be solvable by a computer using brute force or trial and error, but for humans, they are hard because they require advanced logic at multiple points without obvious pathways to start. Human-solvable hard puzzles require specific advanced techniques that aren't immediately apparent.
Summary of Factors:
Need for advanced techniques like X-Wing, Swordfish, or chains.
Sparse or unhelpful clue distribution (even with many clues).
Few starting clues (e.g., minimal 17-clue puzzles).
Symmetrical and isolated regions that limit early progress.
Complex deduction depth, requiring multiple steps to fill a single cell.
A combination of these factors contributes to making a Sudoku puzzle truly difficult.

66
README.md Normal file
View File

@ -0,0 +1,66 @@
# Sudoku
## Sudoku solver
NOTE: not all puzzles in source are valid!
## Sudoku generator
This one is resolvable in 227 attempts:
```
{9, 0, 0, 0, 0, 0, 0, 0, 6},
{0, 8, 0, 0, 0, 0, 0, 5, 0},
{0, 0, 7, 0, 0, 0, 4, 0, 0},
{0, 0, 0, 6, 0, 3, 0, 0, 0},
{7, 6, 4, 0, 5, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 4, 0, 0, 0},
{0, 1, 6, 0, 0, 0, 3, 0, 0},
{0, 7, 0, 0, 0, 0, 0, 2, 0},
{8, 0, 0, 0, 0, 0, 0, 0, 1}
```
// 227 attempts
```
{9, 0, 0, 0, 0, 0, 0, 0, 6},
{0, 8, 0, 0, 0, 0, 0, 5, 0},
{0, 0, 7, 0, 0, 0, 4, 0, 0},
{0, 0, 0, 6, 0, 3, 0, 0, 0},
{7, 6, 4, 0, 5, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 4, 0, 0, 0},
{0, 1, 6, 0, 0, 0, 3, 0, 0},
{0, 7, 0, 0, 0, 0, 0, 2, 0},
{8, 3, 0, 0, 0, 0, 0, 0, 1}
```
// 320220 attempts
```
Generation: 18/18
155.34s
Solution: 220388352
7 2 1 3 4 5 6 8 9
4 6 8 1 9 7 2 5 3
3 9 5 2 8 6 1 7 4
2 5 3 4 6 9 7 1 8
1 7 6 8 2 3 4 9 5
9 8 4 5 7 1 3 2 6
6 3 9 7 5 2 8 4 1
5 4 7 6 1 8 9 3 2
8 1 2 9 3 4 5 6 7
```
// WRONG
int grid[N][N] = {
{9, 0, 0, 0, 0, 0, 0, 0, 6},
{0, 8, 0, 0, 0, 0, 0, 5, 0},
{0, 0, 7, 0, 0, 0, 4, 0, 0},
{0, 0, 0, 6, 0, 3, 0, 0, 0},
{0, 0, 0, 0, 5, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 4, 0, 0, 0},
{0, 0, 6, 0, 0, 0, 3, 0, 0},
{0, 7, 0, 0, 0, 0, 0, 2, 0},
{8, 0, 0, 0, 0, 0, 0, 0, 2}
};
602.85s
Attempts:
1663211698
No solution exists

BIN
a.out Executable file

Binary file not shown.

270
app.js Normal file
View File

@ -0,0 +1,270 @@
class Grid2 {
columns = []
data = []
constructor(data){
this.data = data
if(this.data.length){
this.columns = Object.keys(this.data[0]);
}
this.columns = ['puzzle'];
}
createRow(record){
const el = document.createElement('div');
el.classList.add('row');
this.columns.forEach((column)=>{
const col = document.createElement('pre');
col.classList.add('col');
col.textContent = record[column];
el.appendChild(col);
})
return el;
}
render(el){
this.data.forEach((record)=>{
const row = this.createRow(record);
el.appendChild(row);
})
}
}
class Grid {
constructor(data){
this.cols = 5
this.data = data ? data : []
this.sort = data.sort
this.styleElement = null
}
get(index){
return this.data[index] ? this.data[index] : null
}
insert(record){
this.data.push(record)
}
renderStyle(container){
const el = document.createElement('style');
el.textContent = `
.rgrid {
font-size:16px;
display: 'grid';
grid-template-columns: repeat(${this.cols}, 1fr);
grid-template-rows: auto;
gap: 0px;
border-radius: 5px;
clear: both;
color: #efefef;
/*aspect-ratio: 1/1;*/
}
.rgrid-item {
width:relative;
padding: 10px;
/*aspect-ratio: 1/1*/;
text-align: left;
float: left;
background-color: #000;
}
.rgrid->pre {
width: 100%;
}
`
container.appendChild(el)
return el
}
render(container){
if(this.styleElement == null){
this.styleElement = this.renderStyle(container)
}
const el = document.createElement('div')
el.classList.add('rgrid');
this.data.forEach((record)=>{
const cell = this.createCell(record);
el.appendChild(cell);
})
for(let i =0; i< container.children.length; i++){
if(container.children[i] != this.styleElement){
container.children[i].remove()
};
}
container.appendChild(el)
return el
}
createCell(record){
const el = document.createElement('div');
el.classList.add('rgrid-item');
const pre = this.createPre(record);
el.appendChild(pre);
// el.textContent = record;
return el;
}
createPre(content){
const el = document.createElement('pre');
el.textContent = content;
return el;
}
}
const getPuzzleNode = (str, readOnly, size, darkMode) => {
darkMode = true
const sudoku = document.createElement('my-sudoku')
sudoku.setAttribute('puzzle', str ? str : 'generate')
if(readOnly)
sudoku.setAttribute('read-only', 'true')
sudoku.classList.add('sudoku')
if(darkMode){
sudoku.classList.add('sudoku-dark')
}
return sudoku
}
const renderPuzzleFromString = (str, readOnly, size, darkMode) => {
darkMode = true
const sudoku = document.createElement('my-sudoku')
sudoku.setAttribute('puzzle', str ? str : 'generate')
if(readOnly)
sudoku.setAttribute('read-only', 'true')
sudoku.classList.add('sudoku')
if(darkMode){
sudoku.classList.add('sudoku-dark')
}
document.body.appendChild(sudoku);
const id = generateIdByPosition(sudoku);
sudoku.id = id
return sudoku
}
class App {
container = null
constructor(){
}
async renderProcessList(container){
const processes = await this.getProcessList();
// processes.forEach(process=>{
// renderPuzzleFromString(process.puzzle,false,13,true)
// })
const grid = new Grid(processes.map(process=>{
return [process.puzzle ,
"duration: " +process.duration.toString(),
"initial: "+process.result_initial_count.toString(),
"steps: "," "+process.steps.toString(),
"steps total: "," "+process.steps_total.toString(),
"solved:" + process.solved_count.toString()
].join('\n')
}));
grid.render(container);
}
async renderStatistics(container){
const statistics = await this.statistics;
const column_one = `longest_running: ${statistics.longest_running}
solved_total: ${statistics.solved_total}
start: ${statistics.start}
steps_per_puzzle: ${statistics.steps_per_puzzle}
steps_total: ${statistics.steps_total}
time_winner: ${statistics.time_winner}`
const column_two = statistics.puzzle_winner
const column_three = statistics.solution_winner
const data = [column_one,column_two,column_three]
const grid = new Grid(data)
grid.render(container)
}
get statistics() {
return fetch('/json/statistics/').then(response=>{
return response.json();
}).then(data=>{
console.debug(data)
return data
})
}
async getProcessList() {
return fetch('/json/processes')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
console.log(data);
return data;
})
.catch(error => {
console.error('There was a problem with the fetch operation:', error);
});
}
}
const asyncAnimationFrame = async(callback)=>{
return new Promise((resolve)=>{
requestAnimationFrame(()=>{
resolve(callback());
})
})
}
let app;
document.addEventListener('DOMContentLoaded',(e)=>{
app = new App();
const refreshProcesList = async() =>{
setInterval(async()=>{
await asyncAnimationFrame(async ()=>{
const processes = await app.getProcessList();
newNodes = processes.map(process=>{
return getPuzzleNode(process.puzzle,false,13,true)
})
let hasChildren = document.body.childNodes[0].length ? true : false
let count = 0;
newNodes.forEach(node=>{
document.body.appendChild(node)
count++;
})
if(hasChildren)
for(let i = 0; i < count; i++){
document.body.childNodes[0].remove()
}
})
},1000);
}
//refreshProcesList();
setInterval(async()=>{
const el = document.getElementById('statistics');
asyncAnimationFrame(async ()=>{
await app.renderStatistics(el);
});
},200);
setInterval(async ()=>{
const el = document.getElementById('details')
asyncAnimationFrame(async ()=>{
await app.renderProcessList(el);
})
},200);
console.info("KAAR\n");
})

100
box.c Normal file
View File

@ -0,0 +1,100 @@
#include "sudoku.h"
static char * gridchars = "abcdefghijklmnopqrstuvwxyz";
int * get_box(char c){
unsigned static int result[N];
unsigned int index = 0;
unsigned int offset = strchr(gridchars,c) - gridchars;
offset = offset * N / 3;
for(unsigned int i = offset; i < offset + N/3;i++){
result[index] = i;
index++;
}
for(unsigned int i = offset + N; i < offset + N + N/3;i++){
result[index] = i;
index++;
}
for(unsigned int i = offset + N + N; i < offset + N + N + N/3;i++){
result[index] = i;
index++;
}
return result;
}
int * grid_to_array(int grid[N][N]){
static int result[N*N];
int index = 0;
for(uint row = 0; row < N; row++){
for(uint col = 0; col < N; col++){
result[index] = grid[row][col];
index++;
}
}
return result;
}
char get_box_char(int row, int col){
unsigned int index = 0;
unsigned int cell_index = row * N + col;
int char_index = cell_index / N;
while(true){
char c = gridchars[index];
int * numbers = get_box(c);
for(int i = 0; i < N; i++){
if(numbers[i] == cell_index){
return c;
}
}
index++;
}
return 0;
}
int * get_box_values(int grid[N][N],char c){
int * box = get_box(c);
static unsigned int values[N];
int * array = grid_to_array(grid);
//printf("HIERR\n");
for(unsigned int i = 0; i < N; i++){
printf("FROM ARRAY: %d\n", box[i]);
values[i] = array[box[i]];
}
return values;
}
void print_box(int * box){
unsigned int cols = 0;
for(int i = 0; i < N; i++){
printf("%d", box[i]);
cols++;
if(cols == N / 3){
printf("\n");
cols = 0;
}
}
}
int main(){
int grid[N][N]; // = grid_new();
memset(grid,0,N*N*sizeof(int));
int * box = get_box('d');
for(int i = 0; i < N; i++){
printf("%d\n",box[i]);
}
grid[4][2] = 9;
int * box_values = get_box_values(grid,'d');
print_box(box_values);
int row = 4;
int col = 0;
grid[row][col] = 8;
print_grid(grid,false);
printf("%c",get_box_char(row,col));
return 0;
}

99
footer.h Normal file
View File

@ -0,0 +1,99 @@
#ifndef FOOTER_H
#define FOOTER_H
#include <stdio.h>
#include <unistd.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <rlib.h>
#include <pthread.h>
#include <stdarg.h>
static pthread_mutex_t footer_mutex;
static nsecs_t footer_time_start = 0;
char footer_prefix[1024] = {0};
char footer_suffix[1024] = {0};
static void footer_get_terminal_size(int *rows, int *cols) {
struct winsize w;
// Use ioctl to get the terminal size
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) == -1) {
perror("ioctl");
} else {
*rows = w.ws_row;
*cols = w.ws_col;
}
}
void footer_get_cursor_position(int *rows, int *cols) {
// Save the current terminal settings
struct termios term, term_saved;
tcgetattr(STDIN_FILENO, &term);
term_saved = term;
// Put terminal in raw mode (disable canonical input and echoing)
term.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &term);
// Request cursor position with ANSI code: ESC [ 6 n
printf("\033[6n");
fflush(stdout);
// Read the response: ESC [ rows ; cols R
char buf[32];
long unsigned int i = 0;
char c = 0;
while (c != 'R' && i < sizeof(buf) - 1) {
__attribute_maybe_unused__ ssize_t bytes_read = read(STDIN_FILENO, &c, 1);
buf[i++] = c;
}
buf[i] = '\0';
// Parse the response
if (sscanf(buf, "\033[%d;%dR", rows, cols) != 2) {
*rows = *cols = -1; // In case of parsing error
}
// Restore the original terminal settings
tcsetattr(STDIN_FILENO, TCSANOW, &term_saved);
}
void _footer_set(unsigned int row, unsigned int col_count, char * text,unsigned int original_row,unsigned int original_col) {
char content[2048] = {0};
char line[1024];
memset(line,' ',sizeof(line) - 1);
char cursor_set_string[2048] = {0};
sprintf(cursor_set_string,"\e[%d;%dH",row,0);
sprintf(content,"%s%s%s\033[%u;%uH",cursor_set_string,text,line,original_row,original_col);
content[col_count + strlen(cursor_set_string) + 1] = 0;
printf(content);
}
void footer_printf(const char *format, ...) {
if(footer_time_start == 0)
{
// Quick, for the threads come a default value
footer_time_start = 1;
pthread_mutex_init(&footer_mutex,NULL);
footer_time_start = nsecs();
}
pthread_mutex_lock(&footer_mutex);
nsecs_t time_elapsed = nsecs() - footer_time_start;
int original_row = 0;
int original_col = 0;
int row_count = 0;
int col_count = 0;
footer_get_cursor_position(&original_row, &original_col);
footer_get_terminal_size(&row_count, &col_count);
char text[1024] = {0};
char full_text[4096] = {0};
va_list args;
va_start(args, format);
vsprintf(text, format, args);
va_end(args);
sprintf(full_text,"\r%s - %s%s%s", format_time(time_elapsed), footer_prefix, text, footer_suffix);
_footer_set(row_count,col_count,full_text,original_row,original_col);
pthread_mutex_unlock(&footer_mutex);
}
#endif

56
index.html Normal file
View File

@ -0,0 +1,56 @@
<html>
<head>
<style type="text/css">
.acol {
// width: 10%;
/*float:left;*/
padding: 5px;
display: inline-block;
}
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: black; /*#f0f0f0;*/
}
.row {
background-color: #4CAF50; /* Color for the grid items */
color: white;
padding: 10px;
text-align: center;
text-justify: inter-word;
font-size: 0.9em;
border-radius: 10px; /* Rounded corners */
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); /* Add shadow */
}
.grid-container {
display: grid; /* Enable grid layout */
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); /* Responsive columns */
gap: 20px; /* Spacing between grid items */
padding: 20px;
width: 90%; /* Adjust the width of the container */
}
.container {
width: 100%;
height: 100%;
}
</style>
<script type="text/javascript" src="app.js">
const app = new App();
</script>
</head>
<body class="grid-container">
Loading...
</body>
</html>

8450
rlib.h Normal file

File diff suppressed because it is too large Load Diff

282
rsolve.h Normal file
View File

@ -0,0 +1,282 @@
#ifndef RSOLVE_H
#define RSOLVE_H
#ifndef RSOLVE_SIZE
#define RSOLVE_SIZE 9
#endif
#include "rlib.h"
#include <stdbool.h>
#include <stdlib.h>
typedef unsigned long ulong;
unsigned int rand_int(int min, int max){
return rand() % (max - min + 1) + min;
}
int * example_grid(int identifier){
while(identifier == 4 || identifier == 2)
identifier = rand_int(0,9);
static int grid[RSOLVE_SIZE][RSOLVE_SIZE] = {
{4, 2, 0, 0, 0, 1, 0, 0, 0},
{0, 0, 1, 0, 0, 0, 9, 0, 0},
{0, 0, 0, 3, 0, 0, 0, 8, 0},
{0, 0, 0, 0, 3, 0, 0, 0, 4},
{0, 0, 0, 0, 0, 7, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 4, 2, 0}
};
grid[RSOLVE_SIZE-1][0] = identifier;
return (int *)grid;
}
typedef struct rdigitsum_t {
int count;
int index;
} rdigitsum_t;
int rdigitcmp(const void *a, const void *b) {
rdigitsum_t *as = (rdigitsum_t*)a;
rdigitsum_t *bs = (rdigitsum_t*)b;
return bs->count - as->count;
}
double count_neighbors(int grid[RSOLVE_SIZE][RSOLVE_SIZE], int row, int col) {
double count = 0.0;
for(int i = 0; i < row; i++){
for(int j = 0; j < RSOLVE_SIZE; j++){
if(grid[row][j] != 0 && j != col)
count += 1; //grid[row][j].initial ? 1.1 : 1.0;
}
for(int j = 0; j < RSOLVE_SIZE; j++){
if(grid[j][col] != 0 && j != row)
count += 1; //grid[j][col].initial ? 1.1 : 1.0;
}
}
return count;
}
bool get_easiest_cell2(int grid[RSOLVE_SIZE][RSOLVE_SIZE], unsigned int *easy_row, unsigned int *easy_col){
double highest_neighbor_count = 0;
bool found = false;
for(int row = 0; row < RSOLVE_SIZE; row++){
{
for(int col = 0; col < RSOLVE_SIZE; col++){
double neighbor_count = count_neighbors(grid,row,col);
if(neighbor_count > highest_neighbor_count){
highest_neighbor_count = neighbor_count;
*easy_row = row;
*easy_col = col;
found = true;
}
}
}
}
return found;
}
int * rdigitsum(int grid[RSOLVE_SIZE][RSOLVE_SIZE]){
rdigitsum_t digit_sums[10];
static int sum[10];
for(size_t i = 0; i < sizeof(digit_sums) / sizeof(digit_sums[0]); i++){
digit_sums[i].count = 0;
digit_sums[i].index = i;
}
for (uint row = 0; row < RSOLVE_SIZE; row++){
for(uint col = 0; col < RSOLVE_SIZE; col++){
digit_sums[grid[row][col]].count++;
}
}
qsort(digit_sums, sizeof(digit_sums) / sizeof(digit_sums[0]), sizeof(digit_sums[0]), rdigitcmp);
for(size_t i = 0; i < sizeof(digit_sums) / sizeof(digit_sums[0]); i++){
sum[i] = digit_sums[i].index;
}
return sum;
}
int rsolve_check(int grid[RSOLVE_SIZE][RSOLVE_SIZE], int row, int col, int num)
{
if (num == 0)
return true;
for (int x = 0; x < RSOLVE_SIZE; x++)
{
if (grid[row][x] == num && col != x)
{
return false;
}
}
for(int x = 0; x < RSOLVE_SIZE; x++){
if (grid[x][col] == num && row != x)
{
return false;
}
}
// Check box
int startRow = row - row % (RSOLVE_SIZE / 3), startCol = col - col % (RSOLVE_SIZE / 3);
for (int i = 0; i < RSOLVE_SIZE / 3; i++)
{
for (int j = 0; j < RSOLVE_SIZE / 3; j++)
{
if (grid[i + startRow][j + startCol] == num && row != i + startRow && col != j + startCol)
{
return false;
}
}
}
return true;
}
bool get_easiest_cell(int grid[RSOLVE_SIZE][RSOLVE_SIZE], unsigned int * easy_row, unsigned int * easy_col){
double easy_neighbor_count = 0;
bool found = true;
for(int row = 0; row < RSOLVE_SIZE; row++){
for (int col = 0; col < RSOLVE_SIZE; col++){
if(grid[row][col] != 0){
continue;
}
double ncount = count_neighbors(grid,row,col);
if(easy_neighbor_count <= ncount){
easy_neighbor_count = ncount;
*easy_row = row;
*easy_col = col;
found = true;
return found;
}
}
}
return false;
}
unsigned int rsolve(int grid[RSOLVE_SIZE][RSOLVE_SIZE], unsigned long long *attempts){
(*attempts)++;
// if(*attempts % 10000000 == 0)
// print_grid(grid);
unsigned int row, col;
if(!get_easiest_cell(grid,&row,&col)){
return *attempts;
}
int * counts = rdigitsum(grid);
for(int num = 0; num < RSOLVE_SIZE + 1; num++){
int fieldNum = counts[num];
if(fieldNum == 0)
continue;
if(rsolve_check(grid,row,col,fieldNum)){
grid[row][col] = fieldNum;
//print_grid(grid,true);
if(rsolve(grid,attempts))
{
return *attempts;
}
grid[row][col] = 0;
}
}
return 0;
}
int ris_safe(int grid[RSOLVE_SIZE][RSOLVE_SIZE], int row, int col, int num)
{
if (num == 0)
return true;
for (int x = 0; x < RSOLVE_SIZE; x++)
{
if (grid[row][x] == num && col != x)
{
return false;
}
}
for (int x = 0; x < RSOLVE_SIZE; x++)
{
if (grid[x][col] == num && row != x)
{
return false;
}
}
// Check box
int startRow = row - row % (RSOLVE_SIZE / 3), startCol = col - col % (RSOLVE_SIZE / 3);
for (int i = 0; i < RSOLVE_SIZE / 3; i++)
{
for (int j = 0; j < RSOLVE_SIZE / 3; j++)
{
if (grid[i + startRow][j + startCol] == num && row != i + startRow && col != j + startCol)
{
return false;
}
}
}
return true;
}
void grid_set_random_free_cell(int grid[RSOLVE_SIZE][RSOLVE_SIZE]){
int rn, row, col;
while(true){
rn = (rand() % RSOLVE_SIZE) + 1;
row = rand() % RSOLVE_SIZE;
col = rand() % RSOLVE_SIZE;
while(grid[row][col] != 0){
row = rand() % RSOLVE_SIZE;
col = rand() % RSOLVE_SIZE;
}
// printf("CHECK %d:%d:%d\RSOLVE_SIZE",row,col,rn);
if(ris_safe(grid,row,col, rn)){
// printf("CHECKED\RSOLVE_SIZE");
grid[row][col] = rn;
break;
}
}
}
void grid_set_random_free_cells(int * grid, unsigned int count){
//grid[rand() % RSOLVE_SIZE][rand() % RSOLVE_SIZE] = rand() % RSOLVE_SIZE;
for (uint i =0 ; i < count; i++){
grid_set_random_free_cell(grid);
}
}
bool rvalidate_grid(int grid[RSOLVE_SIZE][RSOLVE_SIZE])
{
for (int row = 0; row < RSOLVE_SIZE; row++)
{
for (int col = 0; col < RSOLVE_SIZE; col++)
{
if (!ris_safe(grid, row, col, grid[row][col]))
{
return false;
}
}
}
return true;
}
int * rgenerate_puzzle(unsigned int count){
int * grid = (int *)malloc(sizeof(int)*RSOLVE_SIZE*RSOLVE_SIZE);
while(true){
memset(grid,0,RSOLVE_SIZE*RSOLVE_SIZE*sizeof(int));
grid_set_random_free_cells(grid, count);
//print_grid(grid,true);
if(rvalidate_grid(grid))
return grid;
}
return NULL;
}
#endif

BIN
solve Executable file

Binary file not shown.

277
solve.c Normal file
View File

@ -0,0 +1,277 @@
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <rlib.h>
#include "rsolve.h"
#define N 9
// VERSION 1
void clear()
{
printf("\e[2J");
fflush(stdout);
}
void print_grid(int grid[N][N])
{
for (int row = 0; row < N; row++)
{
for (int col = 0; col < N; col++)
{
printf("%d ", grid[row][col]);
}
printf("\n");
}
}
unsigned int solve2(int grid[N][N], unsigned long long *attempts)
{
(*attempts)++;
// if(*attempts % 10000000 == 0)
// print_grid(grid);
unsigned int row, col;
bool emptySpot = false;
for (row = 0; row < N; row++)
{
for (col = 0; col < N; col++)
{
if (grid[row][col] == 0)
{
emptySpot = true;
break;
}
}
if (emptySpot)
{
break;
}
}
if (!emptySpot)
return true;
for (int num = 1; num <= 9; num++)
{
if (ris_safe(grid, row, col, num))
{
grid[row][col] = num;
if (solve2(grid, attempts))
{
return *attempts;
}
grid[row][col] = 0;
}
}
return 0;
}
int solve(int grid[N][N], unsigned long long *steps)
{
*(steps)++;
// if(*steps % 1000000){
// printf("%lld\n",*steps);
// }
int col;
int row;
bool found_empty = false;
for (row = 0; row < N; row++)
{
for (col = 0; col < N; col++)
{
if (!grid[row][col])
{
found_empty = true;
}
}
if (found_empty)
break;
}
if (!found_empty)
return true;
for (int i = 1; i < 10; i++)
{
if (ris_safe(grid, row, col, i))
{
grid[row][col] = i;
if (solve(grid, steps))
{
print_grid(grid);
return true;
}
grid[row][col] = 0;
}
}
return false;
}
int get_next_int(FILE *f)
{
char c;
while ((c = getc(f)) && c != EOF)
{
if (c < '0' || c > '9')
{
continue;
}
return c - '0';
}
return -1;
}
bool grid_read(int grid[N][N])
{
for (int row = 0; row < N; row++)
{
for (int col = 0; col < N; col++)
{
grid[row][col] = get_next_int(stdin);
clear();
print_grid(grid);
if (grid[row][col] == -1)
{
return false;
}
}
}
return true;
} /*
1 2 3 4 5 6 9 7 8
4 5 6 7 8 9 1 2 3
7 8 9 3 2 1 6 4 5
9 4 7 8 3 2 5 1 6
2 6 8 9 1 5 7 3 4
5 3 1 6 7 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 */
/*
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 9
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 2 0 0 0 0 0 0
0 0 0 0 0 0 0 2 0
1 0 0 3 0 7 0 0 0
*/
bool is_empty(int grid[N][N])
{
for (int i = 0; i < N * N; i++)
{
if (grid[i / 9][i % 9] != 0)
return false;
}
return true;
}
/* 27 secs with rsolve. Good test:
2 0 0 0 0 8 0 0 0
0 0 0 0 0 0 0 9 0
0 0 8 0 2 0 0 0 5
0 0 0 0 0 7 0 0 0
0 4 0 0 0 0 9 0 0
0 0 0 3 0 5 0 0 0
0 2 0 0 0 1 0 0 0
0 9 0 0 0 0 8 0 0
0 8 0 0 0 2 0 0 0
*/
/*
4:16
7 0 0 0 0 4 0 0 6
0 0 3 0 0 0 0 0 0
0 0 0 0 0 0 0 0 4
4 0 0 0 6 0 0 5 0
0 0 0 0 4 0 1 0 0
0 8 0 0 0 0 0 0 9
0 0 0 0 5 0 0 4 0
0 0 0 0 2 0 0 6 0
0 0 0 7 0 0 0 0 0
*/
int main(int argc, char *argv[])
{
printf("Usage: ./solve [auto (optional)]\n");
printf(" run without parameters to provide your own grid. Many formats supported, XML, HTML, JSON, NO DELIMITER, DELMITED BY ANYTHING.\n");
RBENCH(1000000,{
int * g = rgenerate_puzzle(17);
free(g);
})
srand(time(NULL));
setbuf(stdout, 0);
int grid[N][N];
memset(grid, 0, N * N * sizeof(int));
clear();
int * ex_grid = rgenerate_puzzle(17);
printf("Paste grid in this format:\n");
print_grid(ex_grid);
// grid[8][8] = 9;
// grid[8][8] = 8;
while (argc != 2)
{
if (is_empty(grid))
while (!grid_read(grid))
{
printf("Invalid grid\n");
}
if (rvalidate_grid(grid))
{
break;
}
printf("Corrupt grid..\n");
memset(grid, 0, N * N * sizeof(int));
}
clear();
int grid_original[N][N];
if(argc == 2){
memcpy(grid, ex_grid, N * N * sizeof(int));
}
memcpy(grid_original, grid, N * N * sizeof(int));
printf("Grid valid. Solving...\n\n");
unsigned long long steps = 0;
nsecs_t start = nsecs();
RBENCH(1, {
memcpy(grid, grid_original, N * N * sizeof(int));
steps = 0;
if (rsolve(grid, &steps))
{
printf("original:\n");
print_grid(grid_original);
printf("\ncompleted:\n");
print_grid(grid);
printf("\n");
}
});
printf("Steps: %u\n", steps);;
RBENCH(1, {
memcpy(grid, grid_original, N * N * sizeof(int));
steps = 0;
if (solve2(grid, &steps)){
printf("%s","2");
}
memcpy(grid, grid_original, N * N * sizeof(int));
});
printf("Steps: %u\n", steps);;
nsecs_t end = nsecs();
printf("Duration: %s\n", format_time(end - start));
return 0;
}

BIN
sudoku Executable file

Binary file not shown.

333
sudoku.c Normal file
View File

@ -0,0 +1,333 @@
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#define PUZZLE_ROWS 9
#define PUZZLE_COLS 9
#define PUZZLE_SIZE PUZZLE_ROWS*PUZZLE_COLS
#define ANSII_BLUE_BG "\033[44m"
#define ANSII_RED_BG "\033[41m"
#define ANSII_GREEN_BG "\033[42m"
#define ANSII_CLEAR "\033[0m"
typedef struct sudoku_cell_t
{
char c;
bool predefined;
bool sure;
bool empty;
} sudoku_cell_t;
sudoku_cell_t *get_cell(sudoku_cell_t *puzzle, unsigned int row, unsigned int column)
{
sudoku_cell_t *cell = &puzzle[row * PUZZLE_COLS + column];
return cell;
}
void set_cell(sudoku_cell_t *puzzle, unsigned int row, unsigned int column, bool predefined, bool sure, char c)
{
sudoku_cell_t *cell = get_cell(puzzle, row, column);
cell->predefined = predefined;
cell->sure = predefined ? true : sure;
cell->c = c;
cell->empty = c == 0;
}
void set_cellc(sudoku_cell_t *puzzle, char column, unsigned int row, bool predefined, bool sure, char c)
{
unsigned int irow = row - 1;
unsigned int icol = column - 65;
set_cell(puzzle, irow, icol, predefined, sure, c);
}
void set_predefinedc(sudoku_cell_t *puzzle, char row, unsigned int column, char c)
{
set_cellc(puzzle, row, column, true, true, c);
}
void set_sure(sudoku_cell_t *puzzle, char row, unsigned int column, char c)
{
set_cell(puzzle, row, column, false, true, c);
}
void set_guess(sudoku_cell_t *puzzle, char row, unsigned int column, char c)
{
set_cell(puzzle, row, column, false, false, c);
}
void set_guessc(sudoku_cell_t *puzzle, char row, unsigned int column, char c)
{
set_cellc(puzzle, row, column, false, false, c);
}
void set_zero(sudoku_cell_t *puzzle, char row, unsigned int column)
{
set_cellc(puzzle, row, column, false, false, 0);
}
void init_puzzle(sudoku_cell_t *puzzle, unsigned int rows, unsigned int cols)
{
for (unsigned int i = 0; i < rows * cols; i++)
{
puzzle[i].c = 0;
puzzle[i].empty = true;
puzzle[i].predefined = false;
puzzle[i].sure = true;
}
}
void draw_cell(sudoku_cell_t *cell)
{
if (cell->predefined)
{
printf("%s", ANSII_BLUE_BG);
}
else if (cell->empty)
{
printf("%s", ANSII_CLEAR);
}
else if (cell->sure)
{
printf("%s", ANSII_GREEN_BG);
}
else if (!cell->empty)
{
printf("%s", ANSII_RED_BG);
}
printf(" %c ", cell->c == 0 ? ' ' : cell->c);
printf("%s", ANSII_CLEAR);
}
void draw_puzzle(sudoku_cell_t *puzzle, unsigned int rows, unsigned int cols)
{
for (unsigned int i = 0; i < cols; i++)
{
for (unsigned int j = 0; j < rows; j++)
{
sudoku_cell_t *cell = &puzzle[i * cols + j];
draw_cell(cell);
}
printf("\n");
}
}
sudoku_cell_t *get_col(sudoku_cell_t *puzzle, unsigned int rows, unsigned int col)
{
static sudoku_cell_t box[4096];
for (unsigned int i = 0; i < rows; i++)
{
box[i] = *get_cell(puzzle, i, col);
}
return box;
}
sudoku_cell_t *get_row(sudoku_cell_t *puzzle, unsigned int cols, unsigned int row)
{
static sudoku_cell_t box[4096];
for (unsigned int i = 0; i < cols; i++)
{
box[i] = *get_cell(puzzle, row, i);
}
return box;
}
sudoku_cell_t *get_box(sudoku_cell_t *puzzle, unsigned int rows, unsigned int cols, unsigned int row, unsigned int col)
{
unsigned int col_box_column = col / 3;
unsigned int row_box_row = row / 3;
static sudoku_cell_t box[4096];
unsigned int counter = 0;
for (unsigned int i = row_box_row * (rows / 3); i < (row_box_row * rows / 3) + (rows / 3); i++)
{
for (unsigned int j = col_box_column * (cols / 3); j < (col_box_column * cols / 3) + (cols / 3); j++)
{
box[counter] = *get_cell(puzzle, i, j);
counter++;
}
}
return box;
}
unsigned int *get_options(sudoku_cell_t *puzzle, unsigned int rows, unsigned int cols, unsigned int row, unsigned int col)
{
static unsigned int options[4096];
memset(options, 0, sizeof(options));
for (unsigned int i = 0; i < cols; i++)
{
options[i] = true;
}
for (unsigned int i = 0; i < cols; i++)
{
sudoku_cell_t *c = get_col(puzzle, rows, col);
for (unsigned int j = 0; j < cols; j++)
{
if (!c[j].empty)
options[c[i].c - '0' - 1] = false;
}
c = get_row(puzzle, cols, row);
for (unsigned int j = 0; j < cols; j++)
{
if (!c[j].empty)
options[c[j].c - '0' - 1] = false;
}
c = get_box(puzzle, rows, cols, row, col);
for (unsigned int j = 0; j < cols * cols; j++)
{
if (!c[j].empty)
options[c[j].c - '0' - 1] = false;
}
}
return options;
}
unsigned int get_option(unsigned int *options, unsigned int offset)
{
unsigned int result = 0;
unsigned int current_offset = 0;
for (unsigned int i = 0; i < PUZZLE_COLS; i++)
{
if (options[i])
{
if (current_offset == offset)
{
return i;
}
offset++;
}
}
return result;
}
unsigned int get_options_count(unsigned int *options)
{
unsigned int result = 0;
for (unsigned int i = 0; i < PUZZLE_COLS; i++)
{
if (options[i])
{
result++;
}
}
return result;
}
unsigned int count_empty(sudoku_cell_t * puzzle, unsigned int size){
unsigned int count = 0;
for(unsigned int i = 0; i < size;i++){
if(puzzle[i].empty){
count++;
}
}
return count;
}
unsigned int set_sures(sudoku_cell_t *puzzle, unsigned int rows, unsigned int cols, unsigned int accuracy)
{
for (unsigned int i = 0; i < rows; i++)
{
for (unsigned int j = 0; j < cols; j++)
{
sudoku_cell_t *cell = get_cell(puzzle, i, j);
unsigned int *options = get_options(puzzle, rows, cols, i, j);
if (cell->empty && get_options_count(options) == accuracy)
{
unsigned int option = get_option(options, 0);
if (accuracy > 1)
{
set_guess(puzzle, i, j, option + '0' + 1);
}
else
{
set_sure(puzzle, i, j, option + '0' + 1);
}
set_sures(puzzle, rows, cols, accuracy);
return count_empty(puzzle,rows*cols);
}
}
}
return count_empty(puzzle,rows*cols);
}
unsigned int solve(char *charpuzzle)
{
sudoku_cell_t puzzle[PUZZLE_SIZE];
init_puzzle(puzzle, PUZZLE_ROWS, PUZZLE_COLS);
for (unsigned int i = 0; i < PUZZLE_ROWS; i++)
{
for (unsigned int j = 0; j < PUZZLE_COLS; j++)
{
if (charpuzzle[j * PUZZLE_COLS + i] != ' ')
set_predefinedc(puzzle, i + 65, j + 1, charpuzzle[j * PUZZLE_COLS + i]);
}
}
set_sures(puzzle, PUZZLE_ROWS, PUZZLE_COLS, 1);
unsigned int result = set_sures(puzzle, PUZZLE_ROWS, PUZZLE_COLS, 2);
if(result){
printf("Unresolved. %d cells left:\n",result);
}else{
printf("Succesfully resolved:\n");
}
draw_puzzle(puzzle, PUZZLE_ROWS, PUZZLE_COLS);
return result;
}
int main()
{
solve(
"913 5 "
"6 7 24"
" 5 8 7 "
" 79 "
" 2 9 43"
" 4 9 "
" 4 19 "
"7 6 9 5"
" 1 64 7");
printf("\n");
solve(
"5 3 7 "
"6 195 "
" 98 6 "
"8 6 3"
"4 8 3 "
"7 2 6"
" 6 28 "
" 419 5"
" 8 79");
printf("\n");
solve(
" 4 8 3 "
" 1 9 5"
"7 2 6 "
" 5 7 2"
" 4 "
"9 5 7 "
" 2 6 8"
"6 3 1 "
" 8 7 9 ");
printf("\n");
solve(
" 7429 8 "
"2 83 "
"896 351 4"
" 1 6 9 "
"7 1 2 6"
"6 973 1"
" 6 "
"5 8 9 "
"46 58 1 ");
printf("\n");
solve(
"2 5 74 6"
" 31 "
" 23 "
" 2 "
"86 31 "
" 45 "
" 9 7 "
" 695 2"
" 1 6 8");
printf("\n");
return 0;
}

252
sudoku.h Normal file
View File

@ -0,0 +1,252 @@
#ifndef SUDOKU_H
#define SUDOKU_H
#include "rlib.h"
#include "rsolve.h"
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define ANSII_BLUE_BG "\033[34m"
#define ANSII_RED_BG "\033[31m"
#define ANSII_GREEN_BG "\033[32m"
#define ANSII_CLEAR "\033[0m"
#define N 9
void draw_cell(int c)
{
if (c > 0 && c <= 3)
{
printf("%s", ANSII_BLUE_BG);
}
else if (c > 0 && c <= 6)
{
printf("%s", ANSII_GREEN_BG);
}
else if (c > 0 && c <= 9)
{
printf("%s", ANSII_RED_BG);
}
if(c){
printf("%s%d ", c > 9 ? "" :" ", c );
}else{
printf(" 0 ");
}
printf("%s", ANSII_CLEAR);
}
char * grid_to_string(int grid[N][N]){
static char result[1024];
result[0] = 0;
for (int row = 0; row < N; row++) {
for (int col = 0; col < N; col++) {
char chunk[4];
chunk[0] = 0;
sprintf(chunk,"%d ",grid[row][col]);
strcat(result,chunk);
}
strcat(result,"\n");
}
return result;
}
void print_grid(int grid[N][N],bool clear) {
if(clear)
printf("\033[2J\033[H");
for (int row = 0; row < N; row++) {
for (int col = 0; col < N; col++) {
draw_cell(grid[row][col]);
///printf("%d ", grid[row][col]);
}
printf("\n");
}
}
int count_neighbors2(int grid[N][N],int row, int col){
// Check row
int num = -1;
int neighbors = 0;
for (int x = 0; x < N; x++) {
if(grid[row][x])
neighbors++;
if (grid[row][x] == num) {
return 0;
}
}
// Check column
for (int x = 0; x < N; x++) {
if(grid[x][col])
neighbors++;
if (grid[x][col] == num) {
return 0;
}
}
// Check box
int startRow = row - row % (N / 3), startCol = col - col % (N / 3);
for (int i = 0; i < N / 3; i++) {
for (int j = 0; j < N / 3; j++) {
if(grid[i + startRow][j + startCol])
neighbors++;
if (grid[i + startRow][j + startCol] == num) {
return 0;
}
}
}
return neighbors;
}
int is_safe(int grid[N][N], int row, int col, int num) {
//if(count_neighbors(grid, row,col) < 4)
// return false;
// Check row
for (int x = 0; x < N; x++) {
if (grid[row][x] == num) {
return false;
}
}
// Check column
for (int x = 0; x < N; x++) {
if (grid[x][col] == num) {
return false;
}
}
// Check box
int startRow = row - row % (N / 3), startCol = col - col % (N / 3);
for (int i = 0; i < N / 3; i++) {
for (int j = 0; j < N / 3; j++) {
if (grid[i + startRow][j + startCol] == num) {
return false;
}
}
}
return true;
}
void grid_reset(int * grid){
memset(grid,0,N*N*sizeof(int));
}
int * grid_copy(int * grid){
int * new_grid = malloc(N*N*sizeof(int));
memcpy(new_grid,grid,N*N*sizeof(int));
return new_grid;
}
int * grid_new(){
return (int *)calloc(sizeof(int),N*N);
}
bool empty_spot_is_available(int grid[N][N]){
for (unsigned int row = 0; row < N; row++) {
for (unsigned int col = 0; col < N; col++) {
if (grid[row][col] == 0) {
return true;
}
}
}
return false;
}
unsigned long long _solve(int grid[N][N], unsigned long long *attempts, bool draw){
(*attempts)++;
unsigned int row, col;
if(!get_easiest_cell(grid,&row,&col)){
//print_grid(grid, false);
return *attempts;
}
for(int num = 1; num < N + 1; num++){
if(is_safe(grid,row,col,num)){
grid[row][col] = num;
//print_grid(grid,true);
if(_solve(grid,attempts,draw))
{
return *attempts;
}
grid[row][col] = 0;
}
}
return 0;
}
unsigned int _solve2(int grid[N][N], unsigned long long * attempts, bool draw) {
(*attempts)++;
unsigned int row, col;
bool emptySpot = false;
for (row = 0; row < N; row++) {
for (col = 0; col < N; col++) {
if (grid[row][col] == 0) {
// if(count_neighbors(grid,row,col) == 8){
// print_grid(grid,true);
// printf("Found neighbors\n");
// exit(0);
//}
emptySpot = true;
break;
}
}
if (emptySpot) {
break;
}
}
if(!emptySpot)
return true;
/*
if (!empty_spot_is_available(grid)) {
return true;
}*/
for (int num = 1; num <= 9; num++) {
//unsigned int * easy_row = calloc(sizeof(int),1);
//unsigned int * easy_col = calloc(sizeof(int), 1);
//if(get_easiest_cell(grid,easy_row,easy_col)){
// row = *easy_row;
// free(easy_row);
// col = *easy_col;
// free(easy_col);
//}
if (is_safe(grid, row, col, num)) {
grid[row][col] = num;
if(draw)
print_grid(grid,true);
if (_solve2(grid,attempts,draw)) {
return *attempts;
}
grid[row][col] = 0;
}
}
return 0;
}
unsigned int solve2(int grid[N][N], bool draw){
unsigned long long attempts = 0;
return _solve(grid,&attempts, draw);
}
unsigned int solve(int grid[N][N],bool draw) {
unsigned long long attempts = 0;
return _solve2(grid,&attempts, draw);
}
#endif

711
sudoku.html Normal file
View File

@ -0,0 +1,711 @@
<html>
<head>
</head>
<body>
<div id="app" class="app"></div>
<pre>
TODO:
- if orange fields, no other selections should be possible than deselect.
</pre>
<template id="template_sudoku_container">
<div style="width:640px;height:640px;font-size:1.2em;" class="container-content"></div>
</template>
<template id="template_sudoku_parser">
<div style="width:100%;height:100%" class="parser-content"></div>
</template>
<template id="template_sudoku_cell">
<div class="cell-content"></div>
</template>
<style type="text/css">
.app {
width: 400px;
height: 400px;
}
.cell-content {
font-family: 'Courier New', Courier, monospace;
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
border: 1px solid #CCCCCC;
text-align: center;
height: 10%;
width: 10%;
float: left;
display: flex;
justify-content: center;
align-items: center;
font-size: calc(5px + 1vw);
}
.sudoku-cell-selected {
background-color: lightgreen;
}
.sudoku-cell-invalid {
color: red;
font-weight: bold;
}
</style>
<script type="text/javascript">
const game_size = 9;
const container_template = document.getElementById('template_sudoku_container');
const cell_template = document.getElementById("template_sudoku_cell");
const sudokuParserTemplate = document.getElementById('template_sudoku_parser');
const container = document.getElementById('app'); //.importNode(container_template.content, true);
const _hashChars = "abcdefghijklmnopqrstuvwxyz\"':[]0123456789";
function hashString(str) {
let result = 0;
for (let i = 0; i < str.length; i++) {
result += (_hashChars.indexOf(str[i]) + 1) * (i * 40);
}
return result;
}
class State {
number = 0
cells = []
selection = []
selectionCount = 0
_string = null
_hash = null
_json = null;
constructor(arg) {
if (isNaN(arg)) {
this.number = arg.number;
this.cells = arg.cells;
this.selectCount = arg.selectionCount;
this._hash = arg._hash;
this._json = arg;
} else {
this.number = arg;
}
}
toJson() {
if (this._json == null) {
const json = {
number: this.number,
cells: this.cells,
selectionCount: this.selectionCount,
_hash: this.getHash()
};
this._json = json;
}
return this._json;
}
getHash() {
if (this._hash == null) {
let started = false;
let count = 0;
let hash = this.cells.filter((cell) => {
if (!started && cell.state[0] == 0 && cell.state[1] == 1)
return false;
started = true;;
count++;
return true;
}).map(cell => {
return cell.state;
}).join('');
this._hash = `${count}${hash}`;
}
return this._hash
}
equals(otherState) {
if (otherState.selectionCount != this.selectionCount)
return false;
return otherState.getHash() == this.getHash();
}
toString() {
if (this._string == null) {
this._string = JSON.stringify({
number: this.number,
selection: this.selection.map(cell => cell.toString()),
cells: this.cells
});
}
return this._string
}
};
class Cell {
row = 0;
col = 0;
initial = false;
letter = null
name = null
options = [];
value = 0;
values = []
element = null;
app = null;
selected = false;
container = null;
async solve(){
this.app.pushState();
let originalValues = this.values;
let valid = false;
this.selected = false;
for(let i = 1; i < 10; i++){
this.addNumber(i);
if(this.validate())
if(await this.app.solve())
return true;
this.value = 0;
this.values = originalValues;
}
return false;
}
getState() {
return `${this.selected ? 1 : 0}${this.valid ? 1 : 0}`;
}
toggleSelect() {
this.selected = !this.selected;
if (this.selected) {
this.select();
//this.element.classList.add('sudoku-cell-selected');
} else {
this.deSelect();
//this.element.classList.remove('sudoku-cell-selected');
}
this.update();
return this.selected;
}
async addNumber(value) {
this.values.pop(value);
this.values.push(value);
this.value = Number(value);
const _this = this;
window.requestAnimationFrame(() => {
this.element.textContent = this.value == 0 ? "" : String(this.value);
})
this.validate();
//this.update();
}
onClick() {
//this.
if (!this.initial)
this.toggleSelect();
//this.app.onCellClick(this)
}
deSelect() {
this.selected = false;
this.element.classList.remove('sudoku-cell-selected');
}
select() {
this.selected = true;
this.element.classList.add('sudoku-cell-selected');
}
validateBox(){
let startRow = this.row - this.row % (9 / 3);
let startCol = this.col - this.col % (9 / 3);
for (let i = startRow; i < 9 / 3; i++) {
for (let j = startCol; j < 9 / 3; j++) {
let fieldIndex = (i * 9) + j;
console.info(fieldIndex);
if (this.app.cells[fieldIndex].value == this.value) {
return false;
}
}
return true
}
return true;
}
isValid() {
const _this = this;
this.valid = !this.value && (!this.app.cells.filter(cell => {
return cell.value != 0 &&
cell != _this &&
(cell.row == _this.row || cell.col == _this.col) &&
cell.value == _this.value;
}).length && this.validateBox()); //&& !this.app.getBoxValues(this.name).indexOf(this.value) == -1);
return this.valid;
}
update() {
if (this.selected)
this.select()
else
this.deSelect()
this.app.cells.forEach(cell => {
cell.validate()
})
this.element.textContent = this.value ? String(this.value) : "";
}
validate() {
if (this.isValid() || !this.value) {
this.element.classList.remove('sudoku-cell-invalid');
} else {
this.element.classList.add('sudoku-cell-invalid');
}
return this.valid;
}
destructor() {
//this.container.delete();
}
constructor(app, row, col) {
this.app = app;
this.container = document.importNode(cell_template.content, true);
this.row = row;
this.col = col;
this.selected = false;
this.letter = "abcdefghi"[row];
this.name = `${this.letter}${this.col}`
this.value = 0;
this.values = [];
this.element = this.container.querySelector('.cell-content');
this.valid = true;
const _this = this;
this.element.addEventListener('click', (e) => {
_this.onClick();
});
this.element.addEventListener('mousemove', (e) => {
if (!_this.initial && e.buttons == 1)
_this.select();
else if (!_this.initial && e.buttons == 2)
_this.deSelect();
else
_this.app.pushState()
});
this.element.addEventListener('mouseexit', (e) => {
if (!e.buttons) {
// _this.app.pushState();
}
});
this.element.addEventListener('contextmenu', (e) => {
e.preventDefault();
if (!_this.initial && _this.selected) {
_this.deSelect();
} else {
_this.values.pop(_this.value);
_this.addNumber(0);
_this.deSelect();
_this.update();
//_this.app.deSelectAll();
}
});
}
redraw() {
}
render(container) {
container.appendChild(this.container);
}
toString() {
return `${this.row}:${this.col}`
}
}
class SudokuParser {
app = null
element = null
container = null
blank = true
content = ''
size = 9
constructor(app, container) {
//this.container = container;
this.app = app;
this.container = document.importNode(sudokuParserTemplate.content, true);
this.element = document.createElement('div');
this.element.style.width = '90%';
this.element.style.height = '50%';
this.element.border = '1px solid #CCCCCC';
this.content = '';
this.cells = []
this.element.contentEditable = true;
this.element.textContent = 'Paste puzzle here';//this.container.querySelector('.parser-content');
this.toggle();
this.blank = true;
const _this = this;
this.element.addEventListener('click', (e) => {
if (_this.blank)
_this.element.textContent = '';
_this.blank = false;
});
this.element.addEventListener('contextmenu', (e) => {
if (_this.blank)
_this.element.textContent = '';
this.blank = false;
});
this.element.addEventListener('input', (e) => {
_this.element.innerHTML = this.element.textContent;
_this.parseAndApply();
});
this.element.addEventListener('keyup', (e) => {
//_this.parseAndApply();
});
container.appendChild(this.element);
}
parseAndApply() {
this.parse();
this.apply();
}
parse() {
const content = this.element.textContent;
const regex = /\d/g;
const matches = [...content.matchAll(regex)];
let row = 0;
let col = 0;
const cells = []
const max = this.size * this.size;
matches.forEach(match => {
if (row * col == max) {
return;
}
if (col == 9) {
row++;
col = 0;
}
if (row == 9) {
row = 0;
col = 0;
}
const digit = match[0];
const cell = new Cell(this.app, row, col);
cell.addNumber(digit);
cell.initial = true;
cells.push(cell);
col++;
});
this.cells = cells;
}
apply() {
this.app.cells.forEach(cell => {
cell.initial = false;
cell.number = 0;
cell.numbers = [];
cell.addNumber(0);
});
this.cells.forEach(cell => {
const appCell = this.app.getCellByName(cell.name);
appCell.initial = cell.value != 0;
appCell.addNumber(cell.value);
});
this.toggle();
}
toggle() {
if (this.element.style.display == 'none') {
this.element.innerHTML = 'Paste here your puzzle';
}
this.element.style.display = this.element.style.display != 'none' ? 'none' : 'block';
}
}
class Sudoku {
cells = [];
game_size = 0;
cell_count = 0;
selectedCells = []
container = null
element = null
states = []
previousSelection = []
state_number = 1
parser = null;
status = null;
reset(){
this.cells.forEach(cell=>{
cell.values = []
cell.selected = false;
cell.addNumber(0);
})
}
loadSession() {
const session = this.getSession();
if (!session)
return null;
this.state_number = session.state_number;
this.states = session.states.map(state => {
return new State(state);
});
this.refreshState();
}
async getEmptyCell(){
return this.cells.filter(cell=>{
if(cell.value == 0)
return true
return false
})[0]
}
async solve() {
const cell = await this.getEmptyCell();
if(!cell)
return this.isValid();
return await cell.solve();
}
deleteSession(){
localStorage.removeItem('session');
}
getSession() {
const session = localStorage.getItem('session');
if (!session) {
return null
}
return JSON.parse(session);
}
saveSession() {
this.pushState();
const states = this.states.map(state => {
return state.toJson()
});
const session = {
state_number: this.state_number,
states: states
}
localStorage.setItem('session', JSON.stringify(session));
//console.info('session saved');
}
getBoxValues(cell_name) {
let values = this.cells.filter(cell => {
return cell.name != cell_name && cell.name[0] == cell_name[0]
}).map(cell => {
return cell.value
});
return values;
}
toggle() {
this.container.style.display = this.container.style.display != 'none' ? 'none' : 'block';
}
toggleParser() {
//this.parser.toggle();
this.deSelectAll();
this.parser.toggle()
}
constructor(container, game_size) {
const _this = this;
this.container = container
this.element = container
this.parser = new SudokuParser(this, this.container);
this.game_size = game_size;
this.cell_count = game_size * game_size;
for (let row = 0; row < this.game_size; row++) {
for (let col = 0; col < this.game_size; col++) {
this.cells.push(new Cell(this, row, col));
}
}
console.info("Loading session");
setTimeout(()=>{
if(_this.status == "applying state"){
_this.deleteSession();
window.location.reload();
}else{
console.info("Finished session validation");
}
},10000);
this.loadSession();
document.addEventListener('keypress', (e) => {
if (!isNaN(e.key) || e.key == 'd') {
let number = e.key == 'd' ? 0 : Number(e.key);
_this.addNumberToSelection(number);
//console.info({set:Number(e.key)});
}
if (e.key == 'p') {
_this.toggleParser();
}
if (e.key == 'u') {
_this.popState();
}
if (e.key == 'r') {
if (this.selection().length) {
this.pushState();
_this.deSelectAll();
} else {
let state = this.getLastState();
if (state) {
state.cells.filter(cell => cell.selected).forEach(cell => {
this.getCellByName(cell.name).select();
})
}
}
}
});
this.element.addEventListener('mousemove', (e) => {
//this.pushState();
})
document.addEventListener('dblclick', (e) => {
_this.previousSelection = _this.selection();
_this.cells.forEach(cell => {
cell.deSelect();
});
});
document.addEventListener('contextmenu', (e) => {
});
this.element.addEventListener('mouseexit', (e) => {
// Edge case while holding mouse button while dragging out. Should save state
_this.pushState();
});
this.element.addEventListener('mouseup', (e) => {
_this.pushState();
});
this.pushState()
}
isValid() {
return this.cells.filter(cell => !cell.isValid()).length == 0
}
createState() {
const state = new State(this.state_number)
let selectedCount = 0;
state.cells = this.cells.map(cell => {
if (cell.selected) {
selectedCount++;
}
return { name: cell.name, values: cell.values, value: cell.value, selected: cell.selected, state: cell.getState() }
});
state.selectedCount = selectedCount;
return state;
}
pushState() {
const state = this.createState();
const previousState = this.getLastState();
if (!previousState || !previousState.equals(state)) {
this.states.push(state);
this.state_number++;
this.saveSession();
//console.info({ pushState: state.getHash(), length: state.getHash().length, number: state.number });
}
}
refreshState() {
const state = this.getLastState();
if (!state)
return null;
this.applyState(state)
return state;
}
getLastState() {
return this.states.length ? this.states.at(this.states.length - 1) : null;
}
applyState(state) {
this.status = "applying state";
state.cells.forEach(stateCell => {
const cell = this.getCellByName(stateCell.name);
cell.selected = stateCell.selected;
cell.values = stateCell.values;
cell.value = stateCell.value;
cell.update();
})
this.status = "applied state"
}
popState() {
let state = this.states.pop();
if (!state)
return;
if (state.equals(this.createState())) {
return this.popState();
}
this.applyState(state);
this.saveSession();
//console.info({ popState: state.getHash(), length: state.getHash().length, number: state.number });
}
getCellByName(name) {
return this.cells.filter(cell => {
return cell.name == name
})[0]
}
deSelectAll() {
this.cells.forEach(cell => {
cell.deSelect();
});
}
selection() {
return this.cells.filter(cell => {
return cell.selected
});
}
onSelectionToggle() {
}
addNumberToSelection(number) {
const _this = this;
this.pushState();
this.selection().forEach((cell) => {
cell.addNumber(number)
cell.update();
})
_this.pushState();
if (this.isValid()) {
this.deSelectAll();
}
_this.pushState();
}
onCellDblClick(cell) {
this.previousSelection = this.selection();
this.popState()
let originalSelected = this.selctedCells
if (cell.selected) {
this.selectedCells.push(cell);
} else {
this.selectedCells.pop(cell);
}
if (!this.originalSelected != this.selectedCells) {
this.popState();
}
//console.info({selected:this.selectedCells});
}
render() {
this.cells.forEach(cell => {
cell.render(this.element);
});
}
}
const sudoku = new Sudoku(container, 9);
sudoku.render();
const app = sudoku;
/*
document.addEventListener('contextmenu',(e)=>{
e.preventDefault();
});*/
/*
for(let i = 0; i < game_size*game_size; i++){
const cell = document.importNode(cell_template.content,true);
app.appendChild(cell);
}*/
//document.body.appendChild(app);
</script>
</body>

850
sudoku.js Normal file
View File

@ -0,0 +1,850 @@
function randInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function cssRuleExists(match){
for(let i = 0; i < document.styleSheets.length; i++){
let styleSheet = document.styleSheets[i]
let rules = styleSheet.cssRules
for(let j = 0; j < rules.length; j++){
let rule = rules[j]
if(rule.selectorText && rule.selectorText == match){
return true;
}
}
}
return false
}
class EventHandler {
constructor() {
this.events = {};
this.eventCount = 0;
this.suppresEvents = false;
this.debugEvents = false;
}
on(event, listener) {
if (!this.events[event]) {
this.events[event] = { name:name, listeners: [],callCount: 0};
}
this.events[event].listeners.push(listener);
}
off(event, listenerToRemove) {
if (!this.events[event]) return;
this.events[event].listeners = this.events[event].listeners.filter(listener => listener !== listenerToRemove);
}
emit(event, data) {
if (!this.events[event]) return [];
if (this.suppresEvents) return [];
this.eventCount++;
const returnValue = this.events[event].listeners.map(listener =>{
var returnValue = listener(data)
if(returnValue == undefined)
return null
return returnValue
});
this.events[event].callCount++;
if(this.debugEvents){
console.debug('debugEvent',{event:event, arg:data, callCount: this.events[event].callCount, number:this.eventCount, returnValues:returnValue})
}
return returnValue
}
suppres(fn) {
const originallySuppressed = this.suppresEvents
this.suppresEvents = true
fn(this)
this.suppresEvents = originallySuppressed
return originallySuppressed
}
}
class Col extends EventHandler {
id = 0
values = []
initial = false
_marked = false
row = null
index = 0
valid = true
_selected = false
_value = 0
_isValidXy() {
if (!this._value)
return true;
return this.row.puzzle.fields.filter(field => {
return (
field.index == this.index && field.value == this._value
||
field.row.index == this.row.index && field.value == this._value)
}).filter(field => field != this).length == 0
}
mark() {
this.marked = true
}
unmark() {
this.marked = false
}
select() {
this.selected = true
}
unselect() {
this.selected = false
}
_isValidBox() {
if (!this._value)
return true;
let startRow = this.row.index - this.row.index % (this.row.puzzle.size / 3);
let startCol = this.index - this.index % (this.row.puzzle.size / 3);
for (let i = 0; i < this.row.puzzle.size / 3; i++) {
for (let j = 0; j < this.row.puzzle.size / 3; j++) {
const field = this.row.puzzle.get(i + startRow, j + startCol);
if (field != this && field.value == this._value) {
return false;
}
}
}
return true
}
validate() {
if (!this.row.puzzle.initalized) {
return this.valid;
}
if (this.initial) {
this.valid = true
return this.valid
}
if (!this.value && !this.valid) {
this.valid = true
this.emit('update', this)
return this.valid
}
let oldValue = this.valid
this.valid = this._isValidXy() && this._isValidBox();
if (oldValue != this.valid) {
this.emit('update', this)
}
return this.valid
}
set value(val) {
if (this.initial)
return;
const digit = Number(val)
const validDigit = digit >= 0 && digit <= 9;
let update = validDigit && digit != this.value
if (update) {
this._value = Number(digit)
this.validate()
this.emit('update', this);
}
}
get value() {
return this._value
}
get selected() {
return this._selected
}
set selected(val) {
if (val != this._selected) {
this._selected = val
if(this.row.puzzle.initalized)
this.emit('update', this);
}
}
get marked() {
return this._marked
}
set marked(val){
if(val != this._marked){
this._marked = val
if(this.row.puzzle.initalized){
this.emit('update',this)
}
}
}
constructor(row) {
super()
this.row = row
this.index = this.row.cols.length
this.id = this.row.puzzle.rows.length * this.row.puzzle.size + this.index;
this.initial = false
this.selected = false
this._value = 0;
this.marked = false
this.valid = true
}
update() {
this.emit('update',this)
}
toggleSelected() {
this.selected = !this.selected
}
toggleMarked() {
this.marked = !this.marked
}
get data() {
return {
values: this.values,
value: this.value,
index: this.index,
id: this.id,
row: this.row.index,
col: this.index,
valid: this.valid,
initial: this.initial,
selected: this.selected,
marked: this.marked
}
}
toString() {
return String(this.value)
}
toText() {
return this.toString().replace("0", " ");
}
}
class Row extends EventHandler {
cols = []
puzzle = null
index = 0
initialized = false
constructor(puzzle) {
super()
this.puzzle = puzzle
this.cols = []
this.index = this.puzzle.rows.length
const me = this
this.initialized = false
for (let i = 0; i < puzzle.size; i++) {
const col = new Col(this);
this.cols.push(col);
col.on('update', (field) => {
me.emit('update', field)
})
}
this.initialized = true
}
get data() {
return {
cols: this.cols.map(col => col.data),
index: this.index
}
}
toText() {
let result = ''
for (let col of this.cols) {
result += col.toText();
}
return result
}
toString() {
return this.toText().replaceAll(" ", "0");
}
}
class Puzzle extends EventHandler {
rows = []
size = 0
hash = 0
states = []
parsing = false
_initialized = false
initalized = false
_fields = null
constructor(arg) {
super()
this.debugEvents = true;
this.initalized = false
this.rows = []
if (isNaN(arg)) {
// load session
} else {
this.size = Number(arg)
}
for (let i = 0; i < this.size; i++) {
const row = new Row(this);
this.rows.push(row);
row.on('update', (field) => {
this.onFieldUpdate(field)
})
}
this._initialized = true
this.initalized = true
this.commitState()
}
validate() {
return this.valid;
}
_onEventHandler(){
this.eventCount++;
}
makeInvalid() {
if (!app.valid) {
let invalid = this.invalid;
return invalid[invalid.length - 1];
}
this.rows.forEach(row => {
row.cols.forEach(col => {
if (col.value) {
let modify = null;
if (col.index == this.size) {
modify = this.get(row.index, col.index - 2);
} else {
modify = this.get(row.index, col.index + 1);
}
modify.value = col.value
// last one is invalid
return modify.index > col.index ? modify : col;
}
col.valid = false
})
})
this.get(0, 0).value = 1;
this.get(0, 1).value = 1;
return this.get(0, 1);
}
reset() {
this._initialized = false
this.initalized == false;
this.parsing = true
this.fields.forEach(field => {
field.initial = false
field.selected = false
field.marked = false
field.value = 0
})
this.hash = 0
this.states = []
this.parsing = false
this.initalized = true
this._initialized = true
this.commitState()
}
get valid() {
return this.invalid.length == 0
}
get invalid() {
this.emit('validating',this)
const result = this.fields.filter(field => !field.validate())
this.emit('validated',this)
return result
}
get selected() {
return this.fields.filter(field => field.selected)
}
get marked(){
return this.fields.filter(field=>field.marked)
}
loadString(content) {
this.emit('parsing', this)
this.reset()
this.parsing = true
this.initalized = false;
this._initialized = false;
const regex = /\d/g;
const matches = [...content.matchAll(regex)]
let index = 0;
const max = this.size * this.size;
matches.forEach(match => {
const digit = Number(match[0]);
let field = this.fields[index]
field.value = digit;
field.initial = digit != 0
index++;
});
this._initialized = true;
this.parsing = false
this.deselect();
this.initalized = true;
this.suppres(()=>{
this.fields.forEach((field)=>{
field.update()
})
})
this.commitState()
this.emit('parsed', this)
this.emit('update',this)
}
get state() {
return this.getData(true)
}
get previousState() {
if (this.states.length == 0)
return null;
return this.states.at(this.states.length - 1)
}
get stateChanged() {
if (!this._initialized)
return false
return !this.previousState || this.state != this.previousState
}
commitState() {
if (!this.initalized)
return false;
this.hash = this._generateHash()
if (this.stateChanged) {
this.states.push(this.state)
this.emit('commitState', this)
return true
}
return false
}
onFieldUpdate(field) {
if (!this.initalized)
return false;
if (!this._initialized)
return;
this.validate();
this.commitState();
this.emit('update', this)
}
get data() {
return this.getData(true)
}
popState() {
let prevState = this.previousState
if (!prevState)
{
this.deselect()
return null
}while (prevState && prevState.hash == this.state.hash)
prevState = this.states.pop()
if (!prevState)
{
this.deselect()
return null
}
this.applyState(prevState)
this.emit('popState', this)
return prevState
}
applyState(newState) {
this._initialized = false
newState.fields.forEach(stateField => {
let field = this.get(stateField.row, stateField.col)
field.selected = stateField.selected
field.values = stateField.values
field.value = stateField.value
field.initial = stateField.initial
field.validate()
})
this._initialized = true
this.emit('stateApplied', this)
this.emit('update', this)
}
getData(withHash = false) {
let result = {
fields: this.fields.map(field => field.data),
size: this.size,
valid: this.valid
}
if (withHash) {
result['hash'] = this._generateHash()
}
return result;
}
get(row, col) {
if (!this.initalized)
return null;
if (!this.rows.length)
return null;
if (!this.rows[row])
return null;
return this.rows[row].cols[col];
}
get fields() {
if (this._fields == null) {
this._fields = []
for (let row of this.rows) {
for (let col of row.cols) {
this._fields.push(col)
}
}
}
return this._fields
}
_generateHash() {
var result = 0;
JSON.stringify(this.getData(false)).split('').map(char => {
return char.charCodeAt(0) - '0'.charCodeAt(0)
}).forEach(num => {
result += 26
result = result + num
})
return result
}
get text() {
let result = ''
for (let row of this.rows) {
result += row.toText() + "\n"
}
result = result.slice(0, result.length - 1)
return result
}
get initialFields() {
return this.fields.filter(field => field.initial)
}
get json() {
return JSON.stringify(this.data)
}
get zeroedText() {
return this.text.replaceAll(" ", "0")
}
get string() {
return this.toString()
}
toString() {
return this.text.replaceAll("\n","").replaceAll(" ", "0")
}
get humanFormat() {
return ' ' + this.text.replaceAll(" ", "0").split("").join(" ")
}
getRandomField() {
const emptyFields = this.empty;
return emptyFields[randInt(0, emptyFields.length - 1)]
}
update(callback) {
this.commitState()
this.intalized = false
callback(this);
this.intalized = true
this.validate()
this.commitState()
this.intalized = false
if(this.valid)
this.deselect()
this.intalized = true
this.emit('update', this)
}
get empty() {
return this.fields.filter(field => field.value == 0)
}
getRandomEmptyField() {
let field = this.getRandomField()
if (!field)
return null
return field
}
deselect() {
this.fields.forEach(field => field.selected = false)
}
generate() {
this.reset()
this.initalized = false
for (let i = 0; i < 17; i++) {
this.fillRandomField()
}
this.deselect()
this.initalized = true
this.commitState()
this.emit('update',this)
}
fillRandomField() {
let field = this.getRandomEmptyField()
if (!field)
return
this.deselect()
field.selected = true
let number = 0
number++;
while (number <= 9) {
field.value = randInt(1, 9)
field.update()
if (this.validate()) {
field.initial = true
return field
}
number++;
}
return false;
}
}
class PuzzleManager {
constructor(size) {
this.size = size
this.puzzles = []
this._activePuzzle = null
}
addPuzzle(puzzle){
this.puzzles.push(puzzle)
}
get active(){
return this.activePuzzle
}
set activePuzzle(puzzle){
this._activePuzzle = puzzle
}
get activePuzzle(){
return this._activePuzzle
}
}
const puzzleManager = new PuzzleManager(9)
class Sudoku extends HTMLElement {
styleSheet = `
.sudoku {
font-size: 13px;
color:#222;
display: grid;
grid-template-columns: repeat(9, 1fr);
grid-template-rows: auto;
gap: 0px;
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
background-color: #e5e5e5;
border-radius: 5px;
aspect-ratio: 1/1;
}
.sudoku-field-initial {
color: #777;
}
.sudoku-field-selected {
background-color: lightgreen;
}
.soduku-field-marked {
background-color: blue;
}
.sudoku-field-invalid {
color: red;
}
.sudoku-field {
border: 1px solid #ccc;
text-align: center;
padding: 2px;
aspect-ratio: 1/1;
}`
set fieldSize(val) {
this._fieldSize = val ? Number(val) : null
this.fieldElements.forEach(field => {
field.style.fontSize = this._fieldSize ? this._fieldSize.toString() +'px' : ''
})
}
get fieldSize(){
return this._fieldSize
}
get eventCount() {
return this.puzzle.eventCount
}
get puzzleContent(){
return this.puzzle.humanFormat
}
set puzzleContent(val) {
if (val == "generate") {
this.puzzle.generate()
} else if (val) {
this.puzzle.loadString(val)
} else {
this.puzzle.reset()
}
}
connectedCallback() {
this.puzzleContent = this.getAttribute('puzzle') ? this.getAttribute('puzzle') : null
this._fieldSize = null
this.fieldSize = this.getAttribute('size') ? this.getAttribute('size') : null
this.readOnly = this.getAttribute('read-only') ? true : false
this.attachShadow({ mode: 'open' });
this.shadowRoot.appendChild(this.styleElement)
this.shadowRoot.appendChild(this.puzzleDiv)
}
toString(){
return this.puzzleContent
}
set active(val){
this._active = val
if(this._active)
this.manager.activePuzzle =this
}
get active(){
return this._active
}
set readOnly(val){
this._readOnly = val ? true : false
}
get readOnly(){
return this._readOnly
}
constructor() {
super();
this._readOnly = false;
this._active = false
this.fieldElements = []
this.puzzle = new Puzzle(9)
this.fields = []
this.styleElement = document.createElement('style');
this.styleElement.textContent = this.styleSheet
this.puzzleDiv = document.createElement('div')
this.puzzleDiv.classList.add('sudoku');
this._bind()
this.manager.addPuzzle(this)
}
get manager() {
return puzzleManager
}
_bind(){
this._bindFields()
this._bindEvents()
this._sync()
}
_bindFields(){
const me = this
this.puzzle.rows.forEach((row) => {
row.cols.forEach((field) => {
const fieldElement = document.createElement('div');
fieldElement.classList.add('sudoku-field');
fieldElement.field = field
field.on('update', (field) => {
me._sync()
})
fieldElement.addEventListener('click', (e) => {
if(!me.readOnly)
field.toggleSelected()
})
fieldElement.addEventListener('contextmenu',(e)=>{
e.preventDefault()
field.row.puzzle.update(()=>{
field.selected = false
field.value = 0
})
})
this.fields.push(field)
this.fieldElements.push(fieldElement)
this.puzzleDiv.appendChild(fieldElement);
});
});
}
_bindEvents(){
const me = this
this.puzzle.on('update', () => {
me._sync()
});
this.puzzleDiv.addEventListener('mouseenter', (e) => {
me.active = true
})
this.puzzleDiv.addEventListener('mouseexit', (e) => {
me.active = false
})
document.addEventListener('keydown', (e) => {
if(me.readOnly)
return
if (!puzzleManager.active)
return
const puzzle = puzzleManager.active.puzzle
if (e.key == 'u') {
puzzle.popState();
} else if (e.key == 'd') {
puzzle.update((target) => {
puzzle.selected.forEach(field => {
field.value = 0
});
})
} else if (e.key == 'a') {
puzzle.autoSolve()
} else if (e.key == 'r') {
puzzle.fillRandomField();
} else if (!isNaN(e.key)) {
puzzle.update((target) => {
puzzle.selected.forEach(field => {
field.value = Number(e.key)
})
});
} else if(e.key == 'm'){
let fields = [];
puzzle.update((target) => {
target.selected.forEach(field => {
field.selected = false;
fields.push(field)
});
});
puzzle.update((target)=>{
fields.forEach((field)=>{
field.toggleMarked();
})
});
puzzle.emit('update',puzzle);
}
})
}
autoSolve() {
const me = this
window.requestAnimationFrame(() => {
if (me.fillRandomField()) {
if (me.empty.length)
return me.autoSolve()
}
})
}
get(row, col){
return this.puzzle.get(row,col)
}
_syncField(fieldElement) {
const field = fieldElement.field
fieldElement.classList.remove('sudoku-field-selected')
fieldElement.classList.remove('sudoku-field-empty')
fieldElement.classList.remove('sudoku-field-invalid')
fieldElement.classList.remove('sudoku-field-initial')
fieldElement.classList.remove('sudoku-field-marked')
console.info('Removed marked class');
fieldElement.innerHTML = field.value ? field.value.toString() : '&nbsp;'
if (field.selected) {
fieldElement.classList.add('sudoku-field-selected')
window.selected = field.field
}
if (!field.valid) {
fieldElement.classList.add('sudoku-field-invalid')
}
if (!field.value) {
fieldElement.classList.add('sudoku-field-empty')
}
if(field.initial){
fieldElement.classList.add('sudoku-field-initial')
}
if(field.marked){
fieldElement.classList.add('sudoku-field-marked')
console.info("added marked lcass")
}
}
_sync() {
this.fieldElements.forEach(fieldElement => {
this._syncField(fieldElement);
})
}
}
customElements.define("my-sudoku", Sudoku);
function generateIdByPosition(element) {
// Get the parent element
const parent = element.parentNode;
// Get the index of the element within its parent
const index = Array.prototype.indexOf.call(parent.children, element);
// Generate a unique ID using the tag name and index
const generatedId = `${element.tagName.toLowerCase()}-${index}`;
// Assign the generated ID to the element
element.id = generatedId.replace('div-', 'session-key-');
return element.id;
}

BIN
sudoku.zip Normal file

Binary file not shown.

28
sudoku2.c Normal file
View File

@ -0,0 +1,28 @@
#include "sudoku.h"
int main() {
setbuf(stdout,0);
int grid[N][N] = {//13456789
//{9, 4, 3, 5, 2, 7, 8, 1, 6},
{9, 0, 0, 0, 0, 0, 0, 0, 6},
{0, 8, 0, 0, 0, 0, 0, 5, 0},
{0, 0, 7, 0, 0, 0, 4, 0, 0},
{0, 0, 0, 6, 0, 3, 0, 0, 0},
{7, 6, 4, 0, 5, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 4, 0, 0, 0},
{0, 1, 6, 0, 0, 0, 3, 0, 0},
{0, 7, 0, 0, 0, 0, 0, 2, 0},
{8, 3, 0, 0, 0, 0, 0, 0, 1}
};
RBENCH(1,{
unsigned int attempts = solve2(grid,false);
if (!attempts) {
printf("No solution exists\n");
}
printf("Attempts: %d\n", attempts);
});
return 0;
}

83
sudoku2.html Normal file
View File

@ -0,0 +1,83 @@
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script type="text/javascript" src="sudoku.js"></script>
<style>
body {
background-color: #222;
}
.sudoku {
float: left;
color: hwb(0 20% 80%);
opacity: 0.90
}
.sudoku:hover {
opacity: 1;
}
.sudoku-dark {
background-color: #222 !important;
color: #ffffff;
opacity: 0.8
}
.sudoku-dark > .sudoku-field {
color: #333333;
}
.sudoku-dark>.sudoku-field-selected {
color: black;
}
.sudoku-dark>.grid-item {
border: none;
border: 1px solid #333;
}
</style>
</head>
<body>
<pre style="opacity: 0.8; color: #fff;margin:0;padding:10;text-align: left">
Refresh the page for different theme. The widgets are in two colors and multiple sizes available.
These keyboard shortcuts are available for active puzzle widget:
- `d` for deleting last filled field.
- `a` for auto resolving.
- `u` for unlimited undo's.
- `r` for a tip.
Developer notes:
- Widget size is defined by font size on the .soduku css class or as html
attribute to the component.
- You can use an existing puzzle by setting the puzzle attribute on a component.
It must contain at least 81 digits to be valid.
</pre>
<my-sudoku size="17" class="sudoku sudoku-dark" puzzle="
A nice generated puzzle.
It will parse, even with this
nonsense above it. Cool huh?
0 0 8 0 9 0 0 0 0
0 9 0 0 0 0 0 0 7
0 0 0 0 2 0 0 0 0
0 0 3 0 0 0 0 0 0
5 0 0 0 8 0 0 0 0
0 0 6 0 0 0 0 0 0
0 0 0 0 0 3 9 0 0
9 1 0 0 0 0 0 0 0
0 0 0 0 1 0 0 0 0
">
</my-sudoku>
<my-sudoku size="8" class="sudoku" puzzle="generate">
Small generated puzzle
</my-sudoku>
<my-sudoku class="sudoku" puzzle="generate">
Default generated puzzle
</my-sudoku>
<my-sudoku class="sudoku">
Default empty puzzle
</my-sudoku>
</body>
</html>

189
sudoku3.c Normal file
View File

@ -0,0 +1,189 @@
#include "rlib.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct field_t {
unsigned int value;
unsigned int selected;
unsigned int initial;
unsigned int column;
unsigned int row;
unsigned int index;
unsigned char box;
} field_t;
field_t * new_field(){
field_t * field = (field_t *)calloc(1,sizeof(field_t));
return field;
}
typedef struct row_t {
unsigned int index;
field_t * columns;
unsigned int column_count;
} row_t;
row_t * new_row(unsigned int size) {
row_t * row = (row_t *)calloc(1, sizeof(row));
row->columns = calloc(size,sizeof(field_t));
row->index = 0;
return row;
}
typedef struct grid_t {
unsigned int size;
unsigned int row_count;
unsigned int field_count;
row_t * rows;
field_t * fields;
field_t * (*get)(struct grid_t * grid, unsigned int row, unsigned int col);
field_t * (*get_empty_field)(struct grid_t * grid);
} grid_t;
field_t * grid_get(grid_t * grid, unsigned int row, unsigned int col){
return &grid->rows[row].columns[col];
}
field_t * grid_get_empty_field(grid_t * grid){
for(int i = 0; i < grid->size * grid->size; i++){
printf("AT:%d\n",i);
printf("V:%d\n",grid->fields[i].value);
printf("V2:%d\n",grid->rows[i].columns[i].value);
printf("V2:%d\n",grid->get(grid,i,i)->value);
if(grid->fields[i].value == 0)
return &grid->fields[i];
}
return NULL;
}
char get_box_letter(grid_t * grid, unsigned int row, unsigned int col){
//char box_letter = 65 + (row)*grid->size / grid->size / 3 + row; // (row*grid->size+col)+(grid->size / 3);// + grid->size % (grid->size / 3));
char box_letter = 65 + ((row % (grid->size / 3))) + grid->size % (grid->size / 3);// (row-1)*grid->size+(col-1)+(row-1)+(col-1);
//(65 + row * (grid->size / 3) % (grid->size / 3)+col); /// (grid->size / 3));
return box_letter;
}
grid_t * new_grid(unsigned int size){
grid_t * grid = (grid_t *)calloc(1,sizeof(grid_t));
grid->get = grid_get;
grid->get_empty_field = grid_get_empty_field;
grid->size = size;
grid->row_count = 0;
grid->rows = (row_t *)calloc(size, sizeof(row_t));
grid->fields = (field_t *)calloc(size*size, sizeof(field_t));
for(unsigned int irow = 0; irow < size; irow++){
row_t * row = new_row(size);
row->index = grid->row_count;
row->column_count = size;
grid->row_count++;
grid->rows[row->index] = *row;
for(unsigned int icol = 0; icol < size; icol++){
unsigned int field_index = irow * size + icol;
field_t * field = new_field();
field->column = icol;
field->row = irow;
field->index = field_index;
field->box = get_box_letter(grid, irow, icol);
row->columns[icol] = *field;
grid->fields[field_index] = *field;
grid->field_count++;
}
}
return grid;
}
char * field_to_string_human(field_t * field){
static char result[4] = {0};
result[0] = 0;
sprintf(result,"%d, ",field->value);
return sbuf(result);
}
char * field_to_string(field_t * field){
static char result[4] = {0};
result[0] = 0;
sprintf(result,"%d",field->value);
return sbuf(result);
}
char * row_to_string(row_t * row,char * field_string_fn(field_t * field)){
static char result[100] = {0};
result[0] = 0;
for(unsigned int i = 0; i < row->column_count; i++){
strcat(result, field_string_fn(&row->columns[i]));
}
return sbuf(result);
}
char * row_to_string_human(row_t * row,char * field_string_fn(field_t * field)){
static char result[100] = {0};
result[0] = 0;
for(unsigned int i = 0; i < row->column_count; i++){
strcat(result, field_string_fn(&row->columns[i]));
}
strcat(result, "\n");
return sbuf(result);
}
char * row_to_string_c(row_t * row){
static char result[1000] = {0};
result[0] = 0;
strcat(result,"{");
for(int i = 0; i < row->column_count; i++){
strcat(result, field_to_string_human(&row->columns[i]));
}
result[strlen(result)-2] = 0;
strcat(result,"},\n");
return sbuf(result);
}
char * grid_to_string(grid_t * grid, char * row_to_string_fn(row_t * row,char * (field_t *)), char * field_string_fn(field_t * field)){
static char result[1000] = {0};
result[0] = 0;
for(unsigned int irow = 0; irow < grid->size; irow++){
row_t row = grid->rows[irow];
row.column_count++;
strcat(result,row_to_string_fn(&row,field_string_fn));
}
result[strlen(result)-1] = 0;
return sbuf(result);
}
char * grid_to_string_c(grid_t * grid){
char result[1000] = {0};
result[0] = 0;
strcat(result,"{\n");
for(int i = 0; i < grid->size; i++){
strcat(result," ");
strcat(result,row_to_string_c(&grid->rows[i]));
}
result[strlen(result)-2] = 0;
strcat(result,"\n}");
return sbuf(result);
}
char * grid_to_string_human(grid_t * grid){
return sbuf(grid_to_string(grid, row_to_string_human, field_to_string_human));
}
int main(){
grid_t * grid = new_grid(9);
printf("%d \n",grid->rows[3].index);
printf("%d \n",grid->fields[30].index);
printf("%d \n",grid->rows[6].columns[6].index);
field_t * f = grid->get(grid, 0,3);
printf("%d \n", f->index);
//for(int i = 0; i < grid->size*grid->size;i++){
// printf("%c",grid->fields[i].box);
// }
grid->get(grid,5,5)->value = 3;
grid->get(grid,0,0)->value = 1;
printf("**%d**\n",grid->get(grid,5,5)->index);
printf("^^%d^^\n",grid->get(grid,5,5)->value);
printf("$$%d$$\n", grid->fields[0].index);
printf("!!%d!!\n", grid->get_empty_field(grid)->index);
printf("<%s>\n",grid_to_string(grid,row_to_string,field_to_string));
printf("<%s>\n",grid_to_string_human(grid));
printf("<%s>\n",grid_to_string_c(grid));
return 0;
}

83
sudoku3.html Normal file
View File

@ -0,0 +1,83 @@
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script type="text/javascript" src="sudoku.js"></script>
<style>
body {
background-color: #222;
}
.sudoku {
float: left;
color: hwb(0 20% 80%);
background-color: blue;
}
</style>
<script type="text/javascript">
const renderPuzzleFromString = (str, readOnly, size, darkMode) => {
darkMode = true
const sudoku = document.createElement('my-sudoku')
sudoku.setAttribute('puzzle', str ? str : 'generate')
if(readOnly)
sudoku.setAttribute('read-only', 'true')
sudoku.classList.add('sudoku')
if(darkMode){
sudoku.classList.add('sudoku-dark')
}
document.body.appendChild(sudoku);
const id = generateIdByPosition(sudoku);
sudoku.id = id
return sudoku
}
const renderPuzzles = (readOnly,size,darkMode) => {
renderPuzzleFromString(null,readOnly, size, darkMode)
}
document.addEventListener('DOMContentLoaded', (e) => {
let size = randInt(4, 20);
let darkMode = randInt(0,1);
renderPuzzleFromString(`0 0 8 0 9 0 0 0 0
0 9 0 0 0 7 0 0 7
0 0 0 0 2 0 0 0 0
0 0 3 0 0 0 0 0 0
5 0 0 0 8 0 0 0 0
0 0 6 0 0 0 0 0 0
0 0 0 0 0 3 9 0 0
9 1 0 0 0 0 0 0 0
0 0 0 0 1 0 0 0 0 `, true, size, darkMode)
renderPuzzleFromString(`2 3 8 1 9 7 4 5 6
4 9 1 3 5 0 2 8 7
6 5 0 0 0 0 1 3 9
1 2 3 0 4 9 0 6 8
5 7 9 6 8 1 3 2 4
8 4 0 7 3 2 0 9 1
7 8 4 2 6 3 9 1 5
9 1 2 8 7 5 0 4 3
3 6 5 9 1 4 8 7 0
`, true, size, darkMode)
renderPuzzleFromString(` 0 0 8 0 9 0 0 0 0
0 9 0 0 0 0 0 0 7
0 0 0 0 2 0 0 0 0
0 0 3 0 0 0 0 0 0
5 0 0 0 8 0 0 0 0
0 0 6 0 0 0 0 0 0
0 0 0 0 0 3 9 0 0
9 1 0 0 0 0 0 0 0
0 0 0 0 1 0 0 0 0`, size, darkMode)
for (let i = 0; i < 30; i++) {
renderPuzzles(true, size, darkMode)
}
});
</script>
</head>
<body>
</body>
</html>

31
sudoku4.html Normal file
View File

@ -0,0 +1,31 @@
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script type="text/javascript" src="app.js"></script>
<script type="text/javascript" src="sudoku.js"></script>
<style>
body {
background-color: #000;
}
.sudoku {
float: left;
color: hwb(0 20% 80%);
background-color: blue;
}
</style>
<script type="text/javascript">
</script>
</head>
<div id="statistics"></div>
<div id="details"></div>
<body>
</body>
</html>

BIN
sudoku_gen1 Executable file

Binary file not shown.

574
sudoku_gen1.c Normal file
View File

@ -0,0 +1,574 @@
#include "rlib.h"
#include "sudoku.h"
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <pthread.h>
#include "footer.h"
unsigned long long global_m_lock_count = 0;
pthread_mutex_t * global_m_lock = NULL;
#define WITH_MUTEX(src,update_footer) \
if(global_m_lock == NULL){ \
global_m_lock = malloc(sizeof(pthread_mutex_t)); \
pthread_mutex_init(global_m_lock, NULL); \
} \
pthread_mutex_lock(global_m_lock); \
global_m_lock_count++; \
if(update_footer) { \
footer_printf("l:%s:%d (%lld)",__func__,__LINE__,global_m_lock_count); \
} \
src \
if(update_footer) { \
footer_printf("u:%s:%d (%lld)",__func__,__LINE__,global_m_lock_count); \
} \
pthread_mutex_unlock(global_m_lock);
typedef struct thread_data_t {
int puzzle[N][N];
int solution[N][N];
pthread_t thread;
unsigned long long steps;
unsigned long long steps_total;
uint complexity;
uint result_complexity;
uint solved_count;
uint initial_count_minimum;
uint initial_count_maximum;
uint result_initial_count;
uint id;
pthread_mutex_t lock;
bool is_done;
nsecs_t start;
nsecs_t finish;
nsecs_t duration;
} thread_data_t;
thread_data_t * thread_data_create(){
thread_data_t * data = malloc(sizeof(thread_data_t));
memset(data,0,sizeof(thread_data_t));
return data;
}
void thread_data_free(thread_data_t * data){
free(data);
}
int * grid_with_minimal_complexity(thread_data_t * tdata){
int * grid = grid_new();
int * grid_game = NULL;
while(true){
tdata->result_initial_count = rand_int(tdata->initial_count_minimum,tdata->initial_count_maximum);
grid_set_random_free_cells(grid,tdata->result_initial_count);
//print_grid(grid,false);
grid_game = grid_copy(grid);
//footer_printf("Solving: %ld", tdata->result_initial_count);
tdata->start = nsecs();
//tdata->steps = 0;
tdata->result_complexity = rsolve(grid,&tdata->steps);
tdata->steps_total += tdata->steps;
tdata->steps = 0;
tdata->solved_count++;
if(tdata->result_complexity == 0){
//print_grid(grid,true);
//exit(5);
//footer_printf("thread %d failed validation",tdata->id);
}else{
//footer_printf("thread %d solved: %d",tdata->id, tdata->result_complexity);
}
//if(tdata->solution)
// free(tdata->solution);
WITH_MUTEX({
memcpy(tdata->solution,grid,N*N*sizeof(int));
memcpy(tdata->puzzle,grid_game,N*N*sizeof(int));
},false);
if(tdata->result_complexity >= tdata->complexity){
break;
}else{
free(grid_game);
grid_reset(grid);
}
}
return grid_game;
}
void * generate_game(void * arg){
thread_data_t * tdata = (thread_data_t *)arg;
tick();
//unsigned int * result_complexity = (int *)calloc(sizeof(int),1);
//unsigned int * result_initial_count = (int *)calloc(sizeof(int),1);
//rr_disable_stdout();
int * puzzle = grid_with_minimal_complexity(tdata);
//if(tdata->puzzle){
//free(tdata->puzzle);
//}
WITH_MUTEX({
tdata->finish = nsecs();
tdata->duration = tdata->finish - tdata->start;
tdata->is_done = true;
},false);
return NULL;
}
void thread_data_to_json_object(rjson_t * json ,thread_data_t * data){
rjson_object_start(json);
rjson_kv_int(json,"id",data->id);
rjson_kv_int(json,"solved_count",data->solved_count);
rjson_kv_number(json,"steps_total",data->steps_total + data->steps);
rjson_kv_number(json,"steps",data->steps);
rjson_kv_number(json,"result_initial_count",data->result_initial_count);
rjson_kv_duration(json,"start",data->start);
rjson_kv_duration(json,"finish",data->finish);
rjson_kv_duration(json,"duration",nsecs() - data->start);
rjson_kv_string(json,"puzzle",grid_to_string(data->puzzle));
rjson_kv_string(json,"solution",grid_to_string(data->solution));
rjson_object_close(json);
}
char * thread_data_to_json(thread_data_t * data, int runner_count){
rjson_t * json = rjson();
rjson_array_start(json);
for(int i = 0; i < runner_count; i++){
thread_data_to_json_object(json,&data[i]);
}
rjson_array_close(json);
char * content = strdup(json->content);
rjson_free(json);
return content;
}
char * thread_data_to_string(thread_data_t * data){
static char result[4096];
memset(result,0,sizeof(result));
sprintf(result,"id:%d\tcomplexity: %u\t total solved: %d \tsteps total: %s\tsteps current: %s\tinitc: %d\ttime: %s",
data->id,
data->result_complexity,
data->solved_count,
rtempc(rformat_number(data->steps_total + data->steps)),
rtempc(rformat_number(data->steps)),
data->result_initial_count,
format_time(nsecs() - data->start)
);
return result;
}
char * runner_status_to_string(thread_data_t * runners, unsigned int runner_count){
static char result[1024*1024];
memset(result,0,sizeof(result));
for(uint i = 0; i < runner_count; i++){
strcat(result,thread_data_to_string(&runners[i]));
strcat(result,"\n");
}
return result;
}
typedef struct serve_arguments_t {
thread_data_t * runners;
unsigned int runner_count;
int port;
nsecs_t time_winner;
int puzzle_winner[N][N];
int solution_winner[N][N];
char start_timestamp[30];
} serve_arguments_t;
serve_arguments_t serve_arguments;
void get_totals(thread_data_t * runners, unsigned int runner_count, ulong * steps_total, ulong * solved_total,nsecs_t * longest_running){
*steps_total = 0;
*solved_total = 0;
*longest_running = 0;
nsecs_t end_time = nsecs();
for(unsigned int i = 0; i < runner_count; i++){
*steps_total += runners[i].steps_total + runners[i].steps;
*solved_total += runners[i].solved_count;
nsecs_t duration = runners[i].start ? end_time - runners[i].start : 0;
if(duration > *longest_running){
*longest_running = duration;
}
}
}
void http_response(rhttp_request_t * r, char * content){
char headers[strlen(content) + 1000];
sprintf(headers,"HTTP/1.1 200 OK\r\n"
"Content-Length:%ld\r\n"
"Content-Type: text/html\r\n"
"Connection: close\r\n\r\n%s",
strlen(content),content);
rhttp_send_drain(r->c,headers,0);
close(r->c);
}
int request_handler_json_processes(rhttp_request_t*r){
if(!strncmp(r->path,"/json/processes",strlen("/json/processes"))){
WITH_MUTEX({
char * content = thread_data_to_json(serve_arguments.runners,serve_arguments.runner_count); // runner_status_to_string(serve_arguments.runners,serve_arguments.runner_count);
http_response(r,content);
free(content);
},false);
return 1;
}
return 0;
}
char * statistics_to_json_object(serve_arguments_t * args, rjson_t * json ,thread_data_t * data){
ulong steps_total = 0;
ulong solved_total = 0;
nsecs_t longest_running = 0;
get_totals(args->runners, args->runner_count, &steps_total, &solved_total, &longest_running);
rjson_object_start(json);
rjson_kv_string(json,"start",args->start_timestamp);
rjson_kv_number(json,"steps_total",steps_total);
rjson_kv_number(json,"solved_total",solved_total);
rjson_kv_number(json,"steps_per_puzzle",solved_total != 0 ? steps_total / (solved_total + args->runner_count) : 0);
rjson_kv_string(json,"longest_running",format_time(longest_running));
rjson_kv_string(json,"time_winner",format_time(args->time_winner));
rjson_kv_string(json,"puzzle_winner",grid_to_string(args->puzzle_winner));
rjson_kv_string(json,"solution_winner",grid_to_string(args->solution_winner));
rjson_object_close(json);
return json;
}
int request_handler_json_statistics(rhttp_request_t *r ){
serve_arguments_t args = *(serve_arguments_t *)r->context;
if(!strncmp(r->path, "/json/statistics",strlen("/json/statistics"))){
rjson_t * json = rjson();
statistics_to_json_object(&args,json,args.runners);
char * content = strdup(json->content);
rjson_free(json);
http_response(r,content);
free(content);
return 1;
}
return 0;
}
int request_handler_root(rhttp_request_t *r){
serve_arguments_t args = *(serve_arguments_t *)r->context;
if(!strcmp(r->path,"/")){
WITH_MUTEX({
char * content = runner_status_to_string(args.runners,args.runner_count);
ulong steps_total = 0;
ulong solved_total = 0;
nsecs_t longest_running = 0;
get_totals(args.runners, args.runner_count, &steps_total, &solved_total, &longest_running);
char html[1024*1024*2] = {0};
sprintf(html, "<html><meta http-equiv=\"refresh\" content=\"5\"><head>"
"<body style=\"background-color:black;color:white;\"><pre>"
"%s"
"Started: %s\n"
"\n"
"Steps total: %s\n"
"Solved total: %s\n"
"Steps per puzzle: %s\n"
"Longest running: %s\n"
"Generation time hardest puzzle: %s\n"
"\n"
"Hardest puzzle:\n"
"%s\n"
"Solution:\n"
"%s\n"
"</pre>"
"</body></html>",
content,
args.start_timestamp,
rtempc(rformat_number(steps_total)),
rtempc(rformat_number(solved_total)),
rtempc(rformat_number(solved_total != 0 ? steps_total / (solved_total + args.runner_count) : 0)),
rtempc(format_time(longest_running)),
rtempc(format_time(args.time_winner)),
rtempc(grid_to_string(args.puzzle_winner)),
rtempc(grid_to_string(args.solution_winner))
);
char response[1024*1024*3];
memset(response,0,sizeof(response));
sprintf(response,"HTTP/1.1 200 OK\r\n"
"Content-Length: %zu\r\n"
"Content-Type: text/html\r\n"
"Connection: close:\r\n\r\n",
strlen(html));
rhttp_send_drain(r->c, response,0);
rhttp_send_drain(r->c, html,0);
},true);
return 1;
}
return 0;
}
int request_handler_empty(rhttp_request_t * r){
if(!strncmp(r->path,"/empty",strlen("/empty"))){
int grid[N][N];
memset(grid,0,N*N*sizeof(int));
char * content = grid_to_string(grid);
char response[1024];
response[0] = 0;
sprintf(
response,
"HTTP/1.1 200 OK\r\nContent-Length:%zu\r\nConnection: close\r\n\r\n",
strlen(content)
);
rhttp_send_drain(r->c,response,0);
rhttp_send_drain(r->c,content,0);
return 1;
}
return 0;
}
int request_handler_404(rhttp_request_t *r){
char content[] = "HTTP/1.1 404 Document not found\r\nContent-Length:3\r\nConnection:close\r\n\r\n404";
rhttp_send_drain(r->c,content,0);
return 1;
}
int request_handler(rhttp_request_t * r){
rhttp_request_handler_t request_handlers[] ={
request_handler_root,
request_handler_empty,
request_handler_json_processes,
request_handler_json_statistics,
rhttp_file_request_handler,
request_handler_404,
NULL
};
int i = -1;
while(request_handlers[++i])
if(request_handlers[i](r))
return 1;
return 0;
}
void * serve_thread(void *arg){
serve_arguments_t * arguments = (serve_arguments_t *)arg;
rhttp_serve("0.0.0.0",arguments->port,1024,1,1,request_handler,(void *)arguments);
return NULL;
}
void generate_games(unsigned int game_count, unsigned int timeout, unsigned int complexity){
pthread_t thread_serve;
thread_data_t runners[game_count];
serve_arguments.runners = runners;
serve_arguments.runner_count = game_count;
serve_arguments.port = 9999;
serve_arguments.time_winner = 0;
strcpy(serve_arguments.start_timestamp,rstrtimestamp());
memset(serve_arguments.solution_winner,0,sizeof(serve_arguments.solution_winner));
memset(serve_arguments.puzzle_winner,0,sizeof(serve_arguments.puzzle_winner));
pthread_create(&thread_serve,0,serve_thread,(void *)&serve_arguments);
//pthread_mutex_t lock;
//pthread_mutex_init(&lock,NULL);
for(unsigned int i = 0; i < game_count; i++){
runners[i].initial_count_maximum = 30;
runners[i].initial_count_minimum = 1;
runners[i].complexity = complexity;
runners[i].is_done = false;
runners[i].id = i;
memset(runners[i].solution,0,N*N*sizeof(int));
memset(runners[i].puzzle,0,N*N*sizeof(int));
runners[i].solved_count = 0;
runners[i].duration = 0;
runners[i].steps = 0;
runners[i].steps_total = 0;
//runners[i].lock = lock;
pthread_create(&runners[i].thread,NULL,generate_game,(void *)(&runners[i]));
}
unsigned int highest_complexity = complexity;
for(unsigned int i = 0; i < timeout; i++){
sleep(1);
WITH_MUTEX({
footer_printf("main");
//pthread_mutex_lock(&lock);
for(unsigned int ithread = 0; ithread < game_count; ithread++){
if(runners[ithread].is_done){
pthread_join(runners[ithread].thread,NULL);
if(runners[ithread].result_complexity > highest_complexity){
highest_complexity = runners[ithread].result_complexity;
for(uint j = 0; j < game_count; j++){
runners[j].complexity = highest_complexity;
}
printf("\r\n");
print_grid(runners[ithread].puzzle,true);
printf("\n");
print_grid(runners[ithread].solution,false);
memcpy(serve_arguments.puzzle_winner,runners[ithread].puzzle,N*N*sizeof(int));
memcpy(serve_arguments.solution_winner,runners[ithread].solution,N*N*sizeof(int));
serve_arguments.time_winner = runners[ithread].duration;
printf("Thread %d is done (%s)\n",ithread,format_time(runners[ithread].duration));
printf("Complexity: %ld\n",runners[ithread].result_complexity);
printf("Initial values: %ld\n",runners[ithread].result_initial_count);
}
runners[ithread].is_done = false;
//free(runners[ithread].puzzle);
//runners[ithread].puzzle = NULL;
//free(runners[ithread].solution);
//runners[ithread].solution = NULL;
pthread_create(&runners[ithread].thread,NULL,generate_game,(void *)(&runners[ithread]));
}
}
//pthread_mutex_unlock(&lock);
},true);
}
for(unsigned int i = 0; i < game_count; i++){
pthread_cancel(runners[i].thread);
}
pthread_testcancel();
}
int main() {
setbuf(stdout,NULL);
srand(time(NULL));
// setbuf(stdout,0);
int cores = sysconf(_SC_NPROCESSORS_ONLN);
int threads = cores * 4 - 1;
sprintf(footer_prefix, "Cores: %d - Threads: %d - ", cores,threads);
// Highest: 1481563980
generate_games(threads ,13371337,1);
exit(0);
/*
0 0 9 0 0 0 0 0 0
0 0 0 0 0 0 3 0 0
6 0 1 5 0 2 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 8 0 0 0
9 0 4 7 0 0 0 0 5
0 0 7 8 0 0 0 0 0
0 0 8 0 0 0 0 0 0
0 4 0 0 3 0 0 6 2
Attempts:
213212476
*/
/*
0 0 0 0 0 0 0 0 0
0 0 0 5 0 0 0 0 0
0 0 0 7 0 2 0 0 0
0 0 0 0 0 1 0 0 0
0 0 4 0 0 0 0 9 3
0 0 0 0 0 8 0 0 0
0 0 0 4 0 5 0 0 0
0 0 0 0 0 0 0 0 2
0 0 8 3 0 0 0 0 5
Thread 2 is done (459.04s)
Complexity: 125004041
Initial values: 14
*/
/*
0 0 0 0 0 0 0 0 0
0 8 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 6 5 0
0 0 4 0 0 0 9 0 0
0 0 0 2 0 0 0 0 0
0 0 0 0 0 0 0 0 0
0 0 0 0 4 0 0 9 1
0 0 0 0 6 5 0 0 0
Thread 2 is done (1528.38s)
Complexity: 748250144
Initial values: 11
*/
/*0 3 0 0 0 0 0 0 0
0 0 5 0 0 0 0 0 0
0 0 0 0 0 0 1 0 0
0 0 0 0 0 0 0 4 0
0 0 0 0 9 0 0 0 0
0 0 9 0 3 0 0 0 0
0 0 0 0 0 6 0 0 0
0 0 4 0 0 0 0 7 0
0 6 7 0 0 4 0 2 0
Thread 0 is done (2635.64s)
Complexity: 1481563980
Initial values: 14*/
/*
GROOTSTE!
Complexity: 774858414
0 0 0 4 0 0 9 0 0
0 0 0 0 0 0 0 0 0
0 2 0 5 0 8 0 0 0
5 0 2 0 0 0 0 0 3
0 0 0 0 0 0 0 0 0
0 7 0 0 0 0 0 0 0
0 9 0 0 0 0 4 3 0
2 0 0 0 5 7 0 6 0
0 0 7 1 0 0 2 0 0
Generated in: 1393.95s
*/
/*
ONEINDIG?
0 2 0 0 0 0 0 0 0
0 0 4 0 0 0 0 0 0
6 0 1 8 0 0 0 0 0
3 0 6 0 0 0 8 0 0
0 0 0 0 3 0 2 4 0
0 4 0 1 0 2 5 0 0
7 0 0 2 0 0 1 6 8
0 3 0 0 0 0 7 2 4
8 0 0 0 0 6 0 0 5
*/
/*
0 0 0 0 3 7 0 0 0
0 0 8 0 0 2 5 0 3
0 2 0 8 4 0 0 0 0
0 0 6 0 0 0 0 0 0
0 0 0 0 0 0 0 0 7
0 0 0 2 0 0 0 6 0
0 0 0 0 0 0 6 0 5
0 0 0 6 8 0 0 4 0
4 0 0 1 0 0 0 0 0
*/
__attribute_maybe_unused__ int grid_empty[N][N] = {
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0}
};
}

5
sudoku_n/.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,5 @@
{
"files.associations": {
"sudoku.h": "c"
}
}

8
sudoku_n/Makefile Normal file
View File

@ -0,0 +1,8 @@
all: build run
build:
gcc sudoku3.c -o sudoku3 -Ofast -Werror -Wall -Wextra
run:
./sudoku3

BIN
sudoku_n/a.out Executable file

Binary file not shown.

8450
sudoku_n/rlib.h Normal file

File diff suppressed because it is too large Load Diff

92
sudoku_n/sudoku,h Normal file
View File

@ -0,0 +1,92 @@
#ifndef SUDOKU_H
#define SUDOKU_H
typedef struct field_t {
unsigned int value;
unsigned int selected;
unsigned int initial;
unsigned int column;
unsigned int row;
unsigned int index;
unsigned char box;
} field_t;
field_t * new_field(){
field_t * field = (field_t *)calloc(1,sizeof(field_t));
return field;
}
typedef struct row_t {
unsigned int index;
field_t * columns;
unsigned int column_count;
} row_t;
row_t * new_row(unsigned int size) {
row_t * row = (row_t *)calloc(1, sizeof(row));
row->columns = calloc(size,sizeof(field_t));
row->index = 0;
return row;
}
typedef struct grid_t {
unsigned int size;
unsigned int row_count;
unsigned int field_count;
row_t * rows;
field_t * fields;
field_t * (*get)(struct grid_t * grid, unsigned int row, unsigned int col);
field_t * (*get_empty_field)(struct grid_t * grid);
} grid_t;
field_t * grid_get(grid_t * grid, unsigned int row, unsigned int col){
return &grid->rows[row].columns[col];
}
field_t * grid_get_empty_field(grid_t * grid){
for(int i = 0; i < grid->size * grid->size; i++){
printf("AT:%d\n",i);
printf("V:%d\n",grid->fields[i].value);
printf("V2:%d\n",grid->rows[i].columns[i].value);
printf("V2:%d\n",grid->get(grid,i,i)->value);
if(grid->fields[i].value == 0)
return &grid->fields[i];
}
return NULL;
}
char get_box_letter(grid_t * grid, unsigned int row, unsigned int col){
//char box_letter = 65 + (row)*grid->size / grid->size / 3 + row; // (row*grid->size+col)+(grid->size / 3);// + grid->size % (grid->size / 3));
char box_letter = 65 + ((row % (grid->size / 3))) + grid->size % (grid->size / 3);// (row-1)*grid->size+(col-1)+(row-1)+(col-1);
//(65 + row * (grid->size / 3) % (grid->size / 3)+col); /// (grid->size / 3));
return box_letter;
}
grid_t * new_grid(unsigned int size){
grid_t * grid = (grid_t *)calloc(1,sizeof(grid_t));
grid->get = grid_get;
grid->get_empty_field = grid_get_empty_field;
grid->size = size;
grid->row_count = 0;
grid->rows = (row_t *)calloc(size, sizeof(row_t));
grid->fields = (field_t *)calloc(size*size, sizeof(field_t));
for(unsigned int irow = 0; irow < size; irow++){
row_t * row = new_row(size);
row->index = grid->row_count;
row->column_count = size;
grid->row_count++;
grid->rows[row->index] = *row;
for(unsigned int icol = 0; icol < size; icol++){
unsigned int field_index = irow * size + icol;
field_t * field = new_field();
field->column = icol;
field->row = irow;
field->index = field_index;
field->box = get_box_letter(grid, irow, icol);
row->columns[icol] = *field;
grid->fields[field_index] = *field;
grid->field_count++;
}
}
return grid;
}
#endif

6
sudoku_n/sudoku.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef SUDOKU_H
#define SUDOKU_H
#include "sudoku_header.h"
#include "sudoku_format.h"
#include "sudoku_generate.h"
#endif

BIN
sudoku_n/sudoku3 Executable file

Binary file not shown.

31
sudoku_n/sudoku3.c Normal file
View File

@ -0,0 +1,31 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sudoku.h"
int main(){
grid_t * grid = new_grid(48);
printf("%d \n",grid->rows[3].index);
printf("%d \n",grid->fields[30].index);
printf("%d \n",grid->rows[6].columns[6].index);
field_t * f = grid->get(grid, 0,3);
printf("%d \n", f->index);
//for(int i = 0; i < grid->size*grid->size;i++){
// printf("%c",grid->fields[i].box);
// }
grid->get(grid,5,5)->value = 3;
grid->get(grid,0,0)->value = 1;
grid->fields[0].value = 4;
printf("**%d**\n",grid->get(grid,5,5)->index);
printf("^^%d^^\n",grid->get(grid,0,0)->index);
printf("!!%d!!\n", grid->get_empty_field(grid)->index);
printf("Computer format:\n%s\n",grid_to_string(grid,row_to_string,field_to_string));
printf("Human format:\n%s\n",grid_to_string_human(grid));
printf("C format:\n%s\n",grid_to_string_c(grid));
grid = generate_sudoku(48);
printf("C format:\n%s\n",grid_to_string_c(grid));
return 0;
}

View File

@ -0,0 +1,32 @@
#ifndef SUDOKU_ALGORITHM_H
#define SUDOKU_ALGORITHM_H
#include "sudoku.h"
#include "rlib.h"
#include <stdint.h>
#define uin unsigned int
unsigned int rand_int(int min, int max){
return rand() % (max - min + 1) + min;
}
double count_neighbors(grid_t * grid, uint row, uint col) {
double count = 0.0;
for(uint i = 0; i < row; i++){
for(uint j = 0; j < grid->size; j++){
if(grid->get(grid,row,j)->value != 0 && j != col)
count += 1; //grid[row][j].initial ? 1.1 : 1.0;
}
for(uint j = 0; j < grid->size; j++){
if(grid->get(grid,j,col)->value!= 0 && j != row)
count += 1; //grid[j][col].initial ? 1.1 : 1.0;
}
}
return count;
}
#endif

86
sudoku_n/sudoku_format.h Normal file
View File

@ -0,0 +1,86 @@
#ifndef SUDOKU_FORMAT_H
#define SUDOKU_FORMAT_H
#include "sudoku_header.h"
#include <string.h>
#include "rlib.h"
#define SUDOKU_FORMAT_BUFFER_SIZE 90000
char * field_to_string_human(field_t * field){
static char result[SUDOKU_FORMAT_BUFFER_SIZE] = {0};
result[0] = 0;
sprintf(result,"%d ",field->value);
return sbuf(result);
}
char * field_to_string_comma(field_t * field){
static char result[SUDOKU_FORMAT_BUFFER_SIZE] = {0};
result[0] = 0;
sprintf(result,"%d, ",field->value);
return sbuf(result);
}
char * field_to_string(field_t * field){
static char result[SUDOKU_FORMAT_BUFFER_SIZE] = {0};
result[0] = 0;
sprintf(result,"%d",field->value);
return sbuf(result);
}
char * row_to_string(row_t * row,char * field_string_fn(field_t * field)){
static char result[SUDOKU_FORMAT_BUFFER_SIZE] = {0};
result[0] = 0;
for(uint i = 0; i < row->column_count; i++){
strcat(result, field_string_fn(&row->columns[i]));
}
return sbuf(result);
}
char * row_to_string_human(row_t * row,char * field_string_fn(field_t * field)){
static char result[SUDOKU_FORMAT_BUFFER_SIZE] = {0};
result[0] = 0;
for(uint i = 0; i < row->column_count; i++){
strcat(result, field_string_fn(&row->columns[i]));
}
result[strlen(result)-2] = 0;
strcat(result, "\n");
return sbuf(result);
}
char * row_to_string_c(row_t * row){
static char result[SUDOKU_FORMAT_BUFFER_SIZE] = {0};
result[0] = 0;
strcat(result,"{");
for(uint i = 0; i < row->column_count; i++){
strcat(result, field_to_string_comma(&row->columns[i]));
}
result[strlen(result)-2] = 0;
strcat(result,"},\n");
return sbuf(result);
}
char * grid_to_string(grid_t * grid, char * row_to_string_fn(row_t * row,char * (field_t *)), char * field_string_fn(field_t * field)){
static char result[SUDOKU_FORMAT_BUFFER_SIZE] = {0};
result[0] = 0;
for(uint irow = 0; irow < grid->size; irow++){
row_t row = grid->rows[irow];
row.column_count++;
strcat(result,row_to_string_fn(&row,field_string_fn));
}
result[strlen(result)-1] = 0;
return sbuf(result);
}
char * grid_to_string_c(grid_t * grid){
char result[SUDOKU_FORMAT_BUFFER_SIZE] = {0};
result[0] = 0;
strcat(result,"{\n");
for(uint i = 0; i < grid->size; i++){
strcat(result," ");
strcat(result,row_to_string_c(&grid->rows[i]));
}
result[strlen(result)-2] = 0;
strcat(result,"\n}");
return sbuf(result);
}
char * grid_to_string_human(grid_t * grid){
return grid_to_string(grid, row_to_string_human, field_to_string_human);
}
#endif

View File

@ -0,0 +1,38 @@
#ifndef SUDOKU_GENERATE_H
#define SUDOKU_GENERATE_H
#include <time.h>
#include <stdlib.h>
#include "sudoku_header.h"
#include "sudoku_algorithm.h"
field_t * grid_get_random_empty_field(grid_t * grid){
while(true){
unsigned int row = rand_int(0,grid->size-1);
unsigned int col = rand_int(0,grid->size-1);
field_t * found = grid_get(grid,row,col);
if(found->value == 0)
return found;
}
}
void grid_set_random_field(grid_t * grid){
field_t * field = grid_get_random_empty_field(grid);
field->value = rand_int(1,grid->size);
field->initial = true;
}
grid_t * generate_sudoku(unsigned int size){
srand(time(NULL));
grid_t * grid = new_grid(size);
for(unsigned int i = 0; i < rand_int(17,20); i++){
grid_set_random_field(grid);
}
return grid;
}
#endif

92
sudoku_n/sudoku_header.h Normal file
View File

@ -0,0 +1,92 @@
#ifndef SUDOKU_HEADER_H
#define SUDOKU_HEADER_H
#include "rlib.h"
#include <stdlib.h>
#include <stdio.h>
typedef struct field_t {
unsigned int value;
unsigned int selected;
unsigned int initial;
unsigned int column;
unsigned int row;
unsigned int index;
unsigned char box;
} field_t;
field_t * new_field(){
field_t * field = (field_t *)calloc(1,sizeof(field_t));
return field;
}
typedef struct row_t {
unsigned int index;
field_t * columns;
unsigned int column_count;
} row_t;
row_t * new_row(unsigned int size) {
row_t * row = (row_t *)calloc(sizeof(row_t),1);
row->columns = calloc(size,sizeof(field_t));
row->index = 0;
return row;
}
typedef struct grid_t {
unsigned int size;
unsigned int row_count;
unsigned int field_count;
row_t * rows;
field_t * fields;
field_t * (*get)(struct grid_t * grid, unsigned int row, unsigned int col);
field_t * (*get_empty_field)(struct grid_t * grid);
} grid_t;
field_t * grid_get(grid_t * grid, unsigned int row, unsigned int col){
return &grid->rows[row].columns[col];
}
field_t * grid_get_empty_field(grid_t * grid){
for(uint i = 0; i < grid->size * grid->size; i++){
if(grid->fields[i].value == 0)
return &grid->fields[i];
}
return NULL;
}
/*
char get_box_letter(grid_t * grid, unsigned int row, unsigned int col){
//char box_letter = 65 + (row)*grid->size / grid->size / 3 + row; // (row*grid->size+col)+(grid->size / 3);// + grid->size % (grid->size / 3));
char box_letter = 65 + ((row % (grid->size / 3))) + grid->size % (grid->size / 3);// (row-1)*grid->size+(col-1)+(row-1)+(col-1);
//(65 + row * (grid->size / 3) % (grid->size / 3)+col); /// (grid->size / 3));
return box_letter;
}*/
grid_t * new_grid(unsigned int size){
grid_t * grid = (grid_t *)calloc(1,sizeof(grid_t));
grid->get = grid_get;
grid->get_empty_field = grid_get_empty_field;
grid->size = size;
grid->row_count = 0;
grid->rows = (row_t *)calloc(size, sizeof(row_t));
grid->fields = (field_t *)calloc(size*size, sizeof(field_t));
unsigned int field_index = 0;
for(unsigned int irow = 0; irow < size; irow++){
row_t * row = new_row(size);
row->index = grid->row_count;
row->column_count = size;
grid->row_count++;
grid->rows[row->index] = *row;
for(unsigned int icol = 0; icol < size; icol++){
field_t * field = new_field();
field->column = icol;
field->row = irow;
field->index = field_index;
//field->box = get_box_letter(grid, irow, icol);
row->columns[icol] = *field;
grid->fields[field_index] = *field;
grid->field_count++;
field_index++;
}
}
return grid;
}
#endif

View File

@ -0,0 +1,66 @@
#include "rlib.h"
#include "sudoku.h"
#include <pthread.h>
char * xx = "aa";
void * thread_worker(){
tick();
for(uint i = 0; i< 1000; i++){
grid_t * grid = new_grid(18);
grid_to_string_c(grid);
// printf("%d\n",i);
sbuf(xx);
//strcpy(xx,"AAA");
}
return NULL;
}
void bench_rtemp_c(){
grid_t * grid = new_grid(18);
char * to_copy = sbuf(grid_to_string_c(grid));
uint times_bench = 10;
uint times_function = 10000000;
printf("Benchmark %d*%d with/without thread lock.\n",times_bench,times_function);
RBENCH(times_bench,{
printf("with: ");
RBENCH(times_function,{
sbuf(sbuf(sbuf(to_copy)));
});
rtempc_use_mutex = false;
printf("witout: ");
RBENCH(10000000,{
sbuf(sbuf(sbuf(to_copy)));
});
});
}
int main(){
//rtempc_use_mutex = false;
bench_rtemp_c();
rtempc_use_mutex = true;
uint worker_count = 4;
pthread_t workers[worker_count];
printf("Threading: \n");
RBENCH(1,{
for(uint i = 0; i < worker_count; i++){
pthread_create(&workers[i],0,thread_worker,NULL);
}
for(uint i = 0; i < worker_count; i++){
pthread_join(workers[i],NULL);
}
});
printf("Without threading: \n");
RBENCH(1,{
for(uint i = 0; i < worker_count; i++){
thread_worker();
}
});
return 0;
}

15
test.c Normal file
View File

@ -0,0 +1,15 @@
#include "rlib.h"
#include "sudoku.h"
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <pthread.h>
#include "footer.h"
int main() {
return 0;
}

1
test.h Normal file
View File

@ -0,0 +1 @@
#include "rlib.h"

39
web.c Normal file
View File

@ -0,0 +1,39 @@
#include <rlib.h>
#include <pthread.h>
int request_handler(rhttp_request_t * r) {
rhttp_send_drain(r->c, "HTTP/1.1 200 OK\r\n"
"Content-Length: 1\r\n"
"Connection: close\r\n\r\n"
"A",0);
close(r->c);
}
void * serve_thread(void * arg){
int port = *(int*)arg;
printf("Serving on port: %d\n", port);
rhttp_serve("0.0.0.0", port, 1024, 1, 1, request_handler);
return NULL;
}
pthread_t server_thread;
void serve(int port){
printf("Serving on port: %d\n", port);
int pport = port;
}
int main() {
int port = 8889;
//serve(port);;
pthread_create(&server_thread, 0, serve_thread,(void *)&port);
pthread_join(server_thread, NULL);
while(true){
printf("Loop\n");
sleep(1);
}
return 0;
}