aboutsummaryrefslogtreecommitdiff
path: root/src/hitomezashi.rs
blob: 88ba387ea55ecbb46aec60264e6ac8e06aed1c6a59d6b842412f8ec74ab85107 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
/*
	hitomezashi-rs  Generates classical colored Hitomezashi stitch patterns
	Copyright (C) 2024  Nicholas Johnson

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

use rand::distributions::{Bernoulli, Distribution};

const TRANSPARENT_SQUARE: char = ' ';
const OPAQUE_SQUARE: char = '█';

fn print_square(is_transparent: bool) {
	print!("{}", if is_transparent { TRANSPARENT_SQUARE } else { OPAQUE_SQUARE });
}

pub fn hitomezashi(width: usize, height: usize, skew: Option<f64>) {
	let skew: f64 = match skew {
		Some(n) => { n }
		None => { 0.5 }
	};

	let mut rng = rand::thread_rng();
	let brn = Bernoulli::new(skew).unwrap();

	// the first square (upper left corner) is always transparent
	let init_bit: bool = true;
	let mut row_bits: Vec<bool> = Vec::with_capacity(height - 1);
	let mut col_bits: Vec<bool> = Vec::with_capacity(width - 1);

	for _ in 0..(height - 1) {
		row_bits.push(brn.sample(&mut rng));
	}

	for _ in 0..(width - 1) {
		col_bits.push(brn.sample(&mut rng));
	}

	let mut alt_bits: Vec<bool> = Vec::with_capacity(width - 1);

	for col in 0..width {
		alt_bits.push(col % 2 == 1);
	}

	// each new row of the pattern depends on the bits directly above it
	let mut above_bits: Vec<bool> = Vec::with_capacity(width);

	above_bits.push(init_bit);
	print_square(above_bits[0]);

	for col in 1..width {
	/* each square in the first row is derived from the square to its left. the column bits
	 * represent whether there's a stitch between the two squares. if there's a stitch, the squares
	 * are different, otherwise they are the same. */
		above_bits.push(above_bits[col - 1] ^ col_bits[col - 1]);
		print_square(above_bits[col]);
	}

	println!();

	// height-1 because the first row has already been printed
	for row in 0..(height - 1) {
		/* each square in each successive row is derived from the square above it. the row bits
		 * represent whether there's a stitch between the two squares. if there's a stitch, the
		 * squares are different, otherwise they are the same. */
		above_bits
			.iter_mut()
			.zip(alt_bits.iter())
			.for_each(|(x1, &x2)| {
				*x1 ^= x2 ^ row_bits[row];
				print_square(*x1);
			});

		println!();
	}
}