|
@@ -16,11 +16,10 @@ use std::{thread, time};
|
|
|
|
|
|
use config::{Config};
|
|
|
use util::{copy_shuffle};
|
|
|
+use grid::Grid;
|
|
|
|
|
|
mod board {}
|
|
|
|
|
|
-const DEFAULT_ROWS: usize = 17;
|
|
|
-const DEFAULT_COLS: usize = 24;
|
|
|
const BORDER_HEX_WIDTH: usize = 6;
|
|
|
|
|
|
// Different actions are printed to the console at different speeds: the
|
|
@@ -33,178 +32,7 @@ const TYPING_SPEED: time::Duration = time::Duration::from_millis(70);
|
|
|
pub struct Screen {
|
|
|
pub w: Window,
|
|
|
attempts_left: u8,
|
|
|
- b: Board
|
|
|
-}
|
|
|
-
|
|
|
-// The game board.
|
|
|
-#[derive(Debug)]
|
|
|
-struct Board {
|
|
|
- rows: usize,
|
|
|
- cols: usize,
|
|
|
- c: Vec<char>
|
|
|
-}
|
|
|
-
|
|
|
-// The position of a word on the game board.
|
|
|
-#[derive(Debug)]
|
|
|
-struct Coord {
|
|
|
- start: usize,
|
|
|
- end: usize
|
|
|
-}
|
|
|
-
|
|
|
-impl Coord {
|
|
|
- fn new(start: usize, len: usize) -> Coord {
|
|
|
- Coord{start:start, end:start+len-1}
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-impl Board {
|
|
|
- fn new(rows: Option<usize>, cols: Option<usize>) -> Board {
|
|
|
- let r = match rows {
|
|
|
- Some(r) => r,
|
|
|
- None => DEFAULT_ROWS
|
|
|
- };
|
|
|
- let c = match cols {
|
|
|
- Some(c) => c,
|
|
|
- None => DEFAULT_COLS
|
|
|
- };
|
|
|
- Board {
|
|
|
- rows: r,
|
|
|
- cols: c,
|
|
|
- c: vec![' '; r * c]
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- fn fill_with_garbage(&mut self) {
|
|
|
- let g: Vec<char> = "!@#$%^*()_-=+\\|/[]{}?\"\':;,.<>".chars().collect();
|
|
|
- let mut rng = rand::thread_rng();
|
|
|
-
|
|
|
- for i in 0..self.rows * self.cols {
|
|
|
- self.c[i] = *g.choose(&mut rng).unwrap();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- fn capacity(&self) -> usize {
|
|
|
- self.c.len()
|
|
|
- }
|
|
|
-
|
|
|
- fn get_i(&self, i: usize) -> char {
|
|
|
- self.c[i]
|
|
|
- }
|
|
|
-
|
|
|
- fn get_xy(&self, x: usize, y: usize) -> char {
|
|
|
- self.c[x*y]
|
|
|
- }
|
|
|
-
|
|
|
- fn set_i(&mut self, i: usize, c: char) {
|
|
|
- self.c[i] = c;
|
|
|
- }
|
|
|
-
|
|
|
- fn set_xy(&mut self, x: usize, y: usize, c: char) {
|
|
|
- self.set_i(x*y, c);
|
|
|
- }
|
|
|
-
|
|
|
- fn set_i_str(&mut self, i: usize, s: &str) {
|
|
|
- s.chars().enumerate().for_each(|(str_index, c)| self.set_i(i+str_index, c));
|
|
|
- }
|
|
|
-
|
|
|
- fn set_xy_str(&mut self, x: usize, y: usize, s: &str) {
|
|
|
- s.chars().enumerate().for_each(|(str_index, c)| self.set_i((x*y)+str_index, c));
|
|
|
- }
|
|
|
-
|
|
|
- // Think of the board in word_len "chunks": place a slot for a word in each chunk
|
|
|
- fn build_word_coords(&self, num_of_words: usize, word_len: usize) -> Vec<Coord> {
|
|
|
- let chunk_len = self.capacity() / num_of_words;
|
|
|
- if chunk_len < word_len+1 {
|
|
|
- panic!("Can't fill board: need {}-character long spaces for\
|
|
|
- {}-character long words. But only have {}-character spaces\
|
|
|
- available. Please choose smaller words or increase board size.",
|
|
|
- word_len+1, word_len, chunk_len);
|
|
|
- }
|
|
|
- let mut coords: Vec<Coord> = Vec::with_capacity(num_of_words);
|
|
|
- let mut starting_coord: usize = 0;
|
|
|
- for _ in 0..num_of_words {
|
|
|
- let coord = Coord::new(starting_coord, word_len);
|
|
|
- starting_coord+=chunk_len;
|
|
|
- assert!(starting_coord > coord.end);
|
|
|
- coords.push(coord);
|
|
|
- }
|
|
|
- coords
|
|
|
- }
|
|
|
-
|
|
|
- // "shake" each word slot around, in a random order, 3 times/word.
|
|
|
- fn shake_coords(&self, word_coords: &mut Vec<Coord>) {
|
|
|
- let mut rng = rand::thread_rng();
|
|
|
-
|
|
|
- for _ in 0..3 {
|
|
|
- let indices: Vec<usize> = (0..word_coords.len()).collect();
|
|
|
- let shuffled_indices = copy_shuffle(&indices);
|
|
|
-
|
|
|
- for i in shuffled_indices {
|
|
|
- let coord = &word_coords[i];
|
|
|
- // Where can this coord move (leaving at least 1 space between words)?
|
|
|
- let mut movement_options = Vec::with_capacity(2);
|
|
|
-
|
|
|
- let start = coord.start as i32;
|
|
|
- let previous_coord_end: i32;
|
|
|
- if i == 0 {
|
|
|
- previous_coord_end = coord.start as i32;
|
|
|
- } else {
|
|
|
- let prev_coord = &word_coords[i-1];
|
|
|
- previous_coord_end = prev_coord.end as i32;
|
|
|
- }
|
|
|
- let space_behind = start - previous_coord_end;
|
|
|
- if space_behind > 1 {
|
|
|
- // It can move backwards
|
|
|
- movement_options.push((space_behind-1) * -1);
|
|
|
- }
|
|
|
-
|
|
|
- let next_coord_start: i32;
|
|
|
- let end = coord.end as i32;
|
|
|
- if i == word_coords.len()-1 {
|
|
|
- next_coord_start = self.capacity() as i32;
|
|
|
- } else {
|
|
|
- let next_coord = &word_coords[i+1];
|
|
|
- next_coord_start = next_coord.start as i32;
|
|
|
- }
|
|
|
- let space_in_front = next_coord_start - end;
|
|
|
- if space_in_front > 1 {
|
|
|
- // It can move forwards
|
|
|
- movement_options.push(space_in_front-1);
|
|
|
- }
|
|
|
-
|
|
|
- // Pick a movement option, & pick some amount to move
|
|
|
- let mv = match movement_options.choose(&mut rng) {
|
|
|
- None => continue,
|
|
|
- Some(m) => if *m > 0 {
|
|
|
- rng.gen_range(0, *m)
|
|
|
- } else {
|
|
|
- rng.gen_range(*m, 1)
|
|
|
- }
|
|
|
- };
|
|
|
- word_coords[i] = Coord{
|
|
|
- start: (coord.start as i32+mv) as usize,
|
|
|
- end: (coord.end as i32+mv) as usize,
|
|
|
- };
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- fn insert_words(&mut self, words: &[String], coords: &Vec<Coord>) {
|
|
|
- assert_eq!(words.len(), coords.len());
|
|
|
- for (word, coord) in words.iter().zip(coords.iter()) {
|
|
|
- assert_eq!(word.len(), coord.end - coord.start + 1);
|
|
|
- for (ch, coord_index) in word.chars().zip(coord.start..coord.end+1) {
|
|
|
- self.c[coord_index] = ch;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- fn dump(&self) {
|
|
|
- for chunk in self.c.chunks(self.rows) {
|
|
|
- let s: String = chunk.iter().collect();
|
|
|
- println!("{}", s);
|
|
|
- }
|
|
|
- }
|
|
|
+ board: Grid
|
|
|
}
|
|
|
|
|
|
impl Screen {
|
|
@@ -212,12 +40,12 @@ impl Screen {
|
|
|
let window = initscr();
|
|
|
noecho();
|
|
|
window_enable_colors();
|
|
|
- let mut b = Board::new(conf.rows, conf.cols);
|
|
|
- b.fill_with_garbage();
|
|
|
+ let mut board = Grid::new(conf.rows, conf.cols);
|
|
|
+ board.fill_with_garbage();
|
|
|
Screen {
|
|
|
w: window,
|
|
|
attempts_left: 4,
|
|
|
- b: b
|
|
|
+ board: board
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -237,7 +65,7 @@ impl Screen {
|
|
|
self.print_with_delay(1, String::from("ENTER PASSWORD NOW"), false, TERMINAL_PRINTING_SPEED);
|
|
|
self.print_with_delay(
|
|
|
3,
|
|
|
- self.attempts_display(),
|
|
|
+ self.build_attempts_display(),
|
|
|
false,
|
|
|
TERMINAL_PRINTING_SPEED
|
|
|
);
|
|
@@ -245,25 +73,20 @@ impl Screen {
|
|
|
let shuffled_words = copy_shuffle(&conf.words);
|
|
|
let words = &shuffled_words[..conf.choose];
|
|
|
|
|
|
- // Assumes validate() ensured `words` is not empty, and each word is the same length
|
|
|
- let word_len = words[0].len();
|
|
|
- let required_tiles = word_len * words.len() + (words.len() - 1);
|
|
|
- let board_tiles = self.b.capacity();
|
|
|
- if required_tiles > board_tiles {
|
|
|
- panic!("{} words of length {} requires {} tiles, but an {}x{} board only allows for {} tiles", conf.words.len(), word_len, required_tiles, self.b.cols, self.b.rows, board_tiles);
|
|
|
- }
|
|
|
+ let coord = self.board.randomly_add_words(words);
|
|
|
|
|
|
- let mut coords = self.b.build_word_coords(words.len(), word_len);
|
|
|
- self.b.shake_coords(&mut coords);
|
|
|
- println!("{:?}", coords);
|
|
|
- self.b.insert_words(words, &coords);
|
|
|
+ // The board of words + garbage characters has been built: now the screen must be displayed.
|
|
|
+ // Take the board
|
|
|
|
|
|
- let mut screen_buffer = Board::new(Some(self.b.rows), Some(self.b.cols + (2 * BORDER_HEX_WIDTH) + 3));
|
|
|
- let border_hex = border_hex_gen(self.b.rows, self.b.cols);
|
|
|
- self.add_border_hex_to_board(&mut screen_buffer, &border_hex);
|
|
|
+ let mut screen_buffer = Grid::new(Some(self.board.rows), Some(self.board.cols + (2 * BORDER_HEX_WIDTH) + 3));
|
|
|
+ let border_hex = border_hex_gen(self.board.rows, self.board.cols);
|
|
|
+ self.add_border_hex_to_grid(&mut screen_buffer, &border_hex);
|
|
|
+ self.add_board_to_grid(&mut screen_buffer);
|
|
|
|
|
|
- self.b.dump();
|
|
|
- screen_buffer.dump();
|
|
|
+ // Debugging
|
|
|
+ endwin();
|
|
|
+ trace!("Words:\n{}", self.board.dump());
|
|
|
+ trace!("Hex:\n{}", screen_buffer.dump());
|
|
|
}
|
|
|
|
|
|
pub fn end_window(&self) {
|
|
@@ -291,23 +114,30 @@ impl Screen {
|
|
|
format!("{} ATTEMPT(S) LEFT:{}", self.attempts_left, " *".repeat(self.attempts_left as usize))
|
|
|
}
|
|
|
|
|
|
- fn add_border_hex_to_board(&self, board: &mut Board, hex: &Vec<u32>) {
|
|
|
- let left_side = (0..board.rows).zip(hex.iter());
|
|
|
- let right_side = (board.rows..).zip(hex[board.rows..].iter());
|
|
|
+ fn add_border_hex_to_grid(&self, grid: &mut Grid, hex: &Vec<u32>) {
|
|
|
+ let left_side = (0..grid.rows).zip(hex.iter());
|
|
|
+ let right_side = (0..grid.rows).zip(hex[grid.rows..].iter());
|
|
|
+ trace!("Left side from 0..{}, right side from {}..{}", grid.rows, grid.rows, hex.len());
|
|
|
|
|
|
- for (y, hex_val) in left_side {
|
|
|
- println!("Printing 0x{:x} at 0, {}", hex_val, y);
|
|
|
- board.set_xy_str(0, y, &format!("0x{:x}", hex_val));
|
|
|
+ for (row, hex_val) in left_side {
|
|
|
+ trace!("Printing 0x{:x} at {}, 0", hex_val, row);
|
|
|
+ grid.set_rowcol_str(row, 0, &format!("0x{:x}", hex_val));
|
|
|
}
|
|
|
|
|
|
// Half the board, plus characters for the first hex values, plus 2 spaces of padding.
|
|
|
- let right_hand_offset = board.cols / 2;
|
|
|
- for (y, hex_val) in right_side {
|
|
|
- println!("Printing 0x{:x} at {}, {}", hex_val, right_hand_offset, y);
|
|
|
- board.set_xy_str(right_hand_offset, y, &format!("0x{:x}", hex_val));
|
|
|
+ let right_hand_offset = grid.cols / 2;
|
|
|
+ for (row, hex_val) in right_side {
|
|
|
+ trace!("Printing 0x{:x} at {}, {}", hex_val, row, right_hand_offset);
|
|
|
+ grid.set_rowcol_str(row, right_hand_offset, &format!("0x{:x}", hex_val));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ fn add_board_to_grid(&self, grid: &mut Grid) {
|
|
|
+ // TODO
|
|
|
+ // The board is pressed to the right of each column
|
|
|
+ // Where to start placing the board on each row: (grid.cols / 2) - (self.board.cols / 2)
|
|
|
+ }
|
|
|
+
|
|
|
}
|
|
|
|
|
|
fn window_enable_colors() {
|