Skip to content

Commit 44ccf90

Browse files
committed
move all "working" functions to use an array instead of a vector to lower memory overhead
1 parent f7a4e8e commit 44ccf90

File tree

6 files changed

+56
-48
lines changed

6 files changed

+56
-48
lines changed

Cargo.toml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,6 @@ name = "lib-sudoku"
33
version = "1.0.0"
44
edition = "2024"
55

6-
[[bin]]
7-
name = "lib-sudoku"
8-
path = "src/main.rs"
9-
106
[lib]
117
name = "lib_sudoku"
128
crate-type = ["cdylib"]

src/libraries/puzzle_generator.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ pub enum Order {
1212
RANDOM,
1313
}
1414

15-
fn alt_get_possibilities(puzz: &Vec<u8>, pos: u8, order: &Order) -> Vec<u8> {
15+
fn alt_get_possibilities(puzz: &[u8; 81], pos: u8, order: &Order) -> Vec<u8> {
1616
let mut possibilities: Vec<u8> = Vec::new();
1717
let mut seen: [bool; 10] = [false; 10];
1818
let row: usize = (pos / 9) as usize;
@@ -49,7 +49,7 @@ fn alt_get_possibilities(puzz: &Vec<u8>, pos: u8, order: &Order) -> Vec<u8> {
4949
possibilities
5050
}
5151

52-
fn alt_solver_prep(puzz: Vec<u8>, order: Order) -> Puzzle {
52+
fn alt_solver_prep(puzz: [u8;81], order: Order) -> Puzzle {
5353
let mut p: Puzzle = Puzzle {
5454
puzz,
5555
blank_positions: Vec::new(),
@@ -100,7 +100,7 @@ fn alt_solver_prep(puzz: Vec<u8>, order: Order) -> Puzzle {
100100
p
101101
}
102102

103-
pub fn alt_solve(puzz: Vec<u8>, order: Order) -> PyResult<Vec<u8>> {
103+
pub fn alt_solve(puzz: [u8; 81], order: Order) -> PyResult<[u8; 81]> {
104104
if puzz.len() != 81 {
105105
return Err(pyo3::exceptions::PyValueError::new_err("A puzzle must have a length of 81!"));
106106
}
@@ -155,17 +155,17 @@ pub fn alt_solve(puzz: Vec<u8>, order: Order) -> PyResult<Vec<u8>> {
155155
}
156156
}
157157

158-
fn gen_random_solved() -> Vec<u8> {
159-
let puzzle: Vec<u8> = vec![0; 81];
158+
fn gen_random_solved() -> [u8; 81] {
159+
let puzzle: [u8; 81] = [0; 81];
160160
alt_solve(puzzle, RANDOM).unwrap()
161161
}
162162

163-
fn is_legal(puzzle: &Vec<u8>) -> bool {
164-
puzzle_solver::solve(puzzle.clone()).unwrap() == alt_solve(puzzle.clone(), REVERSE).unwrap()
163+
fn is_legal(puzzle: &[u8; 81]) -> bool {
164+
puzzle_solver::backend_solve(puzzle.clone()).unwrap() == alt_solve(puzzle.clone(), REVERSE).unwrap()
165165
}
166166

167167
#[pyo3::pyfunction]
168-
pub fn gen_unsolved(num_hints: usize) -> PyResult<Vec<u8>> {
168+
pub fn gen_unsolved(num_hints: usize) -> PyResult<[u8; 81]> {
169169

170170
if num_hints < 23 || num_hints > 40 {
171171
return Err(pyo3::exceptions::PyValueError::new_err("Please specify a value between 23 and 40 for num_hints."));

src/libraries/puzzle_reader.rs

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,16 @@ use std::io::{Write, stdout};
55
#[derive(Clone)]
66
pub struct PuzzleReader {
77
pub size: usize,
8-
pub unsolved: Vec<Vec<u8>>,
9-
pub solved: Vec<Vec<u8>>,
8+
pub unsolved: Vec<[u8; 81]>,
9+
pub solved: Vec<[u8; 81]>,
1010
}
1111

1212
#[pymethods]
1313
impl PuzzleReader {
1414
#[new]
1515
pub fn load_puzzles(file: &str, from_url: bool) -> PyResult<Self> {
1616

17-
let mut p = PuzzleReader {
18-
size: 0,
19-
unsolved: Vec::new(),
20-
solved: Vec::new(),
21-
};
17+
2218

2319
let puzzles_string: String;
2420

@@ -38,16 +34,23 @@ impl PuzzleReader {
3834
println!("Read {} in {:?}", file, read_start.elapsed());
3935
}
4036

37+
let num_puzzles = (puzzles_string.len() - 15) * 81/82/(81*2);
38+
39+
let mut p = PuzzleReader {
40+
size: num_puzzles,
41+
unsolved: Vec::with_capacity(num_puzzles),
42+
solved: Vec::with_capacity(num_puzzles),
43+
};
44+
4145
let parse_start = std::time::Instant::now();
4246
let mut first_line = true;
4347
for line in puzzles_string.lines() {
4448
if first_line {
4549
first_line = false;
4650
continue;
4751
}
48-
p.size += 1;
49-
let mut unsolved_list = vec![0;81];
50-
let mut solved_list = vec![0; 81];
52+
let mut unsolved_list: [u8; 81] = [0; 81];
53+
let mut solved_list: [u8; 81] = [0; 81];
5154
let line_bytes = line.as_bytes();
5255

5356
if line_bytes.len() == 163 && line_bytes[81] == 0x2c { //0x2c is the utf-8 char for a comma
@@ -67,17 +70,21 @@ impl PuzzleReader {
6770
Ok(p)
6871
}
6972

70-
pub fn get_unsolved_puzz(&self, line_num: usize) -> PyResult<Vec<u8>> {
73+
pub fn get_unsolved_puzz(&self, line_num: usize) -> PyResult<[u8; 81]> {
7174
Ok(self.unsolved[line_num - 2].clone())
7275
}
7376

74-
pub fn get_solved_puzz(&self, line_num: usize) -> PyResult<Vec<u8>> {
77+
pub fn get_solved_puzz(&self, line_num: usize) -> PyResult<[u8; 81]> {
7578
Ok(self.solved[line_num - 2].clone())
7679
}
7780
}
7881

7982
#[pyfunction]
8083
pub fn print_puzz(puzz: Vec<u8>) {
84+
backend_print_puzz(puzz.try_into().expect("A puzzle must have a length of 81!"));
85+
}
86+
87+
pub fn backend_print_puzz(puzz: [u8; 81]) {
8188
let mut lock = stdout().lock();
8289

8390
for row in 0..9 {

src/libraries/puzzle_solver.rs

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use pyo3::{pyfunction, PyResult};
2+
use std::convert::TryInto;
3+
24
pub(crate) struct Puzzle {
3-
pub puzz: Vec<u8>,
5+
pub puzz: [u8; 81],
46
pub blank_positions: Vec<u8>,
57
pub possibilities: Vec<Vec<u8>>,
68
pub cached_possibilities: Vec<[bool;10]>,
@@ -24,9 +26,10 @@ fn no_repeats(items: Vec<u8>) -> bool {
2426

2527
#[pyfunction]
2628
pub(crate) fn is_valid(puzzle: Vec<u8>) -> PyResult<bool> {
27-
if puzzle.len() != 81 {
28-
return Err(pyo3::exceptions::PyValueError::new_err("A puzzle must have a length of 81!"));
29-
}
29+
backend_is_valid(puzzle.try_into().expect("A puzzle must have a length of 81!"))
30+
}
31+
32+
pub(crate) fn backend_is_valid(puzzle: [u8; 81]) -> PyResult<bool> {
3033

3134
for i in 0..9 { //check rows
3235
if !no_repeats(Vec::from(&puzzle[i * 9..(i + 1) * 9])) {
@@ -67,7 +70,7 @@ pub(crate) fn is_valid(puzzle: Vec<u8>) -> PyResult<bool> {
6770
Ok(true)
6871
}
6972

70-
pub(crate) fn get_possibilities(puzz: &Vec<u8>, pos: u8) -> Vec<u8> {
73+
pub(crate) fn get_possibilities(puzz: &[u8; 81], pos: u8) -> Vec<u8> {
7174
let mut possibilities: Vec<u8> = Vec::new();
7275
let mut seen: [bool; 10] = [false; 10];
7376
let row: usize = (pos / 9) as usize;
@@ -92,7 +95,7 @@ pub(crate) fn get_possibilities(puzz: &Vec<u8>, pos: u8) -> Vec<u8> {
9295
}
9396
possibilities
9497
}
95-
fn solver_prep(puzz: Vec<u8>) -> Puzzle {
98+
fn solver_prep(puzz: [u8; 81]) -> Puzzle {
9699
let mut p: Puzzle = Puzzle {
97100
puzz,
98101
blank_positions: Vec::new(),
@@ -143,7 +146,7 @@ fn solver_prep(puzz: Vec<u8>) -> Puzzle {
143146
p
144147
}
145148

146-
pub(crate) fn get_possibilities_as_array(puzz: &Vec<u8>, pos: usize) -> [bool; 10] {
149+
pub(crate) fn get_possibilities_as_array(puzz: &[u8; 81], pos: usize) -> [bool; 10] {
147150
let mut seen: [bool; 10] = [true; 10];
148151
let row: usize = pos / 9;
149152
let col: usize = pos % 9;
@@ -161,18 +164,20 @@ pub(crate) fn get_possibilities_as_array(puzz: &Vec<u8>, pos: usize) -> [bool; 1
161164
}
162165
seen
163166
}
167+
164168
#[pyfunction]
165-
pub fn solve(puzz: Vec<u8>) -> PyResult<Vec<u8>> {
166-
if puzz.len() != 81 {
167-
return Err(pyo3::exceptions::PyValueError::new_err("Your puzzle must have a length of 81!"));
168-
}
169+
pub fn solve(puzz: Vec<u8>) -> PyResult<[u8; 81]> {
170+
backend_solve(puzz.try_into().expect("A puzzle must have a length of 81!"))
171+
}
172+
173+
pub fn backend_solve(puzz: [u8; 81]) -> PyResult<[u8; 81]> {
169174

170175
let mut p = solver_prep(puzz);
171176
if p.solved {
172177
return Ok(p.puzz);
173178
}
174179

175-
if !is_valid(p.puzz.clone())? {
180+
if !backend_is_valid(p.puzz.clone())? {
176181
return Err(pyo3::exceptions::PyValueError::new_err("The puzzle is illegal!"));
177182
}
178183

@@ -221,7 +226,7 @@ pub fn solve(puzz: Vec<u8>) -> PyResult<Vec<u8>> {
221226
}
222227
}
223228

224-
pub async fn async_solve(puzz: Vec<u8>) -> PyResult<Vec<u8>> {
225-
solve(puzz)
229+
pub async fn async_solve(puzz: [u8; 81]) -> PyResult<[u8; 81]> {
230+
backend_solve(puzz)
226231
}
227232

src/libraries/speedtest.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use pyo3::pyfunction;
22
use crate::libraries::puzzle_reader;
3-
use crate::libraries::puzzle_solver::{async_solve, solve};
3+
use crate::libraries::puzzle_solver::{async_solve, backend_solve};
44

55

66
#[pyfunction]
@@ -31,7 +31,7 @@ pub fn async_speedtest(puzzle_reader: &puzzle_reader::PuzzleReader, verbose: boo
3131
let start_validate = std::time::Instant::now();
3232

3333
for i in 0..solved_puzzles.len() {
34-
let pc_solved: Vec<u8>;
34+
let pc_solved: [u8; 81];
3535

3636
match &solved_puzzles[i] {
3737
Ok(puzz) => {
@@ -43,9 +43,9 @@ pub fn async_speedtest(puzzle_reader: &puzzle_reader::PuzzleReader, verbose: boo
4343
if verbose {
4444
println!("\nLine {}:", i + 2);
4545
println!("Unsolved: ",);
46-
puzzle_reader::print_puzz(puzzle_reader.unsolved[i].clone());
46+
puzzle_reader::backend_print_puzz(puzzle_reader.unsolved[i].clone());
4747
println!("\nSolved:");
48-
puzzle_reader::print_puzz(pc_solved.clone());
48+
puzzle_reader::backend_print_puzz(pc_solved.clone());
4949

5050

5151
}
@@ -66,16 +66,16 @@ pub fn async_speedtest(puzzle_reader: &puzzle_reader::PuzzleReader, verbose: boo
6666
#[pyo3(signature = (puzzle_reader, verbose = false))]
6767
pub fn synchronous_speedtest(puzzle_reader: &puzzle_reader::PuzzleReader, verbose: bool) -> pyo3::PyResult<()> {
6868
println!("---------------\nStarting Synchronous Speedtest\n---------------");
69-
let mut solved_puzzles = Vec::new();
69+
let mut solved_puzzles: Vec<pyo3::PyResult<[u8; 81]>> = Vec::with_capacity(puzzle_reader.size);
7070
let start_solve = std::time::Instant::now();
7171
for i in 0..puzzle_reader.unsolved.len() {
72-
solved_puzzles.push(solve(puzzle_reader.unsolved[i].clone()));
72+
solved_puzzles.push(backend_solve(puzzle_reader.unsolved[i].clone()));
7373
}
7474
let solve_time = start_solve.elapsed();
7575
let start_validate = std::time::Instant::now();
7676

7777
for i in 0..solved_puzzles.len() {
78-
let pc_solved: Vec<u8>;
78+
let pc_solved: [u8; 81];
7979

8080
match &solved_puzzles[i] {
8181
Ok(puzz) => {
@@ -87,9 +87,9 @@ pub fn synchronous_speedtest(puzzle_reader: &puzzle_reader::PuzzleReader, verbos
8787
if verbose {
8888
println!("\nLine {}:", i + 2);
8989
println!("Unsolved: ",);
90-
puzzle_reader::print_puzz(puzzle_reader.unsolved[i].clone());
90+
puzzle_reader::backend_print_puzz(puzzle_reader.unsolved[i].clone());
9191
println!("\nSolved:");
92-
puzzle_reader::print_puzz(pc_solved.clone());
92+
puzzle_reader::backend_print_puzz(pc_solved.clone());
9393

9494

9595
}

testing.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
def tests():
66
reader = sudoku.PuzzleReader(
7-
"https://raw.githubusercontent.com/shaggysa/lib_sudoku/master/puzzles.csv", True
7+
"puzzles.csv", False
88
)
99
sudoku.async_speedtest(reader)
1010
sudoku.synchronous_speedtest(reader)

0 commit comments

Comments
 (0)