123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204 |
- extern crate rand;
- use self::rand::Rng;
- use self::rand::prelude::SliceRandom;
- use util::{copy_shuffle};
- // The position of a word on the character grid.
- #[derive(Debug)]
- pub struct Coord {
- start: usize,
- end: usize
- }
- impl Coord {
- fn new(start: usize, len: usize) -> Coord {
- Coord{start:start, end:start+len-1}
- }
- }
- const DEFAULT_ROWS: usize = 17;
- const DEFAULT_COLS: usize = 24;
- const GARBAGE_CHARS: &'static [char] = &['!', '@', '#', '$', '%', '^', '*', '(', ')',
- '_', '-', '=', '+', '\\', '|', '/', '[', ']',
- '{', '}', '?', '\'', '"'];
- // An x by y grid of characters.
- #[derive(Debug)]
- pub struct Grid {
- pub rows: usize,
- pub cols: usize,
- c: Vec<char>
- }
- impl Grid {
- pub fn new(rows: Option<usize>, cols: Option<usize>) -> Grid {
- let r = match rows {
- Some(r) => r,
- None => DEFAULT_ROWS
- };
- let c = match cols {
- Some(c) => c,
- None => DEFAULT_COLS
- };
- Grid {
- rows: r,
- cols: c,
- c: vec![' '; r * c]
- }
- }
- pub fn fill_with_garbage(&mut self) {
- let mut rng = rand::thread_rng();
- for i in 0..self.rows * self.cols {
- self.set_i(i, *GARBAGE_CHARS.choose(&mut rng).unwrap());
- }
- }
- pub fn capacity(&self) -> usize {
- self.c.len()
- }
- pub fn get_i(&self, i: usize) -> char {
- self.c[i]
- }
- pub fn get_rowcol(&self, row: usize, col: usize) -> char {
- self.c[(row*self.cols)+col]
- }
- pub fn set_i(&mut self, i: usize, c: char) {
- // trace!("Setting {} to character {}", i, c);
- self.c[i] = c;
- }
- pub fn set_rowcol(&mut self, row: usize, col: usize, c: char) {
- self.set_i((row*self.cols)+col, c);
- }
- pub 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));
- }
- pub fn set_rowcol_str(&mut self, row: usize, col: usize, s: &str) {
- s.chars().enumerate().for_each(|(str_index, c)| self.set_rowcol(row, col+str_index, c));
- }
- pub fn randomly_add_words(&mut self, words: &[String]) -> Vec<Coord> {
- // Assumes configuration validation 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.capacity();
- if required_tiles > board_tiles {
- panic!("{} words of length {} requires {} tiles, but an {}x{} board only allows for {} tiles", words.len(), word_len, required_tiles, self.cols, self.rows, board_tiles);
- }
- let mut coords = self.generate_word_coords(words.len(), word_len);
- self.shake_coords(&mut coords);
- self.insert_words(words, &coords);
- coords
- }
- pub fn iter(&self) -> std::slice::Iter<char> {
- return self.c.iter();
- }
- pub fn dump(&self) -> String {
- let mut s = String::with_capacity(self.rows * self.cols);
- for row in 0..self.rows {
- for col in 0..self.cols {
- s.push(self.get_rowcol(row, col));
- }
- s.push('\n');
- }
- s
- }
- // Think of the board in word_len "chunks": place a slot for a word in each chunk
- fn generate_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);
- }
- self.shake_coords(&mut coords);
- 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);
- self.set_i_str(coord.start, word);
- }
- }
- }
|