1extern crate pancurses;
2extern crate rand;
3extern crate structopt;
4extern crate term_size;
5
6use std::collections::VecDeque;
7
8pub mod config;
9
10use config::Config;
11
12use pancurses::*;
13use rand::distributions::{Distribution, Standard};
14use rand::rngs::SmallRng;
15use rand::{Rng, SeedableRng};
16use std::cell::RefCell;
17
18thread_local! {
19 static RNG: RefCell<SmallRng> = RefCell::new(SmallRng::from_entropy());
20}
21
22fn rng<T>() -> T
23where
24 Standard: Distribution<T>,
25{
26 RNG.with(|rng| (*rng).borrow_mut().r#gen::<T>())
27}
28
29fn rand_char() -> char {
30 let (randnum, randmin) = (93, 33);
31 RNG.with(|rng| (*rng).borrow_mut().r#gen::<u8>() % randnum + randmin) as char
32}
33
34fn coin_flip() -> bool {
35 RNG.with(|rng| (*rng).borrow_mut().r#gen())
36}
37
38#[derive(Clone)]
39pub struct Block {
40 val: char,
41 white: bool,
42 color: i16,
43}
44
45impl Block {
46 fn is_space(&self) -> bool {
47 self.val == ' '
48 }
49}
50
51impl Default for Block {
52 fn default() -> Self {
53 Block {
54 val: ' ',
55 white: false,
56 color: COLOR_RED,
57 }
58 }
59}
60
61pub struct Column {
62 length: usize, spaces: usize, col: VecDeque<Block>, }
66
67impl Column {
68 fn new(lines: usize) -> Self {
70 Column {
71 length: rng::<usize>() % (lines - 3) + 3,
72 spaces: rng::<usize>() % lines + 1,
73 col: (0..lines).map(|_| Block::default()).collect(),
74 }
75 }
76 fn head_is_empty(&self) -> bool {
77 self.col[1].val == ' '
78 }
79 fn new_rand_char(&mut self) {
80 self.col[0].val = rand_char();
81 self.col[0].color = self.col[1].color;
82 }
83 fn new_rand_head(&mut self, config: &Config) {
84 self.col[0].val = rand_char();
85 self.col[0].color = if config.rainbow {
86 match rng::<usize>() % 6 {
87 0 => COLOR_GREEN,
88 1 => COLOR_BLUE,
89 2 => COLOR_WHITE,
90 3 => COLOR_YELLOW,
91 4 => COLOR_CYAN,
92 5 => COLOR_MAGENTA,
93 _ => unreachable!(),
94 }
95 } else {
96 config.colour
97 };
98 self.col[0].white = coin_flip();
100 }
101}
102
103impl std::ops::Index<usize> for Column {
104 type Output = Block;
105 fn index(&self, i: usize) -> &Self::Output {
106 &self.col[i]
107 }
108}
109
110pub struct Matrix {
111 m: Vec<Column>,
112}
113
114impl std::ops::Index<usize> for Matrix {
115 type Output = Column;
116 fn index(&self, i: usize) -> &Self::Output {
117 &self.m[i]
118 }
119}
120
121impl Default for Matrix {
122 fn default() -> Self {
124 let (lines, cols) = get_term_size();
126
127 Matrix {
129 m: (0..cols).map(|_| Column::new(lines)).collect(),
130 }
131 }
132}
133
134impl Matrix {
135 fn num_columns(&self) -> usize {
136 self.m.len()
137 }
138
139 fn num_lines(&self) -> usize {
140 self[0].col.len()
141 }
142
143 pub fn arrange(&mut self, config: &Config) {
145 let lines = self.num_lines();
146
147 self.m.iter_mut().for_each(|col| {
148 if col.head_is_empty() && col.spaces != 0 {
149 col.spaces -= 1;
151 } else if col.head_is_empty() && col.spaces == 0 {
152 col.new_rand_head(config);
154
155 col.length -= 1;
157
158 col.spaces = rng::<usize>() % lines + 1;
160 } else if col.length != 0 {
161 col.new_rand_char();
163 col.length -= 1;
164 } else {
165 col.col[0].val = ' ';
167 col.length = rng::<usize>() % (lines - 3) + 3;
168 }
169 });
170 if config.oldstyle {
171 self.old_style_move_down();
172 } else {
173 self.move_down();
174 }
175 }
176 fn move_down(&mut self) {
177 self.m.iter_mut().for_each(|col| {
178 let mut in_stream = false;
180
181 let mut last_was_white = false; let mut running_color = COLOR_CYAN;
183
184 col.col.iter_mut().for_each(|block| {
185 if !in_stream {
186 if !block.is_space() {
187 block.val = ' ';
188 in_stream = true; running_color = block.color;
190 }
191 } else if block.is_space() {
192 block.val = rand_char();
194 block.white = last_was_white;
195 in_stream = false;
196 }
197 std::mem::swap(&mut last_was_white, &mut block.white);
199 block.color = running_color;
200 })
201 })
202 }
203 fn old_style_move_down(&mut self) {
204 self.m.iter_mut().for_each(|col| {
206 col.col.pop_back();
207 col.col.push_back(Block::default()); col.col.rotate_right(1)
209 });
210 }
211 pub fn draw(&self, window: &Window, config: &Config) {
213 for j in 1..self.num_lines() {
215 let mut last_colour: i16 = self[0][j].color;
217 window.attron(COLOR_PAIR(last_colour as chtype));
218
219 for i in 0..self.num_columns() {
220 let mcolour = if self[i][j].white {
222 COLOR_WHITE
223 } else {
224 self[i][j].color
225 };
226
227 window.mv(j as i32 - 1, 2 * i as i32); if last_colour != mcolour {
229 window.attron(COLOR_PAIR(mcolour as chtype));
231 last_colour = mcolour;
232 }
233 window.addch(self[i][j].val as chtype);
235 }
236 }
237 napms(config.update as i32 * 10);
238 }
239}
240
241pub fn finish() {
243 curs_set(1);
244 endwin();
245 std::process::exit(0);
246}
247
248pub fn ncurses_init() -> Window {
250 let window = initscr();
251 window.nodelay(true);
252 window.refresh();
253
254 noecho();
255 nonl();
256 cbreak();
257 curs_set(0);
258
259 if has_colors() {
260 start_color();
261 if use_default_colors() != ERR {
262 init_pair(COLOR_BLACK, -1, -1);
263 init_pair(COLOR_GREEN, COLOR_GREEN, -1);
264 init_pair(COLOR_WHITE, COLOR_WHITE, -1);
265 init_pair(COLOR_RED, COLOR_RED, -1);
266 init_pair(COLOR_CYAN, COLOR_CYAN, -1);
267 init_pair(COLOR_MAGENTA, COLOR_MAGENTA, -1);
268 init_pair(COLOR_BLUE, COLOR_BLUE, -1);
269 init_pair(COLOR_YELLOW, COLOR_YELLOW, -1);
270 } else {
271 init_pair(COLOR_BLACK, COLOR_BLACK, COLOR_BLACK);
272 init_pair(COLOR_GREEN, COLOR_GREEN, COLOR_BLACK);
273 init_pair(COLOR_WHITE, COLOR_WHITE, COLOR_BLACK);
274 init_pair(COLOR_RED, COLOR_RED, COLOR_BLACK);
275 init_pair(COLOR_CYAN, COLOR_CYAN, COLOR_BLACK);
276 init_pair(COLOR_MAGENTA, COLOR_MAGENTA, COLOR_BLACK);
277 init_pair(COLOR_BLUE, COLOR_BLUE, COLOR_BLACK);
278 init_pair(COLOR_YELLOW, COLOR_YELLOW, COLOR_BLACK);
279 }
280 }
281
282 window
283}
284
285fn get_term_size() -> (usize, usize) {
286 match term_size::dimensions() {
287 Some((mut width, mut height)) => {
288 if width < 10 {
290 width = 10
291 }
292 if height < 10 {
293 height = 10
294 }
295 if width % 2 != 0 {
296 (height + 1, (width / 2) + 1)
298 } else {
299 (height + 1, width / 2)
300 }
301 }
302 None => (10, 10),
303 }
304}
305
306pub fn resize_window() {
307 endwin();
309 initscr();
310}