Skip to content

Commit e25444a

Browse files
authored
Merge pull request #6 from Jayllyz/feature/darkUI&clean
2 parents eae17b6 + b4c478d commit e25444a

File tree

5 files changed

+153
-90
lines changed

5 files changed

+153
-90
lines changed

src/main.rs

Lines changed: 24 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
use actix_web::{get, web, App, HttpResponse, HttpServer, Responder};
22
use lazy_static::lazy_static;
3-
use rand::Rng;
43
use std::sync::Mutex;
54
use tera::{Context, Tera};
5+
mod sudoku;
66

77
const BOARD_SIZE: usize = 9;
8-
const SQUARE_SIZE: usize = 3;
9-
108
struct Sudoku {
119
pub board: Mutex<Vec<Vec<usize>>>,
1210
}
@@ -47,7 +45,7 @@ async fn update_table(
4745
difficulty: web::Path<usize>,
4846
) -> impl Responder {
4947
let difficulty = difficulty.into_inner();
50-
let board = generate(BOARD_SIZE, difficulty);
48+
let board = sudoku::generate(BOARD_SIZE, difficulty);
5149
app_state.set_board(board.clone());
5250

5351
let mut context = Context::new();
@@ -62,7 +60,7 @@ async fn update_table(
6260

6361
async fn solve_table(tera: web::Data<Tera>, data: web::Data<Sudoku>) -> impl Responder {
6462
let mut board = data.board.lock().unwrap().clone();
65-
resolv_backtrack(&mut board, 0, 0);
63+
sudoku::resolv_backtrack(&mut board, 0, 0);
6664
data.set_board(board.clone());
6765

6866
let mut context = Context::new();
@@ -106,74 +104,28 @@ async fn main() -> std::io::Result<()> {
106104
.await
107105
}
108106

109-
pub fn generate(size: usize, difficulty: usize) -> Vec<Vec<usize>> {
110-
let mut board = vec![vec![0; size]; size];
111-
let mut rng = rand::thread_rng();
112-
let luck: f64;
113-
match difficulty {
114-
1 => luck = 0.4,
115-
2 => luck = 0.45,
116-
3 => luck = 0.5,
117-
_ => luck = 0.4,
118-
}
119-
resolv_backtrack(&mut board, 0, 0); // generate a valid board
120-
for i in 0..size {
121-
for j in 0..size {
122-
if rng.gen_bool(luck) {
123-
board[i][j] = 0;
107+
#[cfg(test)]
108+
mod tests {
109+
use crate::{sudoku::generate, sudoku::resolv_backtrack};
110+
111+
#[test]
112+
fn board_valid() {
113+
const BOARD_SIZE: usize = 9;
114+
let board = generate(BOARD_SIZE, 1);
115+
assert_eq!(board.len(), 9);
116+
117+
let mut hm = std::collections::HashMap::new();
118+
for i in 0..BOARD_SIZE {
119+
for j in 0..BOARD_SIZE {
120+
if hm.contains_key(&board[i][j]) {
121+
assert!(false);
122+
}
123+
if board[i][j] != 0 {
124+
hm.insert(board[i][j], true);
125+
}
124126
}
127+
hm.clear();
125128
}
129+
assert_eq!(resolv_backtrack(&mut board.clone(), 0, 0), true);
126130
}
127-
board
128-
}
129-
pub fn is_num_valid(board: &Vec<Vec<usize>>, row: usize, col: usize, num: usize) -> bool {
130-
for i in 0..board.len() {
131-
if board[row][i] == num || board[i][col] == num {
132-
return false;
133-
}
134-
}
135-
136-
let sub_row = (row / SQUARE_SIZE) * SQUARE_SIZE;
137-
let sub_col = (col / SQUARE_SIZE) * SQUARE_SIZE;
138-
for i in 0..SQUARE_SIZE {
139-
for j in 0..SQUARE_SIZE {
140-
if board[sub_row + i][sub_col + j] == num {
141-
return false;
142-
}
143-
}
144-
}
145-
true
146-
}
147-
148-
// backtracking algorithm
149-
// https://en.wikipedia.org/wiki/Sudoku_solving_algorithms#Backtracking
150-
// inspired by https://gist.github.com/raeffu/8331328
151-
152-
pub fn resolv_backtrack(board: &mut Vec<Vec<usize>>, mut row: usize, mut col: usize) -> bool {
153-
if col == board.len() {
154-
col = 0;
155-
row += 1;
156-
if row == board.len() {
157-
// end of board
158-
return true;
159-
}
160-
}
161-
162-
if board[row][col] != 0 {
163-
return resolv_backtrack(board, row, col + 1);
164-
}
165-
166-
for num in 1..=board.len() {
167-
if is_num_valid(board, row, col, num) {
168-
board[row][col] = num;
169-
if resolv_backtrack(board, row, col + 1) {
170-
// found a number
171-
return true;
172-
}
173-
// backtrack
174-
board[row][col] = 0;
175-
}
176-
}
177-
178-
false
179131
}

src/sudoku.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
use rand::Rng;
2+
3+
const SQUARE_SIZE: usize = 3;
4+
5+
pub fn generate(size: usize, difficulty: usize) -> Vec<Vec<usize>> {
6+
let mut board = vec![vec![0; size]; size];
7+
let mut rng = rand::thread_rng();
8+
let luck: f64;
9+
match difficulty {
10+
1 => luck = 0.4,
11+
2 => luck = 0.5,
12+
3 => luck = 0.6,
13+
_ => luck = 0.4,
14+
}
15+
resolv_backtrack(&mut board, 0, 0); // generate a valid board
16+
for i in 0..size {
17+
for j in 0..size {
18+
if rng.gen_bool(luck) {
19+
board[i][j] = 0;
20+
}
21+
}
22+
}
23+
board
24+
}
25+
pub fn is_num_valid(board: &Vec<Vec<usize>>, row: usize, col: usize, num: usize) -> bool {
26+
for i in 0..board.len() {
27+
if board[row][i] == num || board[i][col] == num {
28+
return false;
29+
}
30+
}
31+
32+
let sub_row = (row / SQUARE_SIZE) * SQUARE_SIZE;
33+
let sub_col = (col / SQUARE_SIZE) * SQUARE_SIZE;
34+
for i in 0..SQUARE_SIZE {
35+
for j in 0..SQUARE_SIZE {
36+
if board[sub_row + i][sub_col + j] == num {
37+
return false;
38+
}
39+
}
40+
}
41+
true
42+
}
43+
44+
// backtracking algorithm
45+
// https://en.wikipedia.org/wiki/Sudoku_solving_algorithms#Backtracking
46+
// inspired by https://gist.github.com/raeffu/8331328
47+
48+
pub fn resolv_backtrack(board: &mut Vec<Vec<usize>>, mut row: usize, mut col: usize) -> bool {
49+
if col == board.len() {
50+
col = 0;
51+
row += 1;
52+
if row == board.len() {
53+
// end of board
54+
return true;
55+
}
56+
}
57+
58+
if board[row][col] != 0 {
59+
return resolv_backtrack(board, row, col + 1);
60+
}
61+
62+
for num in 1..=board.len() {
63+
if is_num_valid(board, row, col, num) {
64+
board[row][col] = num;
65+
if resolv_backtrack(board, row, col + 1) {
66+
// found a number
67+
return true;
68+
}
69+
// backtrack
70+
board[row][col] = 0;
71+
}
72+
}
73+
74+
false
75+
}

styles/main.css

Lines changed: 48 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,18 @@
22
box-sizing: border-box;
33
}
44

5+
body {
6+
display: flex;
7+
flex-direction: column;
8+
margin: auto;
9+
min-height: 100vh;
10+
background-color: #131516 !important;
11+
}
12+
513
h1 {
614
text-align: center;
15+
color: #dd4814;
16+
margin-top: 3rem;
717
}
818

919
table {
@@ -17,17 +27,18 @@ td.sudoku-case[data-value='0'] {
1727
}
1828

1929
td {
20-
border: 1px solid black;
30+
border: 1px solid #8d8272;
2131
width: 40px;
2232
height: 40px;
2333
font-size: 24px;
2434
font-weight: bold;
25-
background-color: #fff;
35+
background-color: #191b1c;
36+
color: #eee;
2637
cursor: pointer;
2738
}
2839

2940
td:hover {
30-
background-color: #eee;
41+
background-color: #2c2f30;
3142
}
3243

3344
.button-container {
@@ -40,9 +51,10 @@ td:hover {
4051
padding: 10px;
4152
font-size: 20px;
4253
font-weight: bold;
43-
color: white;
54+
color: #eee;
4455
border: none;
4556
cursor: pointer;
57+
border-radius: 4px;
4658
}
4759

4860
.easy-button {
@@ -64,9 +76,10 @@ td:hover {
6476
font-size: 20px;
6577
font-weight: bold;
6678
background-color: #4caf50;
67-
color: white;
79+
color: #eee;
6880
border: none;
6981
cursor: pointer;
82+
border-radius: 4px;
7083
}
7184

7285
.generate-button:hover {
@@ -83,38 +96,56 @@ td:hover {
8396
opacity: 0.8;
8497
}
8598

99+
footer {
100+
color: #eee;
101+
padding: 20px;
102+
text-align: center;
103+
font-size: 14px;
104+
margin-top: auto;
105+
background-color: #191b1c !important;
106+
}
107+
108+
footer a {
109+
color: #dd4814;
110+
text-decoration: none;
111+
}
112+
113+
footer a:hover {
114+
text-decoration: underline;
115+
}
116+
86117
tr:nth-child(3n + 1) td:nth-child(3n + 1) {
87-
border-top: 3px solid black;
88-
border-left: 3px solid black;
118+
border-top: 3px solid #8d8272;
119+
border-left: 3px solid #8d8272;
89120
}
90121

91122
tr:nth-child(3n + 1) td:nth-child(3n + 2) {
92-
border-top: 3px solid black;
123+
border-top: 3px solid #8d8272;
93124
}
94125

95126
tr:nth-child(3n + 1) td:nth-child(3n + 3) {
96-
border-top: 3px solid black;
97-
border-right: 3px solid black;
127+
border-top: 3px solid #8d8272;
128+
border-right: 3px solid #8d8272;
98129
}
99130

100131
tr:nth-child(3n + 2) td:nth-child(3n + 1) {
101-
border-left: 3px solid black;
132+
border-left: 3px solid #8d8272;
102133
}
103134

104135
tr:nth-child(3n + 2) td:nth-child(3n + 3) {
105-
border-right: 3px solid black;
136+
border-right: 3px solid #8d8272;
106137
}
107138

108139
tr:nth-child(3n + 3) td:nth-child(3n + 1) {
109-
border-bottom: 3px solid black;
110-
border-left: 3px solid black;
140+
border-bottom: 3px solid #8d8272;
141+
border-left: 3px solid #8d8272;
111142
}
112143

113144
tr:nth-child(3n + 3) td:nth-child(3n + 2) {
114-
border-bottom: 3px solid black;
145+
border-bottom: 3px solid #8d8272;
115146
}
116147

117148
tr:nth-child(3n + 3) td:nth-child(3n + 3) {
118-
border-bottom: 3px solid black;
119-
border-right: 3px solid black;
149+
border-bottom: 3px solid #8d8272;
150+
border-right: 3px solid #8d8272;
120151
}

templates/layout/footer.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<footer>
2+
<p>Made in rust with Actix.</p>
3+
<p>Copyright &copy; <a href="https://github.com/Jayllyz">Jayllyz</a></p>
4+
</footer>

templates/pages/index.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<meta charset="UTF-8" />
55
<title>{{ title }}</title>
66
<meta name="viewport" content="width=device-width, initial-scale=1" />
7-
<link rel="stylesheet" type="text/css" media="screen" href="/static/styles/main.css" />
7+
<link rel="stylesheet" type="text/css" href="/static/styles/main.css" />
88
</head>
99
<body>
1010
<main>
@@ -31,5 +31,6 @@ <h1>{{ title }}</h1>
3131
</form>
3232
</div>
3333
</main>
34+
{% include "layout/footer.html" %}
3435
</body>
3536
</html>

0 commit comments

Comments
 (0)