first commit
This commit is contained in:
commit
e42e873456
30
Makefile
Normal file
30
Makefile
Normal 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
41
NOTES.md
Normal 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
|
||||||
|
It’s 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 it’s 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
66
README.md
Normal 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
|
270
app.js
Normal file
270
app.js
Normal 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
100
box.c
Normal 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
99
footer.h
Normal 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
56
index.html
Normal 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>
|
282
rsolve.h
Normal file
282
rsolve.h
Normal 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
|
277
solve.c
Normal file
277
solve.c
Normal 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;
|
||||||
|
}
|
333
sudoku.c
Normal file
333
sudoku.c
Normal 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
252
sudoku.h
Normal 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
711
sudoku.html
Normal 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
850
sudoku.js
Normal 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() : ' '
|
||||||
|
|
||||||
|
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
BIN
sudoku.zip
Normal file
Binary file not shown.
28
sudoku2.c
Normal file
28
sudoku2.c
Normal 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
83
sudoku2.html
Normal 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
189
sudoku3.c
Normal 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
83
sudoku3.html
Normal 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
31
sudoku4.html
Normal 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
BIN
sudoku_gen1
Executable file
Binary file not shown.
574
sudoku_gen1.c
Normal file
574
sudoku_gen1.c
Normal 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
5
sudoku_n/.vscode/settings.json
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"files.associations": {
|
||||||
|
"sudoku.h": "c"
|
||||||
|
}
|
||||||
|
}
|
8
sudoku_n/Makefile
Normal file
8
sudoku_n/Makefile
Normal 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
BIN
sudoku_n/a.out
Executable file
Binary file not shown.
8450
sudoku_n/rlib.h
Normal file
8450
sudoku_n/rlib.h
Normal file
File diff suppressed because it is too large
Load Diff
92
sudoku_n/sudoku,h
Normal file
92
sudoku_n/sudoku,h
Normal 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
6
sudoku_n/sudoku.h
Normal 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
BIN
sudoku_n/sudoku3
Executable file
Binary file not shown.
31
sudoku_n/sudoku3.c
Normal file
31
sudoku_n/sudoku3.c
Normal 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;
|
||||||
|
}
|
32
sudoku_n/sudoku_algorithm.h
Normal file
32
sudoku_n/sudoku_algorithm.h
Normal 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
86
sudoku_n/sudoku_format.h
Normal 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
|
38
sudoku_n/sudoku_generate.h
Normal file
38
sudoku_n/sudoku_generate.h
Normal 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
92
sudoku_n/sudoku_header.h
Normal 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
|
66
sudoku_n/sudoku_thread_test.c
Normal file
66
sudoku_n/sudoku_thread_test.c
Normal 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
15
test.c
Normal 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;
|
||||||
|
}
|
39
web.c
Normal file
39
web.c
Normal 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;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user