Josh Bicking 4 лет назад
Родитель
Сommit
e849780f3f
2 измененных файлов с 103 добавлено и 20 удалено
  1. 94 13
      src/board.rs
  2. 9 7
      src/config.rs

+ 94 - 13
src/board.rs

@@ -25,23 +25,35 @@ const DEFAULT_COLS: usize = 24;
 const TERMINAL_PRINTING_SPEED: time::Duration = time::Duration::from_millis(20);
 const TYPING_SPEED: time::Duration = time::Duration::from_millis(70);
 
+// Everything displayed to the player, + a reference to the curses window.
+#[derive(Debug)]
 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 {
@@ -71,6 +83,10 @@ impl Board {
         b
     }
 
+    fn capacity(&self) -> usize {
+        self.c.len()
+    }
+
     fn get(&self, i: usize) -> char {
         self.c[i]
     }
@@ -86,6 +102,73 @@ impl Board {
     fn setxy(&mut self, x: usize, y: usize, c: char) {
         self.c[x*y] = c;
     }
+
+    // Think of the board in word_len "chunks": place a slot for a word in each chunk
+    fn place_words(&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");
+        }
+        let mut coords: Vec<Coord> = Vec::with_capacity(num_of_words);
+        let mut starting_coord = 0;
+        for _ in 0..num_of_words {
+            let coord = Coord::new(starting_coord, word_len);
+            coords.push(coord);
+            starting_coord+=chunk_len;
+        }
+        coords
+    }
+
+    // "shake" each word slot around, in a random order, 3 times/word.
+    fn shake_words(&self, word_coords: &mut Vec<Coord>) {
+        let mut rng = rand::thread_rng();
+
+        for _ in 0..3 {
+            let mut positions: Vec<usize> = (0..word_coords.len()).collect();
+            positions.shuffle(&mut rng);
+            for i in positions {
+                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 mut start = coord.start as i32;
+                let mut end = -2;
+                if i > 0 {
+                    let prev_coord = &word_coords[i-1];
+                    end = prev_coord.end as i32;
+                }
+                if start - end > 1 {
+                    // It can move backwards
+                    movement_options.push((start-end-1) * -1);
+                }
+
+                start = self.capacity() as i32;
+                end = coord.end as i32;
+                if i < word_coords.len()-1 {
+                    let next_coord = &word_coords[i+1];
+                    start = next_coord.start as i32;
+                }
+                if start - end > 1 {
+                    // It can move forwards
+                    movement_options.push(start-end-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,
+                };
+            }
+        }
+    }
 }
 
 impl Screen {
@@ -123,17 +206,21 @@ impl Screen {
         let border_hex = border_hex_gen(self.b.rows);
 
         let shuffled_words = copy_shuffle(&conf.words);
+        let words = &shuffled_words[..conf.choose];
 
-        let word_len = conf.words[0].len();
         // Assumes validate() ensured `words` is not empty, and each word is the same length
-        let required_tiles = word_len * conf.words.len() + (conf.words.len() - 1);
-        let board_tiles = self.b.rows * self.b.cols;
+        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);
         }
 
-        name_this(&self.b);
-        // TODO: insert each word, of size N, into the array, with at least 1 character between each
+        let mut coords = self.b.place_words(words.len(), word_len);
+        self.b.shake_words(&mut coords);
+
+        println!("{:?}", coords);
+        println!("{:?}", self.b);
 
     }
 
@@ -179,16 +266,10 @@ fn border_hex_gen(rows: usize) -> Vec<u32> {
     let mut hex = rng.gen_range(0,200) + 63744;
 
     let mut ls = Vec::with_capacity(rows * 2);
-    for i in 0..rows * 2 {
-        ls[i] = hex;
+    for _ in 0..rows * 2 {
+        ls.push(hex);
         hex += 12;
     }
 
     ls
 }
-
-// Think of the board in word_len "chunks": place a word in each chunk. Then "shake" each one around a bit, in a random order, 3 times/word.
-fn name_this(b: &Board) -> Vec<Coord> {
-    // TODO
-    return Vec::new();
-}

+ 9 - 7
src/config.rs

@@ -11,16 +11,16 @@ use self::serde::{Deserialize};
 
 mod config {}
 
+// TODO: built-in validator.
+// For now, ensure any built Config has called validate()
 #[derive(Debug, PartialEq, Deserialize)]
 pub struct Config {
     pub words: Vec<String>,
-    pub choose: u32,
+    pub choose: usize,
     pub rows: Option<usize>,
     pub cols: Option<usize>
 }
 
-// const CONFIG_FOLDERS: &'static [&'static str] = &["XDG_CONFIG_HOME", "HOME"];
-
 pub fn default_config_file() -> String {
     let config_folders = [
         env::var("XDG_CONFIG_HOME"),
@@ -78,15 +78,17 @@ pub fn load_config_file(config_file_path: String) -> Config {
 
 fn validate(config: &Config) {
     if config.words.is_empty() {
-        println!("Config file validation error: word list must not be empty");
-        exit(1)
+        panic!("Config file validation error: word list must not be empty");
     }
 
     let first_len = config.words[0].len();
     for s in &config.words[1..] {
         if s.len() != first_len {
-            println!("Config file validation error: All words must be the same length [len({}) != len({})]", config.words[0], s);
-            exit(1)
+            panic!("Config file validation error: All words must be the same length [len({}) != len({})]", config.words[0], s);
         }
     }
+
+    if config.choose > config.words.len() {
+        panic!("'choose' value is greater than the number of available words.");
+    }
 }