Browse Source

Board -> Grid, almost finished building screen

Josh Bicking 4 years ago
parent
commit
6f85c5bade
6 changed files with 419 additions and 205 deletions
  1. 1 0
      .gitignore
  2. 173 0
      Cargo.lock
  3. 3 1
      Cargo.toml
  4. 34 204
      src/board.rs
  5. 200 0
      src/grid.rs
  6. 8 0
      src/main.rs

+ 1 - 0
.gitignore

@@ -1,3 +1,4 @@
 
 /target
 **/*.rs.bk
+fallout-terminal.log

+ 173 - 0
Cargo.lock

@@ -5,6 +5,36 @@ name = "argparse"
 version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "arrayref"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "arrayvec"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "autocfg"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "base64"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "blake2b_simd"
+version = "0.5.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "arrayref 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "c2-chacha"
 version = "0.2.3"
@@ -23,6 +53,51 @@ name = "cfg-if"
 version = "0.1.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "chrono"
+version = "0.4.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "constant_time_eq"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "dirs"
+version = "2.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "dirs-sys"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
+ "redox_users 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "dtoa"
 version = "0.4.4"
@@ -33,10 +108,12 @@ name = "fallout-terminal"
 version = "0.1.0"
 dependencies = [
  "argparse 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "pancurses 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_yaml 0.8.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "simplelog 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -49,6 +126,11 @@ dependencies = [
  "wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [[package]]
 name = "libc"
 version = "0.2.65"
@@ -77,6 +159,23 @@ dependencies = [
  "pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "num-integer"
+version = "0.1.42"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "pancurses"
 version = "0.16.1"
@@ -161,6 +260,32 @@ dependencies = [
  "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "redox_syscall"
+version = "0.1.56"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "redox_users"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rust-argon2 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rust-argon2"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "blake2b_simd 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "serde"
 version = "1.0.101"
@@ -190,6 +315,16 @@ dependencies = [
  "yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "simplelog"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "term 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "syn"
 version = "1.0.5"
@@ -200,6 +335,25 @@ dependencies = [
  "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "term"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "time"
+version = "0.1.42"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)",
+ "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "unicode-xid"
 version = "0.2.0"
@@ -247,15 +401,28 @@ dependencies = [
 
 [metadata]
 "checksum argparse 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3f8ebf5827e4ac4fd5946560e6a99776ea73b596d80898f357007317a7141e47"
+"checksum arrayref 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
+"checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
+"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
+"checksum base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"
+"checksum blake2b_simd 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a"
 "checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb"
 "checksum cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc9a35e1f4290eb9e5fc54ba6cf40671ed2a2514c3eeb2b2a908dda2ea5a1be"
 "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
+"checksum chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2"
+"checksum constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
+"checksum crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
+"checksum dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3"
+"checksum dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b"
 "checksum dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ea57b42383d091c85abcc2706240b94ab2a8fa1fc81c10ff23c4de06e2a90b5e"
 "checksum getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407"
+"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 "checksum libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)" = "1a31a0627fdf1f6a39ec0dd577e101440b7db22672c0901fe00a9a6fbb5c24e8"
 "checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83"
 "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
 "checksum ncurses 5.99.0 (registry+https://github.com/rust-lang/crates.io-index)" = "15699bee2f37e9f8828c7b35b2bc70d13846db453f2d507713b758fabe536b82"
+"checksum num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba"
+"checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096"
 "checksum pancurses 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d3058bc37c433096b2ac7afef1c5cdfae49ede0a4ffec3dfc1df1df0959d0ff0"
 "checksum pdcurses-sys 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "084dd22796ff60f1225d4eb6329f33afaf4c85419d51d440ab6b8c6f4529166b"
 "checksum pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "72d5370d90f49f70bd033c3d75e87fc529fbfff9d6f7cccef07d6170079d91ea"
@@ -266,10 +433,16 @@ dependencies = [
 "checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853"
 "checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
 "checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
+"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
+"checksum redox_users 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431"
+"checksum rust-argon2 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017"
 "checksum serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "9796c9b7ba2ffe7a9ce53c2287dfc48080f4b2b362fcc245a259b3a7201119dd"
 "checksum serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "4b133a43a1ecd55d4086bd5b4dc6c1751c68b1bfbeba7a5040442022c7e7c02e"
 "checksum serde_yaml 0.8.11 (registry+https://github.com/rust-lang/crates.io-index)" = "691b17f19fc1ec9d94ec0b5864859290dff279dbd7b03f017afda54eb36c3c35"
+"checksum simplelog 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "bcacac97349a890d437921dfb23cbec52ab5b4752551cb637df2721371acd467"
 "checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf"
+"checksum term 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c0863a3345e70f61d613eab32ee046ccd1bcc5f9105fe402c61fcd0c13eeb8b5"
+"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
 "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
 "checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d"
 "checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"

+ 3 - 1
Cargo.toml

@@ -8,4 +8,6 @@ pancurses = "0.16.0"
 argparse = "0.2.1"
 serde = { version = "1.0", features = ["derive"] }
 serde_yaml = "0.8"
-rand = "0.7.2"
+rand = "0.7.2"
+simplelog = "0.7.5"
+log = "0.4.8"

+ 34 - 204
src/board.rs

@@ -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() {

+ 200 - 0
src/grid.rs

@@ -0,0 +1,200 @@
+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
+    }
+
+    // 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);
+        }
+    }
+
+    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
+    }
+}

+ 8 - 0
src/main.rs

@@ -1,12 +1,20 @@
 extern crate argparse;
+extern crate simplelog;
 
 mod config;
 mod board;
 mod util;
+mod grid;
 
+#[macro_use] extern crate log;
 use self::argparse::{ArgumentParser, Store};
+use simplelog::{CombinedLogger,WriteLogger, LevelFilter, Config};
+
+use std::fs::File;
 
 fn main() {
+    // Logging
+    CombinedLogger::init(vec![WriteLogger::new(LevelFilter::Trace, Config::default(), File::create("fallout-terminal.log").unwrap())]).unwrap();
 
     // Argument handling
     let mut config_location = config::default_config_file();