grid.rs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. extern crate rand;
  2. use self::rand::Rng;
  3. use self::rand::prelude::SliceRandom;
  4. use util::{copy_shuffle};
  5. // The position of a word on the character grid.
  6. #[derive(Debug)]
  7. pub struct Coord {
  8. start: usize,
  9. end: usize
  10. }
  11. impl Coord {
  12. fn new(start: usize, len: usize) -> Coord {
  13. Coord{start:start, end:start+len-1}
  14. }
  15. }
  16. const DEFAULT_ROWS: usize = 17;
  17. const DEFAULT_COLS: usize = 24;
  18. const GARBAGE_CHARS: &'static [char] = &['!', '@', '#', '$', '%', '^', '*', '(', ')',
  19. '_', '-', '=', '+', '\\', '|', '/', '[', ']',
  20. '{', '}', '?', '\'', '"'];
  21. // An x by y grid of characters.
  22. #[derive(Debug)]
  23. pub struct Grid {
  24. pub rows: usize,
  25. pub cols: usize,
  26. c: Vec<char>
  27. }
  28. impl Grid {
  29. pub fn new(rows: Option<usize>, cols: Option<usize>) -> Grid {
  30. let r = match rows {
  31. Some(r) => r,
  32. None => DEFAULT_ROWS
  33. };
  34. let c = match cols {
  35. Some(c) => c,
  36. None => DEFAULT_COLS
  37. };
  38. Grid {
  39. rows: r,
  40. cols: c,
  41. c: vec![' '; r * c]
  42. }
  43. }
  44. pub fn fill_with_garbage(&mut self) {
  45. let mut rng = rand::thread_rng();
  46. for i in 0..self.rows * self.cols {
  47. self.set_i(i, *GARBAGE_CHARS.choose(&mut rng).unwrap());
  48. }
  49. }
  50. pub fn capacity(&self) -> usize {
  51. self.c.len()
  52. }
  53. pub fn get_i(&self, i: usize) -> char {
  54. self.c[i]
  55. }
  56. pub fn get_rowcol(&self, row: usize, col: usize) -> char {
  57. self.c[(row*self.cols)+col]
  58. }
  59. pub fn set_i(&mut self, i: usize, c: char) {
  60. // trace!("Setting {} to character {}", i, c);
  61. self.c[i] = c;
  62. }
  63. pub fn set_rowcol(&mut self, row: usize, col: usize, c: char) {
  64. self.set_i((row*self.cols)+col, c);
  65. }
  66. pub fn set_i_str(&mut self, i: usize, s: &str) {
  67. s.chars().enumerate().for_each(|(str_index, c)| self.set_i(i+str_index, c));
  68. }
  69. pub fn set_rowcol_str(&mut self, row: usize, col: usize, s: &str) {
  70. s.chars().enumerate().for_each(|(str_index, c)| self.set_rowcol(row, col+str_index, c));
  71. }
  72. pub fn randomly_add_words(&mut self, words: &[String]) -> Vec<Coord> {
  73. // Assumes configuration validation ensured `words` is not empty, and each word is the same length
  74. let word_len = words[0].len();
  75. let required_tiles = word_len * words.len() + (words.len() - 1);
  76. let board_tiles = self.capacity();
  77. if required_tiles > board_tiles {
  78. 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);
  79. }
  80. let mut coords = self.generate_word_coords(words.len(), word_len);
  81. self.shake_coords(&mut coords);
  82. self.insert_words(words, &coords);
  83. coords
  84. }
  85. // Think of the board in word_len "chunks": place a slot for a word in each chunk
  86. fn generate_word_coords(&self, num_of_words: usize, word_len: usize) -> Vec<Coord> {
  87. let chunk_len = self.capacity() / num_of_words;
  88. if chunk_len < word_len+1 {
  89. panic!("Can't fill board: need {}-character long spaces for\
  90. {}-character long words. But only have {}-character spaces\
  91. available. Please choose smaller words or increase board size.",
  92. word_len+1, word_len, chunk_len);
  93. }
  94. let mut coords: Vec<Coord> = Vec::with_capacity(num_of_words);
  95. let mut starting_coord: usize = 0;
  96. for _ in 0..num_of_words {
  97. let coord = Coord::new(starting_coord, word_len);
  98. starting_coord+=chunk_len;
  99. assert!(starting_coord > coord.end);
  100. coords.push(coord);
  101. }
  102. self.shake_coords(&mut coords);
  103. coords
  104. }
  105. // "shake" each word slot around, in a random order, 3 times/word.
  106. fn shake_coords(&self, word_coords: &mut Vec<Coord>) {
  107. let mut rng = rand::thread_rng();
  108. for _ in 0..3 {
  109. let indices: Vec<usize> = (0..word_coords.len()).collect();
  110. let shuffled_indices = copy_shuffle(&indices);
  111. for i in shuffled_indices {
  112. let coord = &word_coords[i];
  113. // Where can this coord move (leaving at least 1 space between words)?
  114. let mut movement_options = Vec::with_capacity(2);
  115. let start = coord.start as i32;
  116. let previous_coord_end: i32;
  117. if i == 0 {
  118. previous_coord_end = coord.start as i32;
  119. } else {
  120. let prev_coord = &word_coords[i-1];
  121. previous_coord_end = prev_coord.end as i32;
  122. }
  123. let space_behind = start - previous_coord_end;
  124. if space_behind > 1 {
  125. // It can move backwards
  126. movement_options.push((space_behind-1) * -1);
  127. }
  128. let next_coord_start: i32;
  129. let end = coord.end as i32;
  130. if i == word_coords.len()-1 {
  131. next_coord_start = self.capacity() as i32;
  132. } else {
  133. let next_coord = &word_coords[i+1];
  134. next_coord_start = next_coord.start as i32;
  135. }
  136. let space_in_front = next_coord_start - end;
  137. if space_in_front > 1 {
  138. // It can move forwards
  139. movement_options.push(space_in_front-1);
  140. }
  141. // Pick a movement option, & pick some amount to move
  142. let mv = match movement_options.choose(&mut rng) {
  143. None => continue,
  144. Some(m) => if *m > 0 {
  145. rng.gen_range(0, *m)
  146. } else {
  147. rng.gen_range(*m, 1)
  148. }
  149. };
  150. word_coords[i] = Coord{
  151. start: (coord.start as i32+mv) as usize,
  152. end: (coord.end as i32+mv) as usize,
  153. };
  154. }
  155. }
  156. }
  157. fn insert_words(&mut self, words: &[String], coords: &Vec<Coord>) {
  158. assert_eq!(words.len(), coords.len());
  159. for (word, coord) in words.iter().zip(coords.iter()) {
  160. assert_eq!(word.len(), coord.end - coord.start + 1);
  161. self.set_i_str(coord.start, word);
  162. }
  163. }
  164. pub fn dump(&self) -> String {
  165. let mut s = String::with_capacity(self.rows * self.cols);
  166. for row in 0..self.rows {
  167. for col in 0..self.cols {
  168. s.push(self.get_rowcol(row, col));
  169. }
  170. s.push('\n');
  171. }
  172. s
  173. }
  174. }