about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorGriffin Smith <root@gws.fyi>2019-08-25T17·25-0400
committerGriffin Smith <root@gws.fyi>2019-08-25T17·25-0400
commitfb0d1b3e66251aa56a3df1d05fd4b82b33380a31 (patch)
tree367edbe5f504a3eb8130e5e0a8dd695fbbc65684 /src
parente2d2f011c6373894b3cdcfbdb98fbc783504561a (diff)
Wipe Rust project
Sorry rust, but you're just not fun to write
Diffstat (limited to '')
-rw-r--r--src/cli.yml46
-rw-r--r--src/description.rs93
-rw-r--r--src/display/color.rs163
-rw-r--r--src/display/draw_box.rs274
-rw-r--r--src/display/mod.rs52
-rw-r--r--src/display/utils.rs9
-rw-r--r--src/display/viewport.rs303
-rw-r--r--src/entities/character.rs51
-rw-r--r--src/entities/creature.rs63
-rw-r--r--src/entities/entity.rs125
-rw-r--r--src/entities/entity_char.rs24
-rw-r--r--src/entities/environment.rs36
-rw-r--r--src/entities/item.rs50
-rw-r--r--src/entities/mod.rs20
-rw-r--r--src/entities/raw_types.rs110
-rw-r--r--src/entities/raws.rs38
-rw-r--r--src/entities/raws/gormlak.toml10
-rw-r--r--src/entities/raws/noodles.json15
-rw-r--r--src/entities/util.rs72
-rw-r--r--src/game.rs617
-rw-r--r--src/level_gen/cave_automata.rs120
-rw-r--r--src/level_gen/mod.rs101
-rw-r--r--src/level_gen/util.rs52
-rw-r--r--src/main.rs130
-rw-r--r--src/messages.rs166
-rw-r--r--src/messages.toml27
-rw-r--r--src/settings.rs70
-rw-r--r--src/types/collision.rs9
-rw-r--r--src/types/command.rs41
-rw-r--r--src/types/direction.rs13
-rw-r--r--src/types/entity_map.rs430
-rw-r--r--src/types/menu.rs31
-rw-r--r--src/types/mod.rs504
-rw-r--r--src/util/mod.rs7
-rw-r--r--src/util/promise.rs160
-rw-r--r--src/util/static_cfg.rs147
-rw-r--r--src/util/template.rs362
-rw-r--r--src/util/trait_impls.rs17
38 files changed, 0 insertions, 4558 deletions
diff --git a/src/cli.yml b/src/cli.yml
deleted file mode 100644
index 4b2e94e57b..0000000000
--- a/src/cli.yml
+++ /dev/null
@@ -1,46 +0,0 @@
-name: xanthous
-version: "0.0"
-author: Griffin Smith <root@gws.fyi>
-about: hey, it's a terminal game
-args:
-  - config:
-      short: c
-      long: config
-      value_name: FILE
-      help: Sets a custom config file
-      takes_value: true
-subcommands:
-  - info:
-      about: Writes debug information to the terminal and exits
-  - generate-level:
-      about: Generate a level and print it to the screen
-      args:
-      - generator:
-          long: generator
-          value_name: GEN
-          help: Select which generator to use
-          takes_value: true
-      - width:
-          long: width
-          short: w
-          value_name: WIDTH
-          takes_value: true
-      - height:
-          long: height
-          short: h
-          value_name: HEIGHT
-          takes_value: true
-      - start-alive-chance:
-          long: start-alive-chance
-          takes_value: true
-      - birth_limit:
-          long: birth-limit
-          takes_value: true
-      - death_limit:
-          long: death-limit
-          takes_value: true
-      - steps:
-          long: steps
-          short: s
-          value_name: STEPS
-          takes_value: true
diff --git a/src/description.rs b/src/description.rs
deleted file mode 100644
index 48c98d76e0..0000000000
--- a/src/description.rs
+++ /dev/null
@@ -1,93 +0,0 @@
-use crate::entities::Describe;
-
-pub fn list_to_sentence(lst: &[String]) -> String {
-    let mut buf = String::with_capacity(
-        lst.iter()
-            .map(|e| e.len() + 2usize /* ", " */)
-            .sum::<usize>()
-            + if lst.len() >= 3 {
-                3usize /* "and" */
-            } else {
-                0usize
-            },
-    );
-
-    match lst.len() {
-        0 => {}
-        1 => buf.push_str(&lst[0]),
-        2 => {
-            buf.push_str(&lst[0]);
-            buf.push_str(" and ");
-            buf.push_str(&lst[1]);
-        }
-        _ => {
-            for desc in &lst[..lst.len() - 1] {
-                buf.push_str(desc);
-                buf.push_str(", ");
-            }
-            buf.push_str("and ");
-            buf.push_str(&lst[lst.len() - 1]);
-        }
-    }
-
-    buf
-}
-
-pub fn describe_list<A: Describe>(lst: &[A]) -> String {
-    list_to_sentence(
-        &lst.iter().map(|e| e.description()).collect::<Vec<String>>(),
-    )
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use proptest::prelude::*;
-    use proptest_derive::Arbitrary;
-
-    #[derive(Debug, Arbitrary)]
-    struct Description(String);
-
-    impl Describe for Description {
-        fn description(&self) -> String {
-            self.0.clone()
-        }
-    }
-
-    proptest! {
-        #[test]
-        fn test_describe_list_includes_all_descriptions(
-            descriptions: Vec<Description>
-        ) {
-            let res = describe_list(&descriptions);
-            for Description(desc) in descriptions {
-                assert!(res.contains(&desc));
-            }
-        }
-    }
-
-    #[test]
-    fn test_describe_list() {
-        assert_eq!(
-            describe_list(&[Description("one".to_string())]),
-            "one".to_string()
-        );
-
-        assert_eq!(
-            describe_list(&[
-                Description("one".to_string()),
-                Description("two".to_string())
-            ]),
-            "one and two".to_string()
-        );
-
-        assert_eq!(
-            describe_list(&[
-                Description("one".to_string()),
-                Description("two".to_string()),
-                Description("three".to_string())
-            ]),
-            "one, two, and three".to_string()
-        );
-    }
-}
diff --git a/src/display/color.rs b/src/display/color.rs
deleted file mode 100644
index afe0039998..0000000000
--- a/src/display/color.rs
+++ /dev/null
@@ -1,163 +0,0 @@
-use serde::de::{self, Unexpected, Visitor};
-use std::fmt;
-use std::marker::PhantomData;
-use termion::color;
-
-#[derive(Debug)]
-pub struct Color(Box<dyn color::Color>);
-
-unsafe impl Sync for Color {}
-unsafe impl Send for Color {}
-
-impl Color {
-    pub fn new<C: color::Color + 'static>(c: C) -> Self {
-        Color(Box::new(c))
-    }
-}
-
-impl PartialEq for Color {
-    fn eq(&self, other: &Self) -> bool {
-        format!("{}{}", color::Fg(self), color::Bg(self))
-            == format!("{}{}", color::Fg(other), color::Bg(other))
-    }
-}
-
-impl Eq for Color {}
-
-impl color::Color for Color {
-    fn write_fg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.0.write_fg(f)
-    }
-
-    fn write_bg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.0.write_bg(f)
-    }
-}
-
-impl<'a> color::Color for &'a Color {
-    fn write_fg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.0.write_fg(f)
-    }
-
-    fn write_bg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.0.write_bg(f)
-    }
-}
-
-impl Default for Color {
-    fn default() -> Self {
-        Color::new(color::Reset)
-    }
-}
-
-pub struct ColorVisitor {
-    marker: PhantomData<fn() -> Color>,
-}
-
-impl ColorVisitor {
-    fn new() -> Self {
-        ColorVisitor {
-            marker: PhantomData,
-        }
-    }
-}
-
-impl<'de> Visitor<'de> for ColorVisitor {
-    type Value = Color;
-
-    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
-        formatter.write_str("A color")
-    }
-
-    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
-    where
-        E: de::Error,
-    {
-        match v.to_lowercase().as_ref() {
-            "black" => Ok(Color(Box::new(color::Black))),
-            "blue" => Ok(Color(Box::new(color::Blue))),
-            "cyan" => Ok(Color(Box::new(color::Cyan))),
-            "green" => Ok(Color(Box::new(color::Green))),
-            "light black" | "light_black" => {
-                Ok(Color(Box::new(color::LightBlack)))
-            }
-            "light blue" | "light_blue" => {
-                Ok(Color(Box::new(color::LightBlue)))
-            }
-            "light cyan" | "light_cyan" => {
-                Ok(Color(Box::new(color::LightCyan)))
-            }
-            "light green" | "light_green" => {
-                Ok(Color(Box::new(color::LightGreen)))
-            }
-            "light magenta" | "light_magenta" => {
-                Ok(Color(Box::new(color::LightMagenta)))
-            }
-            "light red" | "light_red" => Ok(Color(Box::new(color::LightRed))),
-            "light white" | "light_white" => {
-                Ok(Color(Box::new(color::LightWhite)))
-            }
-            "light yellow" | "light_yellow" => {
-                Ok(Color(Box::new(color::LightYellow)))
-            }
-            "magenta" => Ok(Color(Box::new(color::Magenta))),
-            "red" => Ok(Color(Box::new(color::Red))),
-            "white" => Ok(Color(Box::new(color::White))),
-            "yellow" => Ok(Color(Box::new(color::Yellow))),
-            _ => Err(de::Error::invalid_value(
-                Unexpected::Str(v),
-                &"a valid color",
-            )),
-        }
-    }
-
-    fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
-    where
-        A: de::MapAccess<'de>,
-    {
-        let mut red = None;
-        let mut green = None;
-        let mut blue = None;
-        while let Some((k, v)) = map.next_entry()? {
-            match k {
-                "red" => {
-                    red = Some(v);
-                }
-                "green" => {
-                    green = Some(v);
-                }
-                "blue" => {
-                    blue = Some(v);
-                }
-                _ => {
-                    return Err(de::Error::unknown_field(
-                        k,
-                        &["red", "green", "blue"],
-                    ));
-                }
-            }
-        }
-
-        match (red, green, blue) {
-            (Some(r), Some(g), Some(b)) => {
-                Ok(Color(Box::new(color::Rgb(r, g, b))))
-            }
-            (None, _, _) => Err(de::Error::missing_field("red")),
-            (_, None, _) => Err(de::Error::missing_field("green")),
-            (_, _, None) => Err(de::Error::missing_field("blue")),
-        }
-    }
-
-    fn visit_u8<E: de::Error>(self, v: u8) -> Result<Self::Value, E> {
-        Ok(Color(Box::new(color::AnsiValue(v))))
-    }
-}
-
-impl<'de> serde::Deserialize<'de> for Color {
-    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
-    where
-        D: serde::Deserializer<'de>,
-    {
-        deserializer.deserialize_any(ColorVisitor::new())
-    }
-}
diff --git a/src/display/draw_box.rs b/src/display/draw_box.rs
deleted file mode 100644
index e4d34a7acd..0000000000
--- a/src/display/draw_box.rs
+++ /dev/null
@@ -1,274 +0,0 @@
-use crate::display::utils::clone_times;
-use crate::display::utils::times;
-use crate::types::pos;
-use crate::types::BoundingBox;
-use crate::types::Dimensions;
-use crate::types::Neighbors;
-use itertools::Itertools;
-use proptest::prelude::Arbitrary;
-use proptest::strategy;
-use proptest_derive::Arbitrary;
-use std::io::{self, Write};
-
-// Box Drawing
-//  	    0 	1 	2 	3 	4 	5 	6 	7 	8 	9 	A 	B 	C 	D 	E 	F
-// U+250x 	─ 	━ 	│ 	┃ 	┄ 	┅ 	┆ 	┇ 	┈ 	┉ 	┊ 	┋ 	┌ 	┍ 	┎ 	┏
-// U+251x 	┐ 	┑ 	┒ 	┓ 	└ 	┕ 	┖ 	┗ 	┘ 	┙ 	┚ 	┛ 	├ 	┝ 	┞ 	┟
-// U+252x 	┠ 	┡ 	┢ 	┣ 	┤ 	┥ 	┦ 	┧ 	┨ 	┩ 	┪ 	┫ 	┬ 	┭ 	┮ 	┯
-// U+253x 	┰ 	┱ 	┲ 	┳ 	┴ 	┵ 	┶ 	┷ 	┸ 	┹ 	┺ 	┻ 	┼ 	┽ 	┾ 	┿
-// U+254x 	╀ 	╁ 	╂ 	╃ 	╄ 	╅ 	╆ 	╇ 	╈ 	╉ 	╊ 	╋ 	╌ 	╍ 	╎ 	╏
-// U+255x 	═ 	║ 	╒ 	╓ 	╔ 	╕ 	╖ 	╗ 	╘ 	╙ 	╚ 	╛ 	╜ 	╝ 	╞ 	╟
-// U+256x 	╠ 	╡ 	╢ 	╣ 	╤ 	╥ 	╦ 	╧ 	╨ 	╩ 	╪ 	╫ 	╬ 	╭ 	╮ 	╯
-// U+257x 	╰ 	╱ 	╲ 	╳ 	╴ 	╵ 	╶ 	╷ 	╸ 	╹ 	╺ 	╻ 	╼ 	╽ 	╾ 	╿
-
-static BOX: char = '☐';
-
-static BOX_CHARS: [[char; 16]; 8] = [
-    // 0
-    [
-        // 0    1    2    3    4    5    6    7    8    9
-        '─', '━', '│', '┃', '┄', '┅', '┆', '┇', '┈', '┉',
-        // 10
-        '┊', '┋', '┌', '┍', '┎', '┏',
-    ],
-    // 1
-    [
-        // 0    1    2    3    4    5    6    7    8    9
-        '┐', '┑', '┒', '┓', '└', '┕', '┖', '┗', '┘', '┙',
-        '┚', '┛', '├', '┝', '┞', '┟',
-    ],
-    // 2
-    [
-        // 0    1    2    3    4    5    6    7    8    9
-        '┠', '┡', '┢', '┣', '┤', '┥', '┦', '┧', '┨', '┩',
-        '┪', '┫', '┬', '┭', '┮', '┯',
-    ],
-    // 3
-    [
-        // 0    1    2    3    4    5    6    7    8    9
-        '┰', '┱', '┲', '┳', '┴', '┵', '┶', '┷', '┸', '┹',
-        '┺', '┻', '┼', '┽', '┾', '┿',
-    ],
-    // 4
-    [
-        // 0    1    2    3    4    5    6    7    8    9
-        '╀', '╁', '╂', '╃', '╄', '╅', '╆', '╇', '╈', '╉',
-        '╊', '╋', '╌', '╍', '╎', '╏',
-    ],
-    // 5
-    [
-        // 0    1    2    3    4    5    6    7    8    9
-        '═', '║', '╒', '╓', '╔', '╕', '╖', '╗', '╘', '╙',
-        '╚', '╛', '╜', '╝', '╞', '╟',
-    ],
-    // 6
-    [
-        // 0    1    2    3    4    5    6    7    8    9
-        '╠', '╡', '╢', '╣', '╤', '╥', '╦', '╧', '╨', '╩',
-        '╪', '╫', '╬', '╭', '╮', '╯',
-    ],
-    // 7
-    [
-        // 0    1    2    3    4    5    6    7    8    9
-        '╰', '╱', '╲', '╳', '╴', '╵', '╶', '╷', '╸', '╹',
-        '╺', '╻', '╼', '╽', '╾', '╿',
-    ],
-];
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq)]
-pub enum BoxStyle {
-    Thin,
-    Thick,
-    Dotted,
-    ThickDotted,
-    Dashed,
-    ThickDashed,
-    Double,
-}
-
-impl Arbitrary for BoxStyle {
-    type Parameters = ();
-    type Strategy = strategy::Just<Self>;
-    fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
-        // TODO
-        strategy::Just(BoxStyle::Thin)
-    }
-}
-
-pub trait Stylable {
-    fn style(&self, style: BoxStyle) -> char;
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Arbitrary)]
-enum Corner {
-    TopRight,
-    TopLeft,
-    BottomRight,
-    BottomLeft,
-}
-
-impl Stylable for Corner {
-    fn style(&self, style: BoxStyle) -> char {
-        use BoxStyle::*;
-        use Corner::*;
-
-        match (self, style) {
-            (TopRight, Thin) => BOX_CHARS[1][0],
-            (TopLeft, Thin) => BOX_CHARS[0][12],
-            (BottomRight, Thin) => BOX_CHARS[1][8],
-            (BottomLeft, Thin) => BOX_CHARS[1][4],
-            _ => unimplemented!(),
-        }
-    }
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Arbitrary)]
-enum Line {
-    H,
-    V,
-}
-
-impl Stylable for Line {
-    fn style(&self, style: BoxStyle) -> char {
-        use BoxStyle::*;
-        use Line::*;
-        match (self, style) {
-            (H, Thin) => BOX_CHARS[0][0],
-            (V, Thin) => BOX_CHARS[0][2],
-            _ => unimplemented!(),
-        }
-    }
-}
-
-impl Stylable for Neighbors<Option<BoxStyle>> {
-    fn style(&self, _style: BoxStyle) -> char {
-        use BoxStyle::*;
-        match (self.left, self.right, self.top, self.bottom) {
-            (None, None, None, None) => BOX,
-            (Some(Thin), None, None, None) => BOX_CHARS[7][4],
-            (None, Some(Thin), None, None) => BOX_CHARS[7][6],
-            (None, None, Some(Thin), None) => BOX_CHARS[7][5],
-            (None, None, None, Some(Thin)) => BOX_CHARS[7][7],
-            (Some(Thin), Some(Thin), None, None) => Line::H.style(Thin),
-            (Some(Thin), None, Some(Thin), None) => {
-                Corner::BottomRight.style(Thin)
-            }
-            (Some(Thin), None, None, Some(Thin)) => {
-                Corner::TopRight.style(Thin)
-            }
-            (None, Some(Thin), Some(Thin), None) => {
-                Corner::BottomLeft.style(Thin)
-            }
-            (None, Some(Thin), None, Some(Thin)) => Corner::TopLeft.style(Thin),
-            (None, None, Some(Thin), Some(Thin)) => Line::V.style(Thin),
-            (None, Some(Thin), Some(Thin), Some(Thin)) => BOX_CHARS[1][12],
-            (Some(Thin), None, Some(Thin), Some(Thin)) => BOX_CHARS[2][4],
-            (Some(Thin), Some(Thin), None, Some(Thin)) => BOX_CHARS[2][12],
-            (Some(Thin), Some(Thin), Some(Thin), None) => BOX_CHARS[3][4],
-            (Some(Thin), Some(Thin), Some(Thin), Some(Thin)) => {
-                BOX_CHARS[3][12]
-            }
-            neighs => panic!("unimplemented: {:?}", neighs),
-        }
-    }
-}
-
-#[must_use]
-pub fn make_box(style: BoxStyle, dims: Dimensions) -> String {
-    if dims.h == 0 || dims.w == 0 {
-        "".to_string()
-    } else if dims.h == 1 && dims.w == 1 {
-        BOX.to_string()
-    } else if dims.h == 1 {
-        times(Line::H.style(style), dims.w)
-    } else if dims.w == 1 {
-        (0..dims.h).map(|_| Line::V.style(style)).join("\n\r")
-    } else {
-        let h_line: String = times(Line::H.style(style), dims.w - 2);
-        let v_line = Line::V.style(style);
-        let v_walls: String = clone_times(
-            format!(
-                "{}{}{}\n\r",
-                v_line,
-                times::<_, String>(' ', dims.w - 2),
-                v_line
-            ),
-            dims.h - 2,
-        );
-
-        format!(
-            "{}{}{}\n\r{}{}{}{}",
-            Corner::TopLeft.style(style),
-            h_line,
-            Corner::TopRight.style(style),
-            v_walls,
-            Corner::BottomLeft.style(style),
-            h_line,
-            Corner::BottomRight.style(style),
-        )
-    }
-}
-
-/// Draw the box described by the given BoundingBox's position and dimensions to
-/// the given output, with the given style
-pub fn draw_box<W: Write>(
-    out: &mut W,
-    bbox: BoundingBox,
-    style: BoxStyle,
-) -> io::Result<()> {
-    let box_str = make_box(style, bbox.dimensions);
-    if bbox.position.x == 0 {
-        write!(out, "{}{}", bbox.position.cursor_goto(), box_str)?;
-    } else {
-        for (i, line) in box_str.split("\n\r").enumerate() {
-            debug!("line: {:?}!", line);
-            write!(
-                out,
-                "{}{}",
-                (bbox.position + pos(0, i as i16)).cursor_goto(),
-                line
-            )?;
-        }
-    }
-    Ok(())
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use proptest::prelude::*;
-
-    #[test]
-    fn make_thin_box() {
-        let res = make_box(BoxStyle::Thin, Dimensions { w: 10, h: 10 });
-        assert_eq!(
-            res,
-            "┌────────┐
-\r│        │
-\r│        │
-\r│        │
-\r│        │
-\r│        │
-\r│        │
-\r│        │
-\r│        │
-\r└────────┘"
-        );
-    }
-
-    proptest! {
-        #[test]
-        fn box_has_height_lines(dims: Dimensions, style: BoxStyle) {
-            let res = make_box(style, dims);
-            prop_assume!((dims.w > 0 && dims.h > 0));
-            assert_eq!(res.split("\n\r").count(), dims.h as usize);
-        }
-
-        #[test]
-        fn box_lines_have_width_length(dims: Dimensions, style: BoxStyle) {
-            let res = make_box(style, dims);
-            prop_assume!(dims.w == 0 && dims.h == 0 || (dims.w > 0 && dims.h > 0));
-            assert!(res.split("\n\r").all(|l| l.chars().count() == dims.w as usize));
-        }
-    }
-}
diff --git a/src/display/mod.rs b/src/display/mod.rs
deleted file mode 100644
index 6e37a03d8c..0000000000
--- a/src/display/mod.rs
+++ /dev/null
@@ -1,52 +0,0 @@
-pub mod color;
-pub mod draw_box;
-pub mod utils;
-pub mod viewport;
-use crate::entities::entity::Entity;
-use crate::types::Neighbors;
-use crate::types::Positioned;
-pub use draw_box::{make_box, BoxStyle};
-use std::io::{self, Write};
-use termion::{clear, cursor, style};
-pub use viewport::Viewport;
-
-pub fn clear<T: Write>(out: &mut T) -> io::Result<()> {
-    write!(out, "{}{}{}", clear::All, style::Reset, cursor::Goto(1, 1))
-}
-
-pub trait Draw: Positioned {
-    /// Draw this entity, assuming the character is already at the correct
-    /// position
-    fn do_draw(&self, out: &mut dyn Write) -> io::Result<()>;
-}
-
-impl<T: Draw> Draw for &T {
-    fn do_draw(&self, out: &mut dyn Write) -> io::Result<()> {
-        (**self).do_draw(out)
-    }
-}
-
-impl<T: Draw> Draw for Box<T> {
-    fn do_draw(&self, out: &mut dyn Write) -> io::Result<()> {
-        (**self).do_draw(out)
-    }
-}
-
-pub trait DrawWithNeighbors: Positioned {
-    #[allow(clippy::borrowed_box)]
-    fn do_draw_with_neighbors<'a, 'b>(
-        &'a self,
-        out: &'b mut dyn Write,
-        neighbors: &'a Neighbors<Vec<&'a Box<dyn Entity>>>,
-    ) -> io::Result<()>;
-}
-
-impl<T: Draw> DrawWithNeighbors for T {
-    fn do_draw_with_neighbors<'a, 'b>(
-        &'a self,
-        out: &'b mut dyn Write,
-        _neighbors: &'a Neighbors<Vec<&'a Box<dyn Entity>>>,
-    ) -> io::Result<()> {
-        self.do_draw(out)
-    }
-}
diff --git a/src/display/utils.rs b/src/display/utils.rs
deleted file mode 100644
index acd4416cb8..0000000000
--- a/src/display/utils.rs
+++ /dev/null
@@ -1,9 +0,0 @@
-use std::iter::FromIterator;
-
-pub fn times<A: Copy, B: FromIterator<A>>(elem: A, n: u16) -> B {
-    (0..n).map(|_| elem).collect()
-}
-
-pub fn clone_times<A: Clone, B: FromIterator<A>>(elem: A, n: u16) -> B {
-    (0..n).map(|_| elem.clone()).collect()
-}
diff --git a/src/display/viewport.rs b/src/display/viewport.rs
deleted file mode 100644
index c44316cdaa..0000000000
--- a/src/display/viewport.rs
+++ /dev/null
@@ -1,303 +0,0 @@
-use super::BoxStyle;
-use super::DrawWithNeighbors;
-use crate::display::draw_box::draw_box;
-use crate::display::utils::clone_times;
-use crate::entities::entity::Entity;
-use crate::types::menu::MenuInfo;
-use crate::types::Neighbors;
-use crate::types::{pos, BoundingBox, Direction, Position, Positioned};
-use std::fmt::{self, Debug};
-use std::io::{self, Write};
-
-pub enum CursorState {
-    Game,
-    Prompt(Position),
-}
-
-impl Default for CursorState {
-    fn default() -> Self {
-        CursorState::Game
-    }
-}
-
-pub struct Viewport<W> {
-    /// The box describing the visible part of the viewport.
-    ///
-    /// Generally the size of the terminal, and positioned at 0, 0
-    pub outer: BoundingBox,
-
-    /// The box describing the game part of the viewport.
-    pub game: BoundingBox,
-
-    /// The box describing the inner part of the viewport
-    ///
-    /// Its position is relative to `outer.inner()`, and its size should
-    /// generally not be smaller than outer
-    pub inner: BoundingBox,
-
-    /// The actual screen that the viewport writes to
-    pub out: W,
-
-    cursor_state: CursorState,
-
-    /// Reset the cursor back to this position after every draw
-    pub game_cursor_position: Position,
-}
-
-impl<W> Viewport<W> {
-    pub fn new(outer: BoundingBox, inner: BoundingBox, out: W) -> Self {
-        Viewport {
-            outer,
-            inner,
-            out,
-            game: outer.move_tr_corner(Position { x: 0, y: 1 }),
-            cursor_state: Default::default(),
-            game_cursor_position: pos(0, 0),
-        }
-    }
-
-    /// Returns true if the (inner-relative) position of the given entity is
-    /// visible within this viewport
-    pub fn visible<E: Positioned>(&self, ent: &E) -> bool {
-        self.on_screen(ent.position()).within(self.game.inner())
-    }
-
-    /// Convert the given inner-relative position to one on the actual screen
-    fn on_screen(&self, pos: Position) -> Position {
-        pos + self.inner.position + self.game.inner().position
-    }
-}
-
-impl<W> Debug for Viewport<W> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(
-            f,
-            "Viewport {{ outer: {:?}, inner: {:?}, out: <OUT> }}",
-            self.outer, self.inner
-        )
-    }
-}
-
-impl<W: Write> Viewport<W> {
-    /// Draw the given entity to the viewport at its position, if visible
-    #[allow(clippy::borrowed_box)]
-    pub fn draw<T: DrawWithNeighbors>(
-        &mut self,
-        entity: &T,
-        neighbors: &Neighbors<Vec<&Box<dyn Entity>>>,
-    ) -> io::Result<()> {
-        if !self.visible(entity) {
-            return Ok(());
-        }
-        self.cursor_goto(entity.position())?;
-        entity.do_draw_with_neighbors(self, neighbors)?;
-        self.reset_cursor()
-    }
-
-    fn reset_cursor(&mut self) -> io::Result<()> {
-        self.cursor_goto(self.game_cursor_position)
-    }
-
-    /// Move the cursor to the given inner-relative position
-    pub fn cursor_goto(&mut self, pos: Position) -> io::Result<()> {
-        write!(self, "{}", self.on_screen(pos).cursor_goto())
-    }
-
-    /// Clear whatever single character is drawn at the given inner-relative
-    /// position, if visible
-    pub fn clear(&mut self, pos: Position) -> io::Result<()> {
-        write!(self, "{} ", self.on_screen(pos).cursor_goto(),)?;
-        self.reset_cursor()
-    }
-
-    /// Initialize this viewport by drawing its outer box to the screen
-    pub fn init(&mut self) -> io::Result<()> {
-        draw_box(self, self.game, BoxStyle::Thin)
-    }
-
-    /// Write a message to the message area on the screen
-    ///
-    /// Will overwrite any message already present, and if the given message is
-    /// longer than the screen will truncate. This means callers should handle
-    /// message buffering and ellipsisization
-    pub fn write_message(&mut self, msg: &str) -> io::Result<usize> {
-        let msg_to_write = if msg.len() <= self.outer.dimensions.w as usize {
-            msg
-        } else {
-            &msg[0..self.outer.dimensions.w as usize]
-        };
-        write!(
-            self,
-            "{}{}{}",
-            self.outer.position.cursor_goto(),
-            msg_to_write,
-            clone_times::<_, String>(
-                " ".to_string(),
-                self.outer.dimensions.w - msg.len() as u16
-            ),
-        )?;
-        self.reset_cursor()?;
-        Ok(msg_to_write.len())
-    }
-
-    pub fn clear_message(&mut self) -> io::Result<()> {
-        write!(
-            self,
-            "{}{}",
-            self.outer.position.cursor_goto(),
-            clone_times::<_, String>(
-                " ".to_string(),
-                self.outer.dimensions.w as u16
-            )
-        )?;
-        self.reset_cursor()
-    }
-
-    /// Write a prompt requesting text input to the message area on the screen.
-    ///
-    /// Will overwrite any message already present, and if the given message is
-    /// longer than the screen will truncate. This means callers should handle
-    /// message buffering and ellipsisization
-    pub fn write_prompt<'a, 'b>(&'a mut self, msg: &'b str) -> io::Result<()> {
-        let len = self.write_message(msg)? + 1;
-        let pos = self.outer.position + pos(len as i16, 0);
-        self.cursor_state = CursorState::Prompt(pos);
-        write!(self, "{}", pos.cursor_goto())?;
-        self.flush()
-    }
-
-    pub fn push_prompt_chr(&mut self, chr: char) -> io::Result<()> {
-        if let CursorState::Prompt(pos) = self.cursor_state {
-            write!(self, "{}", chr)?;
-            self.cursor_state = CursorState::Prompt(pos + Direction::Right);
-        }
-        Ok(())
-    }
-
-    pub fn pop_prompt_chr(&mut self) -> io::Result<()> {
-        if let CursorState::Prompt(pos) = self.cursor_state {
-            let new_pos = pos + Direction::Left;
-            write!(
-                self,
-                "{} {}",
-                new_pos.cursor_goto(),
-                new_pos.cursor_goto()
-            )?;
-            self.cursor_state = CursorState::Prompt(new_pos);
-        }
-        Ok(())
-    }
-
-    pub fn clear_prompt(&mut self) -> io::Result<()> {
-        self.clear_message()?;
-        self.cursor_state = CursorState::Game;
-        Ok(())
-    }
-
-    pub fn write_menu(&mut self, menu: &MenuInfo) -> io::Result<()> {
-        let menu_dims = menu.dimensions();
-
-        // TODO: check if the menu is too big
-
-        let menu_position = self.game.position + pos(1, 1);
-
-        let menu_box = BoundingBox {
-            dimensions: menu_dims,
-            position: menu_position,
-        };
-
-        debug!("writing menu at: {:?}", menu_box);
-
-        draw_box(self, menu_box, BoxStyle::Thin)?;
-
-        write!(
-            self,
-            "{}{}",
-            (menu_position + pos(2, 2)).cursor_goto(),
-            menu.prompt
-        )?;
-
-        for (idx, option) in menu.options.iter().enumerate() {
-            write!(
-                self,
-                "{}{}",
-                (menu_position + pos(2, 4 + idx as i16)).cursor_goto(),
-                option
-            )?;
-        }
-
-        Ok(())
-    }
-}
-
-impl<W> Positioned for Viewport<W> {
-    fn position(&self) -> Position {
-        self.outer.position
-    }
-}
-
-impl<W: Write> Write for Viewport<W> {
-    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
-        self.out.write(buf)
-    }
-
-    fn flush(&mut self) -> io::Result<()> {
-        self.out.flush()
-    }
-
-    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
-        self.out.write_all(buf)
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::types::Dimensions;
-
-    #[test]
-    fn test_visible() {
-        assert!(Viewport::new(
-            BoundingBox::at_origin(Dimensions { w: 10, h: 10 }),
-            BoundingBox {
-                position: Position { x: -10, y: -10 },
-                dimensions: Dimensions { w: 15, h: 15 },
-            },
-            ()
-        )
-        .visible(&Position { x: 13, y: 13 }));
-
-        assert!(!Viewport::new(
-            BoundingBox::at_origin(Dimensions { w: 10, h: 10 }),
-            BoundingBox {
-                position: Position { x: -10, y: -10 },
-                dimensions: Dimensions { w: 15, h: 15 },
-            },
-            (),
-        )
-        .visible(&Position { x: 1, y: 1 }));
-    }
-
-    #[test]
-    fn test_write_menu() {
-        let buf: Vec<u8> = Vec::new();
-
-        let mut viewport = Viewport::new(
-            BoundingBox::at_origin(Dimensions::default()),
-            BoundingBox::at_origin(Dimensions::default()),
-            buf,
-        );
-
-        let menu = MenuInfo::new(
-            "Test menu".to_string(),
-            vec!["option 1".to_string(), "option 2".to_string()],
-        );
-
-        viewport.write_menu(&menu).unwrap();
-
-        let res = std::str::from_utf8(&viewport.out).unwrap();
-        assert!(res.contains("Test menu"));
-        assert!(res.contains("option 1"));
-        assert!(res.contains("option 2"));
-    }
-}
diff --git a/src/entities/character.rs b/src/entities/character.rs
deleted file mode 100644
index 3e8336b129..0000000000
--- a/src/entities/character.rs
+++ /dev/null
@@ -1,51 +0,0 @@
-use crate::display;
-use crate::entities::item::Item;
-use crate::types::{Position, Speed};
-use std::io::{self, Write};
-
-const DEFAULT_SPEED: Speed = Speed(100);
-
-entity! {
-    pub struct Character {
-        pub o_name: Option<String>,
-        pub inventory: Vec<Box<Item>>,
-    }
-}
-
-static_description!(Character, "yourself");
-
-impl Character {
-    pub fn new() -> Character {
-        Character {
-            id: None,
-            position: Position { x: 0, y: 0 },
-            o_name: None,
-            inventory: Vec::new(),
-        }
-    }
-
-    pub fn speed(&self) -> Speed {
-        Speed(100)
-    }
-
-    pub fn damage(&self) -> u16 {
-        // TODO
-        1
-    }
-
-    pub fn name(&self) -> &str {
-        self.o_name
-            .as_ref()
-            .expect("Character name not initialized")
-    }
-
-    pub fn set_name(&mut self, name: String) {
-        self.o_name = Some(name);
-    }
-}
-
-impl display::Draw for Character {
-    fn do_draw(&self, out: &mut dyn Write) -> io::Result<()> {
-        write!(out, "@")
-    }
-}
diff --git a/src/entities/creature.rs b/src/entities/creature.rs
deleted file mode 100644
index 20071c1d88..0000000000
--- a/src/entities/creature.rs
+++ /dev/null
@@ -1,63 +0,0 @@
-use crate::display;
-use crate::entities::raws::CreatureType;
-use crate::entities::raws::EntityRaw;
-use crate::entities::{raw, Describe, EntityID};
-use crate::types::Position;
-use std::io::{self, Write};
-
-#[derive(Debug, Clone)]
-pub struct Creature {
-    pub id: Option<EntityID>,
-    pub typ: &'static CreatureType<'static>,
-    pub position: Position,
-    pub hitpoints: u16,
-}
-
-impl Creature {
-    pub fn new_from_raw(name: &'static str, position: Position) -> Self {
-        match raw(name) {
-            EntityRaw::Creature(typ) => Self::new_with_type(typ, position),
-            _ => panic!("Invalid raw type for {:?}, expected Creature", name),
-        }
-    }
-
-    pub fn new_with_type(
-        typ: &'static CreatureType<'static>,
-        position: Position,
-    ) -> Self {
-        Creature {
-            id: None,
-            typ,
-            position,
-            hitpoints: typ.max_hitpoints,
-        }
-    }
-
-    /// Damage the given creature by the given amount
-    pub fn damage(&mut self, amount: u16) {
-        if self.hitpoints <= amount {
-            self.hitpoints = 0;
-        } else {
-            self.hitpoints -= amount;
-        }
-    }
-
-    /// Returns true if this creature has died
-    pub fn dead(&self) -> bool {
-        self.hitpoints == 0
-    }
-}
-
-entity!(Creature);
-
-impl Describe for Creature {
-    fn description(&self) -> String {
-        self.typ.description.to_string()
-    }
-}
-
-impl display::Draw for Creature {
-    fn do_draw(&self, out: &mut dyn Write) -> io::Result<()> {
-        write!(out, "{}", self.typ.chr)
-    }
-}
diff --git a/src/entities/entity.rs b/src/entities/entity.rs
deleted file mode 100644
index 01075d298f..0000000000
--- a/src/entities/entity.rs
+++ /dev/null
@@ -1,125 +0,0 @@
-use crate::display::DrawWithNeighbors;
-use crate::entities::EntityID;
-use crate::types::Neighbors;
-use crate::types::Position;
-use crate::types::{Positioned, PositionedMut};
-use downcast_rs::Downcast;
-use std::fmt::Debug;
-use std::io::{self, Write};
-
-pub trait Identified<ID>: Debug {
-    fn opt_id(&self) -> Option<ID>;
-    fn set_id(&mut self, id: ID);
-
-    fn id(&self) -> ID {
-        self.opt_id()
-            .unwrap_or_else(|| panic!("Entity ({:?}) is not in the game", self))
-    }
-}
-
-impl<'a, A, ID> Identified<ID> for &'a mut A
-where
-    A: Identified<ID>,
-{
-    fn opt_id(&self) -> Option<ID> {
-        (**self).opt_id()
-    }
-    fn set_id(&mut self, id: ID) {
-        (**self).set_id(id);
-    }
-}
-
-impl<ID, A: Identified<ID>> Identified<ID> for Box<A> {
-    fn opt_id(&self) -> Option<ID> {
-        (**self).opt_id()
-    }
-    fn set_id(&mut self, id: ID) {
-        (**self).set_id(id);
-    }
-}
-
-pub trait Describe {
-    fn description(&self) -> String;
-}
-
-ref_impl! {
-    impl<T: Describe> Describe for &T {
-        fn description(&self) -> String {
-            (**self).description()
-        }
-    }
-}
-
-#[macro_export]
-macro_rules! static_description {
-    ($name: ident, $description: expr) => {
-        impl $crate::entities::entity::Describe for $name {
-            fn description(&self) -> String {
-                $description.to_string()
-            }
-        }
-    };
-}
-
-pub trait Entity:
-    Positioned
-    + PositionedMut
-    + Identified<EntityID>
-    + DrawWithNeighbors
-    + Downcast
-    + Describe
-{
-}
-
-impl Identified<EntityID> for Box<dyn Entity> {
-    fn opt_id(&self) -> Option<EntityID> {
-        (**self).opt_id()
-    }
-    fn set_id(&mut self, id: EntityID) {
-        (**self).set_id(id);
-    }
-}
-
-#[macro_export]
-macro_rules! identified {
-    ($name: ident, $typ: path) => {
-        identified!($name, $typ, id);
-    };
-    ($name: ident, $typ: path, $attr: ident) => {
-        impl crate::entities::entity::Identified<$typ> for $name {
-            fn opt_id(&self) -> Option<$typ> {
-                self.$attr
-            }
-
-            fn set_id(&mut self, id: $typ) {
-                self.$attr = Some(id)
-            }
-        }
-    };
-}
-
-impl_downcast!(Entity);
-
-impl DrawWithNeighbors for Box<dyn Entity> {
-    fn do_draw_with_neighbors<'a, 'b>(
-        &'a self,
-        out: &'b mut dyn Write,
-        neighbors: &'a Neighbors<Vec<&'a Box<dyn Entity>>>,
-    ) -> io::Result<()> {
-        (**self).do_draw_with_neighbors(out, neighbors)
-    }
-}
-
-pub type AnEntity = Box<dyn Entity>;
-
-impl Positioned for AnEntity {
-    fn position(&self) -> Position {
-        (**self).position()
-    }
-}
-
-impl PositionedMut for AnEntity {
-    fn set_position(&mut self, pos: Position) {
-        (**self).set_position(pos)
-    }
-}
diff --git a/src/entities/entity_char.rs b/src/entities/entity_char.rs
deleted file mode 100644
index 70f26bfffd..0000000000
--- a/src/entities/entity_char.rs
+++ /dev/null
@@ -1,24 +0,0 @@
-use crate::display::color::Color;
-use std::fmt::{self, Display, Formatter};
-use termion::color;
-
-#[derive(Debug, Deserialize, PartialEq, Eq)]
-pub struct EntityChar {
-    #[serde(default)]
-    color: Color,
-
-    #[serde(rename = "char")]
-    chr: char,
-}
-
-impl Display for EntityChar {
-    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
-        write!(
-            f,
-            "{}{}{}",
-            color::Fg(&self.color),
-            self.chr,
-            color::Fg(color::Reset)
-        )
-    }
-}
diff --git a/src/entities/environment.rs b/src/entities/environment.rs
deleted file mode 100644
index 8f8a567062..0000000000
--- a/src/entities/environment.rs
+++ /dev/null
@@ -1,36 +0,0 @@
-use crate::display;
-use crate::display::draw_box::{BoxStyle, Stylable};
-use crate::entities::Entity;
-use crate::types::{Neighbors, Position};
-use std::io::{self, Write};
-
-entity! {
-    pub struct Wall {
-        pub style: BoxStyle
-    }
-}
-
-static_description!(Wall, "a wall");
-
-impl Wall {
-    pub fn new(position: Position, style: BoxStyle) -> Self {
-        new_entity!(Wall { position, style })
-    }
-}
-
-impl display::DrawWithNeighbors for Wall {
-    fn do_draw_with_neighbors<'a, 'b>(
-        &'a self,
-        out: &'b mut dyn Write,
-        neighbors: &'a Neighbors<Vec<&'a Box<dyn Entity>>>,
-    ) -> io::Result<()> {
-        let neighbor_styles: Neighbors<Option<BoxStyle>> =
-            neighbors.map(|es| {
-                es.iter()
-                    .filter_map(|e| e.downcast_ref::<Wall>())
-                    .map(|wall| wall.style)
-                    .next()
-            });
-        write!(out, "{}", neighbor_styles.style(self.style))
-    }
-}
diff --git a/src/entities/item.rs b/src/entities/item.rs
deleted file mode 100644
index 5f08780d4f..0000000000
--- a/src/entities/item.rs
+++ /dev/null
@@ -1,50 +0,0 @@
-use crate::display;
-use crate::entities::raws::{raw, EntityRaw, ItemType};
-use crate::entities::{Describe, EntityID};
-use crate::types::Position;
-use std::io::{self, Write};
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct Item {
-    pub id: Option<EntityID>,
-    pub typ: &'static ItemType<'static>,
-    pub position: Position,
-}
-
-impl Item {
-    pub fn new_from_raw(name: &'static str, position: Position) -> Self {
-        match raw(name) {
-            EntityRaw::Item(typ) => Self::new_with_type(typ, position),
-            _ => panic!("Invalid raw type for {:?}, expected Item", name),
-        }
-    }
-
-    pub fn new_with_type(
-        typ: &'static ItemType<'static>,
-        position: Position,
-    ) -> Self {
-        Item {
-            id: None,
-            typ,
-            position,
-        }
-    }
-
-    pub fn is_edible(&self) -> bool {
-        self.typ.is_edible()
-    }
-}
-
-entity!(Item);
-
-impl Describe for Item {
-    fn description(&self) -> String {
-        self.typ.description.to_string()
-    }
-}
-
-impl display::Draw for Item {
-    fn do_draw(&self, out: &mut dyn Write) -> io::Result<()> {
-        write!(out, "{}", self.typ.chr)
-    }
-}
diff --git a/src/entities/mod.rs b/src/entities/mod.rs
deleted file mode 100644
index a8c39ed8aa..0000000000
--- a/src/entities/mod.rs
+++ /dev/null
@@ -1,20 +0,0 @@
-#[macro_use]
-pub mod entity;
-#[macro_use]
-pub mod util;
-pub mod character;
-pub mod creature;
-pub mod entity_char;
-pub mod environment;
-pub mod item;
-pub mod raw_types;
-pub mod raws;
-
-pub use character::Character;
-pub use creature::Creature;
-pub use entity::{AnEntity, Describe, Entity, Identified};
-pub use entity_char::EntityChar;
-pub use item::Item;
-pub use raws::raw;
-
-pub type EntityID = u32;
diff --git a/src/entities/raw_types.rs b/src/entities/raw_types.rs
deleted file mode 100644
index 4bc291b695..0000000000
--- a/src/entities/raw_types.rs
+++ /dev/null
@@ -1,110 +0,0 @@
-use crate::entities::entity_char::EntityChar;
-use crate::messages::Message;
-use crate::types::Speed;
-
-#[derive(Debug, Deserialize)]
-pub struct CreatureType<'a> {
-    /// The name of the creature. Used in raw lookups.
-    pub name: &'a str,
-
-    /// A description of the entity, used by the "look" command
-    pub description: &'a str,
-
-    #[serde(rename = "char")]
-    pub chr: EntityChar,
-    pub max_hitpoints: u16,
-    pub speed: Speed,
-    pub friendly: bool,
-}
-
-#[derive(Debug, Deserialize, PartialEq, Eq)]
-pub struct EdibleItem<'a> {
-    #[serde(borrow)]
-    pub eat_message: Option<Message<'a>>,
-
-    /// The number of hitpoints that eating this item heals
-    pub hitpoints_healed: u16,
-}
-
-#[derive(Debug, Deserialize, PartialEq, Eq)]
-pub struct ItemType<'a> {
-    pub name: &'a str,
-
-    /// A description of the item, used by the "look" command and when walking
-    /// over the item on the ground
-    pub description: &'a str,
-
-    /// A longer description of the item
-    pub long_description: &'a str,
-
-    pub edible_item: Option<EdibleItem<'a>>,
-
-    #[serde(rename = "char")]
-    pub chr: EntityChar,
-}
-
-#[cfg(test)]
-mod item_type_tests {
-    use super::*;
-
-    #[test]
-    fn test_deserialize_item_type() {
-        let result = serde_json::from_str(
-            r#"{
-                "Item": {
-                    "name": "noodles",
-                    "description": "a big bowl o' noodles",
-                    "long_description": "You know exactly what kind of noodles",
-                    "char": { "char": "n" },
-                    "edible_item": {
-                        "eat_message": "You slurp up the noodles",
-                        "hitpoints_healed": 2
-                    }
-                }
-            }"#,
-        )
-        .unwrap();
-        assert_matches!(result, EntityRaw::Item(_));
-        if let EntityRaw::Item(item) = result {
-            assert_eq!(item.name, "noodles");
-        }
-
-        let toml_result = toml::from_str(
-            r#"[Item]
-name = "noodles"
-description = "a big bowl o' noodles"
-long_description = "You know exactly what kind of noodles"
-char = { char = "🍜" }
-edible_item = { eat_message = "You slurp up the noodles", hitpoints_healed = 2 }
-"#,
-        )
-        .unwrap();
-
-        assert_matches!(toml_result, EntityRaw::Item(_));
-        if let EntityRaw::Item(item) = toml_result {
-            assert_eq!(item.name, "noodles");
-        }
-    }
-}
-
-impl<'a> ItemType<'a> {
-    pub fn is_edible(&self) -> bool {
-        self.edible_item.is_some()
-    }
-}
-
-#[derive(Debug, Deserialize)]
-pub enum EntityRaw<'a> {
-    Creature(#[serde(borrow)] CreatureType<'a>),
-    Item(#[serde(borrow)] ItemType<'a>),
-}
-
-impl<'a> EntityRaw<'a> {
-    pub fn name(&self) -> &'a str {
-        use EntityRaw::*;
-        match self {
-            Creature(typ) => typ.name,
-            Item(typ) => typ.name,
-        }
-    }
-}
diff --git a/src/entities/raws.rs b/src/entities/raws.rs
deleted file mode 100644
index 061e29a840..0000000000
--- a/src/entities/raws.rs
+++ /dev/null
@@ -1,38 +0,0 @@
-pub use crate::entities::raw_types::{CreatureType, EntityRaw, ItemType};
-use std::collections::HashMap;
-
-static_cfg! {
-    static ref RAWS: Vec<EntityRaw<'static>> = cfg_dir("src/entities/raws");
-}
-
-lazy_static! {
-    static ref RAWS_BY_NAME: HashMap<&'static str, &'static EntityRaw<'static>> = {
-        let mut hm = HashMap::new();
-        for er in RAWS.iter() {
-            if hm.contains_key(er.name()) {
-                panic!("Duplicate entity: {}", er.name())
-            }
-
-            hm.insert(er.name(), er);
-        }
-        hm
-    };
-}
-
-pub fn raw(name: &'static str) -> &'static EntityRaw<'static> {
-    RAWS_BY_NAME
-        .get(name)
-        .copied()
-        .unwrap_or_else(|| panic!("Raw not found: {}", name))
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    fn test_raws() {
-        RAWS_BY_NAME.keys();
-        assert_eq!(raw("noodles").name(), "noodles");
-    }
-}
diff --git a/src/entities/raws/gormlak.toml b/src/entities/raws/gormlak.toml
deleted file mode 100644
index be30362d25..0000000000
--- a/src/entities/raws/gormlak.toml
+++ /dev/null
@@ -1,10 +0,0 @@
-[Creature]
-name = "gormlak"
-description = """
-A chittering imp-like creature with bright yellow horns. It adores shiny objects
-and gathers in swarms.
-"""
-char = { char = "g", color = "red" }
-max_hitpoints = 5
-speed = 120
-friendly = false
diff --git a/src/entities/raws/noodles.json b/src/entities/raws/noodles.json
deleted file mode 100644
index dfa2609f5e..0000000000
--- a/src/entities/raws/noodles.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
-  "Item": {
-    "name": "noodles",
-    "char": {
-      "char": "n",
-      "color": "yellow"
-    },
-    "description": "a big bowl o' noodles",
-    "long_description": "You know exactly what kind of noodles",
-    "edible_item": {
-      "eat_message": "You slurp up the noodles",
-      "hitpoints_healed": 2
-    }
-  }
-}
diff --git a/src/entities/util.rs b/src/entities/util.rs
deleted file mode 100644
index 6c11ffadf9..0000000000
--- a/src/entities/util.rs
+++ /dev/null
@@ -1,72 +0,0 @@
-#[macro_export]
-macro_rules! new_entity {
-    ($name: ident) => {
-        new_entity!($name, {})
-    };
-
-    ($name: ident { position: $position:expr $(, $fields:tt)* }) => {
-        $name {
-            id: None,
-            position: $position,
-            $($fields)*
-        }
-    };
-
-    ($name: ident { $position:expr $(, $fields:tt)* }) => {
-        $name {
-            id: None,
-            position: $position,
-            $($fields)*
-        }
-    };
-}
-
-#[macro_export]
-macro_rules! boring_entity {
-    ($name:ident) => {
-        entity! {
-            pub struct $name {}
-        }
-
-        impl $name {
-            #[allow(dead_code)]
-            pub fn new(position: $crate::types::Position) -> Self {
-                $name { id: None, position }
-            }
-        }
-    };
-
-    ($name:ident, char: $char: expr) => {
-        boring_entity!($name);
-
-        impl $crate::display::Draw for $name {
-            fn do_draw(&self, out: &mut Write) -> io::Result<()> {
-                write!(out, "{}", $char)
-            }
-        }
-    };
-}
-
-#[macro_export]
-macro_rules! entity {
-    ($name: ident) => {
-        positioned!($name);
-        positioned_mut!($name);
-        identified!($name, $crate::entities::EntityID);
-        impl $crate::entities::entity::Entity for $name {}
-    };
-
-    (pub struct $name:ident { $($struct_contents:tt)* } $($rest:tt)*) => {
-        #[derive(Debug, PartialEq, Eq, Clone)]
-        pub struct $name {
-            pub id: Option<$crate::entities::EntityID>,
-            pub position: $crate::types::Position,
-            $($struct_contents)*
-        }
-
-        entity!($name);
-        entity!($($rest)*);
-    };
-
-    () => {};
-}
diff --git a/src/game.rs b/src/game.rs
deleted file mode 100644
index c478e0d2f5..0000000000
--- a/src/game.rs
+++ /dev/null
@@ -1,617 +0,0 @@
-use crate::description::list_to_sentence;
-use crate::display::{self, Viewport};
-use crate::entities::entity::Describe;
-use crate::entities::entity::Entity;
-use crate::entities::{
-    AnEntity, Character, Creature, EntityID, Identified, Item,
-};
-use crate::messages::message;
-use crate::settings::Settings;
-use crate::types::command::Command;
-use crate::types::entity_map::EntityMap;
-use crate::types::{
-    pos, BoundingBox, Collision, Dimensions, Position, Positioned, Ticks,
-};
-use crate::util::promise::Cancelled;
-use crate::util::promise::{promise, Complete, Promise, Promises};
-use crate::util::template::TemplateParams;
-use rand::rngs::SmallRng;
-use rand::SeedableRng;
-use std::io::{self, StdinLock, StdoutLock, Write};
-use termion::input::Keys;
-use termion::input::TermRead;
-use termion::raw::RawTerminal;
-
-type Stdout<'a> = RawTerminal<StdoutLock<'a>>;
-
-type Rng = SmallRng;
-
-enum PromptResolution {
-    Uncancellable(Complete<String>),
-    Cancellable(Complete<Result<String, Cancelled>>),
-}
-
-/// The mode to use when describing entities on a tile to the user
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-enum EntityDescriptionMode {
-    /// Describe the entities that the user is walking over.
-    ///
-    /// This means:
-    /// - Skip the character themselves
-    /// - Describe nothing if there are no items other than the character
-    Walk,
-
-    /// Describe entities that the user is actively asking about.
-    ///
-    /// This means:
-    /// - Describe the character themselves if they've asked to look at the tile
-    ///   they're standing on
-    /// - Explicitly say there's nothing there if there's nothing there.
-    Look,
-}
-
-impl PromptResolution {
-    fn is_cancellable(&self) -> bool {
-        use PromptResolution::*;
-        match self {
-            Uncancellable(_) => false,
-            Cancellable(_) => true,
-        }
-    }
-
-    fn fulfill(&mut self, val: String) {
-        use PromptResolution::*;
-        match self {
-            Cancellable(complete) => complete.ok(val),
-            Uncancellable(complete) => complete.fulfill(val),
-        }
-    }
-
-    fn cancel(&mut self) {
-        use PromptResolution::*;
-        match self {
-            Cancellable(complete) => complete.cancel(),
-            Uncancellable(_complete) => {}
-        }
-    }
-}
-
-/// The kind of input the game is waiting to receive
-enum InputState {
-    /// The initial input state of the game - we're currently waiting for direct
-    /// commands.
-    Initial,
-
-    /// A free text prompt has been shown to the user, and every character
-    /// besides "escape" is interpreted as a response to that prompt
-    Prompt {
-        complete: PromptResolution,
-        buffer: String,
-    },
-}
-
-impl InputState {
-    fn uncancellable_prompt(complete: Complete<String>) -> Self {
-        InputState::Prompt {
-            complete: PromptResolution::Uncancellable(complete),
-            buffer: String::new(),
-        }
-    }
-
-    fn cancellable_prompt(
-        complete: Complete<Result<String, Cancelled>>,
-    ) -> Self {
-        InputState::Prompt {
-            complete: PromptResolution::Cancellable(complete),
-            buffer: String::new(),
-        }
-    }
-}
-
-impl Default for InputState {
-    fn default() -> Self {
-        InputState::Initial
-    }
-}
-
-/// The full state of a running Game
-pub struct Game<'a> {
-    settings: Settings,
-
-    viewport: Viewport<Stdout<'a>>,
-
-    /// An iterator on keypresses from the user
-    keys: Keys<StdinLock<'a>>,
-
-    /// The kind of input the game is waiting to receive
-    input_state: InputState,
-
-    /// The map of all the entities in the game
-    entities: EntityMap<AnEntity>,
-
-    /// The entity ID of the player character
-    character_entity_id: EntityID,
-
-    /// The messages that have been said to the user, in forward time order
-    messages: Vec<String>,
-
-    /// The index of the currently-displayed message. Used to track the index of
-    /// the currently displayed message when handling PreviousMessage commands
-    message_idx: usize,
-
-    /// A global random number generator for the game
-    rng: Rng,
-
-    /// A list of promises that are waiting on the game and a result
-    promises: Promises<'a, Self>,
-}
-
-impl<'a> Game<'a> {
-    pub fn new(
-        settings: Settings,
-        stdout: RawTerminal<StdoutLock<'a>>,
-        stdin: StdinLock<'a>,
-        w: u16,
-        h: u16,
-    ) -> Game<'a> {
-        let rng = match settings.seed {
-            Some(seed) => SmallRng::seed_from_u64(seed),
-            None => SmallRng::from_entropy(),
-        };
-        let mut entities: EntityMap<AnEntity> = EntityMap::new();
-
-        // TODO make this dynamic
-        {
-            entities.insert(Box::new(Creature::new_from_raw(
-                "gormlak",
-                pos(10, 0),
-            )));
-
-            entities
-                .insert(Box::new(Item::new_from_raw("noodles", pos(0, 10))));
-        }
-
-        Game {
-            settings,
-            rng,
-            message_idx: 0,
-            viewport: Viewport::new(
-                BoundingBox::at_origin(Dimensions { w, h }),
-                BoundingBox::at_origin(Dimensions { w: w - 2, h: h - 2 }),
-                stdout,
-            ),
-            keys: stdin.keys(),
-            input_state: Default::default(),
-            character_entity_id: entities.insert(Box::new(Character::new())),
-            messages: Vec::new(),
-            entities,
-            promises: Promises::new(),
-        }
-    }
-
-    fn downcast_entities_at<A: Entity>(&self, pos: Position) -> Vec<&A> {
-        self.entities
-            .at(pos)
-            .iter()
-            .filter_map(|e| e.downcast_ref())
-            .collect()
-    }
-
-    /// Returns a list of all creature entities at the given position
-    fn creatures_at(&self, pos: Position) -> Vec<&Creature> {
-        self.downcast_entities_at(pos)
-    }
-
-    /// Returns a list of all item entities at the given position
-    fn items_at(&self, pos: Position) -> Vec<&Item> {
-        self.downcast_entities_at(pos)
-    }
-
-    /// Returns a collision, if any, at the given Position in the game
-    fn collision_at(&self, pos: Position) -> Option<Collision> {
-        if !pos.within(self.viewport.inner) {
-            Some(Collision::Stop)
-        } else if self.creatures_at(pos).is_empty() {
-            None
-        } else {
-            Some(Collision::Combat)
-        }
-    }
-
-    fn character(&self) -> &Character {
-        (*self.entities.get(self.character_entity_id).unwrap())
-            .downcast_ref()
-            .unwrap()
-    }
-
-    fn mut_character(&mut self) -> &mut Character {
-        (*self.entities.get_mut(self.character_entity_id).unwrap())
-            .downcast_mut()
-            .unwrap()
-    }
-
-    /// Draw all the game entities to the screen
-    fn draw_entities(&mut self) -> io::Result<()> {
-        for entity in self.entities.entities() {
-            self.viewport.draw(
-                entity,
-                &self.entities.neighbor_entities(entity.position()),
-            )?;
-        }
-        Ok(())
-    }
-
-    /// Draw all the game entities to the screen
-    fn draw_entities_at(&mut self, pos: Position) -> io::Result<()> {
-        for entity in self.entities.at(pos) {
-            self.viewport.draw(
-                entity,
-                &self.entities.neighbor_entities(entity.position()),
-            )?;
-        }
-        Ok(())
-    }
-
-    /// Draw the game entity with the given ID, if any, to the screen
-    fn draw_entity(&mut self, entity_id: EntityID) -> io::Result<bool> {
-        if let Some(entity) = self.entities.get(entity_id) {
-            self.viewport.draw(
-                entity,
-                &self.entities.neighbor_entities(entity.position()),
-            )?;
-            Ok(true)
-        } else {
-            Ok(false)
-        }
-    }
-
-    /// Describe all the entities at a given position to the user.
-    ///
-    /// If `force` is not set to `true`, will not do anything if there are no
-    /// entities
-    fn describe_entities_at(
-        &mut self,
-        pos: Position,
-        mode: EntityDescriptionMode,
-    ) -> io::Result<()> {
-        use EntityDescriptionMode::*;
-        let mut entities = self.entities.at(pos);
-        if mode == Walk {
-            entities.retain(|e| e.id() != self.character_entity_id);
-        }
-
-        if entities.is_empty() {
-            match mode {
-                Walk => return Ok(()),
-                Look => {
-                    return self.say(
-                        "global.describe_no_entities",
-                        &template_params!(),
-                    )
-                }
-            }
-        }
-
-        let descriptions = list_to_sentence(
-            &entities
-                .iter()
-                .map(|e| e.description())
-                .collect::<Vec<String>>(),
-        );
-
-        self.say(
-            "global.describe_entities",
-            &template_params!({ "descriptions" => &descriptions, }),
-        )
-    }
-
-    /// Remove the given entity from the game, drawing over it if it's visible
-    fn remove_entity(&mut self, entity_id: EntityID) -> io::Result<()> {
-        if let Some(entity) = self.entities.remove(entity_id) {
-            self.viewport.clear(entity.position())?;
-        }
-        Ok(())
-    }
-
-    /// Step the game forward the given number of ticks
-    fn tick(&mut self, _ticks: Ticks) {}
-
-    /// Get a message from the global map based on the rng in this game
-    fn message<'params>(
-        &mut self,
-        name: &'static str,
-        params: &TemplateParams<'params>,
-    ) -> String {
-        message(name, &mut self.rng, params)
-    }
-
-    /// Say a message to the user
-    fn say<'params>(
-        &mut self,
-        message_name: &'static str,
-        params: &TemplateParams<'params>,
-    ) -> io::Result<()> {
-        let message = self.message(message_name, params);
-        self.messages.push(message.to_string());
-        self.message_idx = self.messages.len() - 1;
-        self.viewport.write_message(&message)?;
-        Ok(())
-    }
-
-    /// Prompt the user for input, returning a Future for the result of the
-    /// prompt
-    fn prompt(
-        &mut self,
-        name: &'static str,
-        params: &TemplateParams<'_>,
-    ) -> io::Result<Promise<Self, String>> {
-        let (complete, promise) = promise();
-        self.input_state = InputState::uncancellable_prompt(complete);
-        let message = self.message(name, params);
-        self.viewport.write_prompt(&message)?;
-        self.promises.push(Box::new(promise.clone()));
-        Ok(promise)
-    }
-
-    fn prompt_cancellable(
-        &mut self,
-        name: &'static str,
-        params: &TemplateParams<'_>,
-    ) -> io::Result<Promise<Self, Result<String, Cancelled>>> {
-        let (complete, promise) = promise();
-        self.input_state = InputState::cancellable_prompt(complete);
-        let message = self.message(name, params);
-        self.viewport.write_prompt(&message)?;
-        self.promises.push(Box::new(promise.clone()));
-        Ok(promise)
-    }
-
-    fn previous_message(&mut self) -> io::Result<()> {
-        if self.message_idx == 0 {
-            return Ok(());
-        }
-        self.message_idx -= 1;
-        let message = &self.messages[self.message_idx];
-        self.viewport.write_message(message)?;
-        Ok(())
-    }
-
-    fn clear_message(&mut self) -> io::Result<()> {
-        debug!("{:?} {:?}", self.message_idx, self.messages);
-        if self.message_idx == self.messages.len() {
-            return Ok(());
-        }
-        self.viewport.clear_message()?;
-        self.message_idx += 1;
-        Ok(())
-    }
-
-    fn creature(&self, creature_id: EntityID) -> Option<&Creature> {
-        self.entities
-            .get(creature_id)
-            .and_then(|e| e.downcast_ref::<Creature>())
-    }
-
-    fn expect_creature(&self, creature_id: EntityID) -> &Creature {
-        self.creature(creature_id).unwrap_or_else(|| {
-            panic!("Creature ID went away: {:?}", creature_id)
-        })
-    }
-
-    fn mut_creature(&mut self, creature_id: EntityID) -> Option<&mut Creature> {
-        self.entities
-            .get_mut(creature_id)
-            .and_then(|e| e.downcast_mut::<Creature>())
-    }
-
-    fn expect_mut_creature(&mut self, creature_id: EntityID) -> &mut Creature {
-        self.mut_creature(creature_id).unwrap_or_else(|| {
-            panic!("Creature ID went away: {:?}", creature_id)
-        })
-    }
-
-    fn attack(&mut self, creature_id: EntityID) -> io::Result<()> {
-        info!("Attacking creature {:?}", creature_id);
-        let damage = self.character().damage();
-        let creature_name = self.expect_creature(creature_id).typ.name;
-        let tps = template_params!({
-            "creature" => {
-                "name" => creature_name,
-            },
-        });
-        self.say("combat.attack", &tps)?;
-
-        let creature = self.expect_mut_creature(creature_id);
-        creature.damage(damage);
-        if creature.dead() {
-            self.say("combat.killed", &tps)?;
-            info!("Killed creature {:?}", creature_id);
-            self.remove_entity(creature_id)?;
-        }
-        Ok(())
-    }
-
-    fn attack_at(&mut self, pos: Position) -> io::Result<()> {
-        let creatures = self.creatures_at(pos);
-        match creatures.len() {
-            0 => Ok(()),
-            1 => {
-                let creature = creatures.get(0).unwrap();
-                let creature_id = creature.id();
-                self.attack(creature_id)
-            }
-            _ => {
-                // TODO prompt with a menu of creatures to combat
-                unimplemented!()
-            }
-        }
-    }
-
-    fn pick_up(&mut self) -> io::Result<()> {
-        let pos = self.character().position;
-        let items = self.items_at(pos);
-        match items.len() {
-            0 => Ok(()),
-            1 => {
-                let item_id = items.get(0).unwrap().id();
-                let item: Box<Item> =
-                    self.entities.remove(item_id).unwrap().downcast().unwrap();
-                let desc = item.description();
-                self.mut_character().inventory.push(item);
-                self.say(
-                    "global.pick_up",
-                    &template_params!({
-                        "item" => { "name" => &desc, },
-                    }),
-                )
-            }
-            _ => {
-                // TODO prompt with a menu of items to pick up
-                unimplemented!()
-            }
-        }
-    }
-
-    fn flush_promises(&mut self) {
-        unsafe {
-            let game = self as *mut Self;
-            (*game).promises.give_all(&mut *game);
-        }
-    }
-
-    /// Run the game
-    pub fn run(mut self) -> io::Result<()> {
-        info!("Running game");
-        self.viewport.init()?;
-        self.draw_entities()?;
-        self.flush().unwrap();
-
-        self.prompt("character.name_prompt", &template_params!())?
-            .on_fulfill(|game, char_name| {
-                game.say(
-                    "global.welcome",
-                    &template_params!({
-                        "character" => {
-                            "name" => char_name,
-                        },
-                    }),
-                )
-                .unwrap();
-                game.flush().unwrap();
-                game.mut_character().set_name(char_name.to_string());
-            });
-
-        loop {
-            let mut old_position = None;
-            let next_key = self.keys.next().unwrap().unwrap();
-            match &mut self.input_state {
-                InputState::Initial => {
-                    use Command::*;
-                    match Command::from_key(next_key) {
-                        Some(Quit) => {
-                            info!("Quitting game due to user request");
-                            break;
-                        }
-
-                        Some(Move(direction)) => {
-                            use Collision::*;
-                            let new_pos = self.character().position + direction;
-                            match self.collision_at(new_pos) {
-                                None => {
-                                    old_position =
-                                        Some(self.character().position);
-                                    self.entities.update_position(
-                                        self.character_entity_id,
-                                        new_pos,
-                                    );
-                                }
-                                Some(Combat) => {
-                                    self.attack_at(new_pos)?;
-                                }
-                                Some(Stop) => (),
-                            }
-                        }
-
-                        Some(PreviousMessage) => self.previous_message()?,
-
-                        Some(PickUp) => self.pick_up()?,
-
-                        None => (),
-                    }
-
-                    if let Some(old_pos) = old_position {
-                        let character = self.character();
-                        let char_pos = character.position;
-                        self.viewport.game_cursor_position = char_pos;
-                        self.viewport.clear(old_pos)?;
-                        self.draw_entities_at(old_pos)?;
-                        self.draw_entity(self.character_entity_id)?;
-                        self.clear_message()?;
-                        self.describe_entities_at(
-                            char_pos,
-                            EntityDescriptionMode::Walk,
-                        )?;
-                        self.tick(
-                            self.character().speed().tiles_to_ticks(
-                                (old_pos - char_pos).as_tiles(),
-                            ),
-                        );
-                    }
-                }
-
-                InputState::Prompt { complete, buffer } => {
-                    use termion::event::Key::*;
-                    match next_key {
-                        Char('\n') => {
-                            info!("Prompt complete: \"{}\"", buffer);
-                            self.viewport.clear_prompt()?;
-                            complete.fulfill(buffer.clone());
-                            self.input_state = InputState::Initial;
-                        }
-                        Char(chr) => {
-                            buffer.push(chr);
-                            self.viewport.push_prompt_chr(chr)?;
-                        }
-                        Esc => complete.cancel(),
-                        Backspace => {
-                            buffer.pop();
-                            self.viewport.pop_prompt_chr()?;
-                        }
-                        _ => {}
-                    }
-                }
-            }
-
-            self.flush()?;
-            self.flush_promises();
-            debug!("{:?}", self.character());
-        }
-        Ok(())
-    }
-}
-
-impl<'a> Drop for Game<'a> {
-    fn drop(&mut self) {
-        display::clear(self).unwrap_or(());
-    }
-}
-
-impl<'a> Write for Game<'a> {
-    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
-        self.viewport.write(buf)
-    }
-
-    fn flush(&mut self) -> io::Result<()> {
-        self.viewport.flush()
-    }
-
-    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
-        self.viewport.write_all(buf)
-    }
-}
-
-impl<'a> Positioned for Game<'a> {
-    fn position(&self) -> Position {
-        Position { x: 0, y: 0 }
-    }
-}
diff --git a/src/level_gen/cave_automata.rs b/src/level_gen/cave_automata.rs
deleted file mode 100644
index e5e2807ab2..0000000000
--- a/src/level_gen/cave_automata.rs
+++ /dev/null
@@ -1,120 +0,0 @@
-use crate::level_gen::util::fill_outer_edges;
-use crate::level_gen::util::rand_initialize;
-use crate::types::Dimensions;
-use rand::Rng;
-
-pub struct Params {
-    chance_to_start_alive: f64,
-    birth_limit: i32,
-    death_limit: i32,
-    steps: usize,
-}
-
-macro_rules! parse_optional {
-    ($out: ident . $attr: ident, $matches: expr, $arg: expr) => {
-        if let Some(val_s) = $matches.value_of($arg) {
-            $out.$attr = val_s.parse().unwrap();
-        }
-    };
-}
-
-macro_rules! parse_optional_matches {
-    ($matches: expr) => {};
-    ($matches: expr , { $ret: ident . $attr: ident = $arg: expr }) => {
-        parse_optional!($ret.$attr, $matches, $arg);
-    };
-    ($matches: expr, { $($ret: ident . $attr: ident = $arg: expr ,)* }) => {
-        $(parse_optional!($ret.$attr, $matches, $arg);)*
-    };
-}
-
-impl Params {
-    pub fn from_matches<'a>(matches: &clap::ArgMatches<'a>) -> Self {
-        let mut ret: Self = Default::default();
-        parse_optional_matches!(matches, {
-            ret.chance_to_start_alive = "start-alive-chance",
-            ret.birth_limit = "birth-limit",
-            ret.death_limit = "death-limit",
-            ret.steps = "steps",
-        });
-        ret
-    }
-}
-
-impl Default for Params {
-    fn default() -> Self {
-        Params {
-            chance_to_start_alive: 0.45,
-            birth_limit: 4,
-            death_limit: 3,
-            steps: 2,
-        }
-    }
-}
-
-pub fn generate<R: Rng + ?Sized>(
-    dimensions: Dimensions,
-    params: &Params,
-    rand: &mut R,
-) -> Vec<Vec<bool>> {
-    let mut cells =
-        rand_initialize(dimensions, rand, params.chance_to_start_alive);
-    for _ in 0..params.steps {
-        step_automata(&mut cells, dimensions, params);
-    }
-
-    fill_outer_edges(&mut cells);
-
-    cells
-}
-
-fn step_automata(
-    cells: &mut Vec<Vec<bool>>,
-    dimensions: Dimensions,
-    params: &Params,
-) {
-    let orig_cells = (*cells).clone();
-    for x in 0..(dimensions.h as usize) {
-        for y in 0..(dimensions.w as usize) {
-            let nbs = num_alive_neighbors(&orig_cells, x as i32, y as i32);
-            if orig_cells[x][y] {
-                if nbs < params.death_limit {
-                    cells[x][y] = false;
-                } else {
-                    cells[x][y] = true;
-                }
-            } else if nbs > params.birth_limit {
-                cells[x][y] = true;
-            } else {
-                cells[x][y] = false;
-            }
-        }
-    }
-}
-
-const COUNT_EDGES_AS_NEIGHBORS: bool = true;
-
-fn num_alive_neighbors(cells: &[Vec<bool>], x: i32, y: i32) -> i32 {
-    let mut count = 0;
-    for i in -1..2 {
-        for j in -1..2 {
-            if i == 0 && j == 0 {
-                continue;
-            }
-
-            let neighbor_x = x + i;
-            let neighbor_y = y + j;
-
-            if (COUNT_EDGES_AS_NEIGHBORS
-                && (neighbor_x < 0
-                    || neighbor_y < 0
-                    || neighbor_x >= (cells.len() as i32)
-                    || neighbor_y >= (cells[0].len()) as i32))
-                || cells[neighbor_x as usize][neighbor_y as usize]
-            {
-                count += 1;
-            }
-        }
-    }
-    count
-}
diff --git a/src/level_gen/mod.rs b/src/level_gen/mod.rs
deleted file mode 100644
index d796a103b1..0000000000
--- a/src/level_gen/mod.rs
+++ /dev/null
@@ -1,101 +0,0 @@
-use crate::display::draw_box::BoxStyle;
-use crate::display::utils::clone_times;
-use crate::display::DrawWithNeighbors;
-use crate::entities::entity::Entity;
-use crate::entities::environment::Wall;
-use crate::types::entity_map::EntityMap;
-use crate::types::pos;
-use itertools::Itertools;
-use std::io;
-
-pub mod cave_automata;
-pub mod util;
-
-pub fn level_to_entities(level: Vec<Vec<bool>>) -> EntityMap<Box<dyn Entity>> {
-    let mut res: EntityMap<Box<dyn Entity>> = EntityMap::new();
-
-    let xmax = level.len() as i16;
-    let ymax = if xmax == 0 {
-        0i16
-    } else {
-        level[0].len() as i16
-    };
-
-    let get = |mut x: i16, mut y: i16| {
-        if x < 0 {
-            x = 0;
-        }
-        if y < 0 {
-            y = 0;
-        }
-        if x >= xmax - 1 {
-            x = xmax - 1;
-        }
-        if y >= ymax - 1 {
-            y = ymax - 1;
-        }
-        level[x as usize][y as usize]
-    };
-
-    for x in 0..xmax {
-        for y in 0..ymax {
-            if get(x, y) {
-                // don't output walls that are surrounded on all 8 sides by
-                // walls
-                if (x == 0 || get(x - 1, y))
-                    && (y == 0 || get(x, y - 1))
-                    && (x == xmax - 1 || get(x + 1, y))
-                    && (y == ymax - 1 || get(x, y + 1))
-                    && ((x == 0 && y == 0) || get(x - 1, y - 1))
-                    && ((x == 0 && y == ymax - 1) || get(x - 1, y + 1))
-                    && ((x == xmax - 1 && y == 0) || get(x + 1, y - 1))
-                    && ((x == xmax - 1 && y == ymax - 1) || get(x + 1, y + 1))
-                {
-                    continue;
-                }
-                res.insert(Box::new(Wall::new(
-                    pos(y as i16, x as i16),
-                    BoxStyle::Thin,
-                )));
-            }
-        }
-    }
-
-    res
-}
-
-pub fn draw_level<W: io::Write>(
-    level: Vec<Vec<bool>>,
-    out: &mut W,
-) -> io::Result<()> {
-    if level.is_empty() {
-        return Ok(());
-    }
-
-    let mut lines = clone_times::<Vec<char>, Vec<Vec<char>>>(
-        clone_times(' ', level[0].len() as u16),
-        level.len() as u16,
-    );
-
-    let em = level_to_entities(level);
-
-    for entity in em.entities() {
-        let mut buf = Vec::new();
-        entity.do_draw_with_neighbors(
-            &mut buf,
-            &em.neighbor_entities(entity.position()),
-        )?;
-        let buf_s = std::str::from_utf8(&buf).unwrap();
-        if let Some(chr) = buf_s.chars().next() {
-            lines[entity.position().y as usize][entity.position().x as usize] =
-                chr;
-        }
-    }
-
-    let res = lines
-        .iter()
-        .map(|line| line.iter().collect::<String>())
-        .join("\n");
-
-    write!(out, "{}", res)
-}
diff --git a/src/level_gen/util.rs b/src/level_gen/util.rs
deleted file mode 100644
index 4f56fe6c95..0000000000
--- a/src/level_gen/util.rs
+++ /dev/null
@@ -1,52 +0,0 @@
-use crate::types::Dimensions;
-use rand::{distributions, Rng};
-
-pub fn falses(dims: Dimensions) -> Vec<Vec<bool>> {
-    let mut ret = Vec::with_capacity(dims.h as usize);
-    for _ in 0..dims.h {
-        let mut row = Vec::with_capacity(dims.w as usize);
-        for _ in 0..dims.w {
-            row.push(false);
-        }
-        ret.push(row);
-    }
-    ret
-}
-
-/// Randomly initialize a 2-dimensional boolean vector of the given
-/// `Dimensions`, using the given random number generator and alive chance
-pub fn rand_initialize<R: Rng + ?Sized>(
-    dims: Dimensions,
-    rng: &mut R,
-    alive_chance: f64,
-) -> Vec<Vec<bool>> {
-    let distrib = distributions::Bernoulli::new(alive_chance).unwrap();
-    let mut ret = Vec::with_capacity(dims.h as usize);
-    for _ in 0..dims.h {
-        let mut row = Vec::with_capacity(dims.w as usize);
-        for _ in 0..dims.w {
-            row.push(rng.sample(distrib));
-        }
-        ret.push(row);
-    }
-    ret
-}
-
-/// Fill the outer edges of a generated level with walls
-pub fn fill_outer_edges(level: &mut Vec<Vec<bool>>) {
-    let xmax = level.len();
-    if xmax == 0 {
-        return;
-    }
-    let ymax = level[0].len();
-
-    for row in level.iter_mut() {
-        row[0] = true;
-        row[ymax - 1] = true;
-    }
-
-    for y in 0..level[0].len() {
-        level[0][y] = true;
-        level[xmax - 1][y] = true;
-    }
-}
diff --git a/src/main.rs b/src/main.rs
deleted file mode 100644
index 8004a5739e..0000000000
--- a/src/main.rs
+++ /dev/null
@@ -1,130 +0,0 @@
-#[macro_use]
-extern crate log;
-#[macro_use]
-extern crate serde_derive;
-#[macro_use]
-extern crate clap;
-#[macro_use]
-extern crate prettytable;
-#[macro_use]
-extern crate lazy_static;
-#[cfg(test)]
-#[macro_use]
-extern crate maplit;
-#[macro_use]
-extern crate downcast_rs;
-#[macro_use]
-extern crate include_dir;
-#[macro_use]
-extern crate nom;
-#[cfg(test)]
-#[macro_use]
-extern crate matches;
-
-#[macro_use]
-mod util;
-#[macro_use]
-mod types;
-#[macro_use]
-mod entities;
-mod description;
-mod display;
-mod game;
-mod level_gen;
-mod messages;
-mod settings;
-
-use crate::types::Dimensions;
-use clap::App;
-use game::Game;
-use prettytable::format::consts::FORMAT_BOX_CHARS;
-use rand::rngs::SmallRng;
-use rand::SeedableRng;
-use settings::Settings;
-
-use backtrace::Backtrace;
-use std::io::{self, StdinLock, StdoutLock};
-use std::panic;
-
-use termion;
-use termion::raw::IntoRawMode;
-use termion::raw::RawTerminal;
-
-fn init(
-    settings: Settings,
-    stdout: RawTerminal<StdoutLock<'_>>,
-    stdin: StdinLock<'_>,
-    w: u16,
-    h: u16,
-) -> io::Result<()> {
-    panic::set_hook(if settings.logging.print_backtrace {
-        Box::new(|info| (error!("{}\n{:#?}", info, Backtrace::new())))
-    } else {
-        Box::new(|info| (error!("{}\n{:#?}", info, Backtrace::new())))
-    });
-
-    let game = Game::new(settings, stdout, stdin, w, h);
-    game.run()
-}
-
-fn generate_level<'a, W: io::Write>(
-    stdout: &mut W,
-    params: &clap::ArgMatches<'a>,
-) -> io::Result<()> {
-    let mut rand = SmallRng::from_entropy();
-
-    let mut dimensions: Dimensions = Default::default();
-    if let Some(h_s) = params.value_of("height") {
-        dimensions.h = h_s.parse().unwrap();
-    }
-    if let Some(w_s) = params.value_of("width") {
-        dimensions.w = w_s.parse().unwrap();
-    }
-
-    let level = match params.value_of("generator") {
-        None => panic!("Must supply a generator with --generator"),
-        Some("cave_automata") => level_gen::cave_automata::generate(
-            dimensions,
-            &level_gen::cave_automata::Params::from_matches(params),
-            &mut rand,
-        ),
-        Some(gen) => panic!("Unrecognized generator: {}", gen),
-    };
-    level_gen::draw_level(level, stdout)
-}
-
-fn main() -> io::Result<()> {
-    let yaml = load_yaml!("cli.yml");
-    let matches = App::from_yaml(yaml).get_matches();
-    let settings = Settings::load().unwrap();
-    settings.logging.init_log();
-    let stdout = io::stdout();
-    let mut stdout = stdout.lock();
-
-    let stdin = io::stdin();
-    let stdin = stdin.lock();
-
-    let termsize = termion::terminal_size().ok();
-    let (termwidth, termheight) = termsize.unwrap_or((70, 40));
-
-    match matches.subcommand() {
-        ("info", _) => {
-            let mut table = table!(
-                [br->"termwidth", termwidth],
-                [br->"termheight", termheight],
-                [br->"logfile", settings.logging.file],
-                [br->"loglevel", settings.logging.level]
-            );
-            table.set_format(*FORMAT_BOX_CHARS);
-            table.printstd();
-            Ok(())
-        }
-        ("generate-level", params) => {
-            generate_level(&mut stdout, params.unwrap())
-        }
-        _ => {
-            let stdout = stdout.into_raw_mode().unwrap();
-            init(settings, stdout, stdin, termwidth, termheight)
-        }
-    }
-}
diff --git a/src/messages.rs b/src/messages.rs
deleted file mode 100644
index b081389efc..0000000000
--- a/src/messages.rs
+++ /dev/null
@@ -1,166 +0,0 @@
-use crate::util::template::Template;
-use crate::util::template::TemplateParams;
-use rand::seq::SliceRandom;
-use rand::Rng;
-use std::collections::HashMap;
-
-#[derive(Deserialize, Debug, PartialEq, Eq)]
-#[serde(untagged)]
-pub enum Message<'a> {
-    #[serde(borrow)]
-    Single(Template<'a>),
-    Choice(Vec<Template<'a>>),
-}
-
-impl<'a> Message<'a> {
-    fn resolve<R: Rng + ?Sized>(&self, rng: &mut R) -> Option<&Template<'a>> {
-        use Message::*;
-        match self {
-            Single(msg) => Some(msg),
-            Choice(msgs) => msgs.choose(rng),
-        }
-    }
-}
-
-#[derive(Deserialize, Debug, PartialEq, Eq)]
-#[serde(untagged)]
-enum NestedMap<'a> {
-    #[serde(borrow)]
-    Direct(Message<'a>),
-    #[serde(borrow)]
-    Nested(HashMap<&'a str, NestedMap<'a>>),
-}
-
-impl<'a> NestedMap<'a> {
-    fn lookup(&'a self, path: &str) -> Option<&'a Message<'a>> {
-        use NestedMap::*;
-        let leaf =
-            path.split('.')
-                .fold(Some(self), |current, key| match current {
-                    Some(Nested(m)) => m.get(key),
-                    _ => None,
-                });
-        match leaf {
-            Some(Direct(msg)) => Some(msg),
-            _ => None,
-        }
-    }
-}
-
-#[cfg(test)]
-mod nested_map_tests {
-    use super::*;
-
-    #[test]
-    fn test_deserialize_nested_map() {
-        let src = r#"
-[global]
-hello = "Hello World!"
-
-[foo.bar]
-single = "Single"
-choice = ["Say this", "Or this"]
-"#;
-        let result = toml::from_str(src);
-        assert_eq!(
-            result,
-            Ok(NestedMap::Nested(hashmap! {
-                "global" => NestedMap::Nested(hashmap!{
-                    "hello" => NestedMap::Direct(Message::Single(Template::parse("Hello World!").unwrap())),
-                }),
-                "foo" => NestedMap::Nested(hashmap!{
-                    "bar" => NestedMap::Nested(hashmap!{
-                        "single" => NestedMap::Direct(Message::Single(
-                            Template::parse("Single").unwrap()
-                        )),
-                        "choice" => NestedMap::Direct(Message::Choice(
-                            vec![
-                                Template::parse("Say this").unwrap(),
-                                Template::parse("Or this").unwrap()
-                            ]
-                        ))
-                    })
-                })
-            }))
-        )
-    }
-
-    #[test]
-    fn test_lookup() {
-        let map: NestedMap<'static> = toml::from_str(
-            r#"
-[global]
-hello = "Hello World!"
-
-[foo.bar]
-single = "Single"
-choice = ["Say this", "Or this"]
-"#,
-        )
-        .unwrap();
-
-        assert_eq!(
-            map.lookup("global.hello"),
-            Some(&Message::Single(Template::parse("Hello World!").unwrap()))
-        );
-        assert_eq!(
-            map.lookup("foo.bar.single"),
-            Some(&Message::Single(Template::parse("Single").unwrap()))
-        );
-        assert_eq!(
-            map.lookup("foo.bar.choice"),
-            Some(&Message::Choice(vec![
-                Template::parse("Say this").unwrap(),
-                Template::parse("Or this").unwrap()
-            ]))
-        );
-    }
-}
-
-// static MESSAGES_RAW: &'static str = include_str!("messages.toml");
-
-static_cfg! {
-    static ref MESSAGES: NestedMap<'static> = toml_file("messages.toml");
-}
-
-pub fn get<R: Rng + ?Sized>(
-    name: &'static str,
-    rng: &mut R,
-) -> Option<&'static Template<'static>> {
-    MESSAGES.lookup(name).and_then(|msg| msg.resolve(rng))
-}
-
-/// Look up and format a game message based on the given (dot-separated) name,
-/// with the given random generator used to select from choice-based messages
-pub fn message<'a, R: Rng + ?Sized>(
-    name: &'static str,
-    rng: &mut R,
-    params: &TemplateParams<'a>,
-) -> String {
-    match get(name, rng) {
-        Some(msg) => msg.format(params).unwrap_or_else(|e| {
-            error!("Error formatting template: {}", e);
-            "Template Error".to_string()
-        }),
-        None => {
-            error!("Message not found: {}", name);
-            "Template Not Found".to_string()
-        }
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use rand::rngs::SmallRng;
-    use rand::SeedableRng;
-
-    #[test]
-    fn test_static_messages() {
-        message(
-            "global.welcome",
-            &mut SmallRng::from_entropy(),
-            &template_params!(),
-        );
-    }
-}
diff --git a/src/messages.toml b/src/messages.toml
deleted file mode 100644
index a9a6b2e009..0000000000
--- a/src/messages.toml
+++ /dev/null
@@ -1,27 +0,0 @@
-[global]
-welcome = "Welcome to Xanthous, {{character.name}}! It's dangerous out there, why not stay inside?"
-describe_entities = "You see here {{descriptions}}"
-describe_no_entities = "You see nothing here."
-pick_up = "You pick up the {{item.name}}."
-
-[combat]
-attack = "You attack the {{creature.name}}."
-killed = [
-    "You've killed the {{creature.name}}.",
-    "The {{creature.name}} dies.",
-    "The {{creature.name}} kicks it.",
-    "The {{creature.name}} beefs it."
-    ]
-
-[character]
-name_prompt = [
-    "Hey there friend. What's your name?",
-    "Hey there friend. What should we call you?",
-    "Howdy. What's your name?",
-    "Name please!",
-    "What's your name?",
-    "Hey, what's your name?",
-]
-
-[defaults.item]
-eat = "You eat the {{item.name}}. {{action.result}}"
diff --git a/src/settings.rs b/src/settings.rs
deleted file mode 100644
index 1f205814d1..0000000000
--- a/src/settings.rs
+++ /dev/null
@@ -1,70 +0,0 @@
-use config::{Config, ConfigError};
-use log::LevelFilter;
-use log4rs::append::file::FileAppender;
-use log4rs::config::{Appender, Root};
-use log4rs::encode::pattern::PatternEncoder;
-
-#[derive(Debug, Deserialize, Clone)]
-pub struct Logging {
-    #[serde(default = "Logging::default_level")]
-    pub level: LevelFilter,
-
-    #[serde(default = "Logging::default_file")]
-    pub file: String,
-
-    #[serde(default = "Logging::default_print_backtrace")]
-    pub print_backtrace: bool,
-}
-
-impl Default for Logging {
-    fn default() -> Self {
-        Logging {
-            level: LevelFilter::Off,
-            file: "debug.log".to_string(),
-            print_backtrace: true,
-        }
-    }
-}
-
-impl Logging {
-    pub fn init_log(&self) {
-        let logfile = FileAppender::builder()
-            .encoder(Box::new(PatternEncoder::new("{d} {l} - {m}\n")))
-            .build(self.file.clone())
-            .unwrap();
-
-        let config = log4rs::config::Config::builder()
-            .appender(Appender::builder().build("logfile", Box::new(logfile)))
-            .build(Root::builder().appender("logfile").build(self.level))
-            .unwrap();
-
-        log4rs::init_config(config).unwrap();
-    }
-
-    fn default_level() -> LevelFilter {
-        Logging::default().level
-    }
-
-    fn default_file() -> String {
-        Logging::default().file
-    }
-
-    fn default_print_backtrace() -> bool {
-        Logging::default().print_backtrace
-    }
-}
-
-#[derive(Debug, Deserialize, Clone)]
-pub struct Settings {
-    pub seed: Option<u64>,
-    pub logging: Logging,
-}
-
-impl Settings {
-    pub fn load() -> Result<Self, ConfigError> {
-        let mut s = Config::new();
-        s.merge(config::File::with_name("Config").required(false))?;
-        s.merge(config::Environment::with_prefix("XAN"))?;
-        s.try_into()
-    }
-}
diff --git a/src/types/collision.rs b/src/types/collision.rs
deleted file mode 100644
index 59c60e69ee..0000000000
--- a/src/types/collision.rs
+++ /dev/null
@@ -1,9 +0,0 @@
-/// Describes a kind of game collision
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum Collision {
-    /// Stop moving - you can't move there!
-    Stop,
-
-    /// Moving into an entity at the given position indicates combat
-    Combat,
-}
diff --git a/src/types/command.rs b/src/types/command.rs
deleted file mode 100644
index 17ca4d280f..0000000000
--- a/src/types/command.rs
+++ /dev/null
@@ -1,41 +0,0 @@
-use super::Direction;
-use super::Direction::*;
-use termion::event::Key;
-use termion::event::Key::{Char, Ctrl};
-
-pub enum Command {
-    /// Quit the game
-    Quit,
-
-    /// Move the character in a direction
-    Move(Direction),
-
-    /// Pick up any item(s) at the current position
-    PickUp,
-
-    /// Display the previous message
-    PreviousMessage,
-}
-
-impl Command {
-    pub fn from_key(k: Key) -> Option<Command> {
-        use Command::*;
-        match k {
-            Char('q') => Some(Quit),
-
-            Char('h') | Char('a') | Key::Left => Some(Move(Left)),
-            Char('k') | Char('w') | Key::Up => Some(Move(Up)),
-            Char('j') | Char('s') | Key::Down => Some(Move(Down)),
-            Char('l') | Char('d') | Key::Right => Some(Move(Right)),
-            Char('y') => Some(Move(UpLeft)),
-            Char('u') => Some(Move(UpRight)),
-            Char('b') => Some(Move(DownLeft)),
-            Char('n') => Some(Move(DownRight)),
-
-            Ctrl('p') => Some(PreviousMessage),
-            Char(',') => Some(PickUp),
-
-            _ => None,
-        }
-    }
-}
diff --git a/src/types/direction.rs b/src/types/direction.rs
deleted file mode 100644
index 9b5c0991da..0000000000
--- a/src/types/direction.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-use proptest_derive::Arbitrary;
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Arbitrary)]
-pub enum Direction {
-    Left,
-    Up,
-    Down,
-    Right,
-    UpLeft,
-    UpRight,
-    DownRight,
-    DownLeft,
-}
diff --git a/src/types/entity_map.rs b/src/types/entity_map.rs
deleted file mode 100644
index 202d8b593e..0000000000
--- a/src/types/entity_map.rs
+++ /dev/null
@@ -1,430 +0,0 @@
-use crate::entities::entity::Identified;
-use crate::entities::EntityID;
-use crate::types::Neighbors;
-use crate::types::Position;
-use crate::types::Positioned;
-use crate::types::PositionedMut;
-use alga::general::{
-    AbstractMagma, AbstractMonoid, AbstractSemigroup, Additive, Identity,
-};
-use std::collections::{hash_map, BTreeMap, HashMap};
-use std::iter::FromIterator;
-
-#[derive(Debug, Clone, Default)]
-pub struct EntityMap<A> {
-    by_position: BTreeMap<Position, Vec<EntityID>>,
-    by_id: HashMap<EntityID, A>,
-    last_id: EntityID,
-}
-
-impl<A: PartialEq> PartialEq for EntityMap<A> {
-    fn eq(&self, other: &Self) -> bool {
-        self.by_position == other.by_position && self.by_id == other.by_id
-    }
-}
-impl<A: Eq> Eq for EntityMap<A> {}
-
-const BY_POS_INVARIANT: &str =
-    "Invariant: All references in EntityMap.by_position should point to existent references in by_id";
-
-impl<A> EntityMap<A> {
-    pub fn new() -> EntityMap<A> {
-        EntityMap {
-            by_position: BTreeMap::new(),
-            by_id: HashMap::new(),
-            last_id: 0,
-        }
-    }
-
-    pub fn len(&self) -> usize {
-        self.by_id.len()
-    }
-
-    /// Returns a list of all entities at the given position
-    pub fn at<'a>(&'a self, pos: Position) -> Vec<&'a A> {
-        self.by_position
-            .get(&pos)
-            .iter()
-            .flat_map(|eids| {
-                eids.iter()
-                    .map(|eid| self.by_id.get(eid).expect(BY_POS_INVARIANT))
-            })
-            .collect()
-    }
-
-    /// Remove all entities at the given position
-    pub fn remove_all_at(&mut self, pos: Position) {
-        if let Some(eids) = self.by_position.remove(&pos) {
-            for eid in eids {
-                self.by_id.remove(&eid).expect(BY_POS_INVARIANT);
-            }
-        }
-    }
-
-    pub fn get(&self, id: EntityID) -> Option<&A> {
-        self.by_id.get(&id)
-    }
-
-    pub fn get_mut(&mut self, id: EntityID) -> Option<&mut A> {
-        self.by_id.get_mut(&id)
-    }
-
-    pub fn entities(&self) -> impl Iterator<Item = &A> {
-        self.by_id.values()
-    }
-
-    pub fn entities_mut(&mut self) -> impl Iterator<Item = &mut A> {
-        self.by_id.values_mut()
-    }
-
-    pub fn ids(&self) -> hash_map::Keys<'_, EntityID, A> {
-        self.by_id.keys()
-    }
-
-    pub fn drain(&mut self) -> Drain<'_, A> {
-        let ids = self.ids().copied().collect::<Vec<_>>();
-        Drain {
-            map: self,
-            ids_iter: Box::new(ids.into_iter()),
-        }
-    }
-
-    fn next_id(&mut self) -> EntityID {
-        self.last_id += 1;
-        self.last_id
-    }
-}
-
-impl<A: Positioned + Identified<EntityID>> EntityMap<A> {
-    pub fn insert(&mut self, mut entity: A) -> EntityID {
-        let pos = entity.position();
-        let entity_id = self.next_id();
-        entity.set_id(entity_id);
-        self.by_id.entry(entity_id).or_insert(entity);
-        self.by_position
-            .entry(pos)
-            .or_insert_with(Vec::new)
-            .push(entity_id);
-        entity_id
-    }
-
-    /// Remove the entity with the given ID
-    pub fn remove(&mut self, id: EntityID) -> Option<A> {
-        self.by_id.remove(&id).map(|e| {
-            let mut empty = false;
-            let position = e.position();
-
-            if let Some(es) = self.by_position.get_mut(&position) {
-                es.retain(|e| *e != id);
-                if es.is_empty() {
-                    empty = true;
-                }
-            }
-
-            if empty {
-                self.by_position.remove(&position);
-            }
-            e
-        })
-    }
-
-    /// Moves all elements from `other` into `Self`, leathing `other` empty.
-    pub fn append(&mut self, other: &mut Self) {
-        // TODO there's probably some perf opportunities here by calling
-        // reserve() on stuff
-        for (_, entity) in other.drain() {
-            self.insert(entity);
-        }
-    }
-
-    /// Gets all 8 neighbors of the given position.
-    pub fn neighbors<'a>(
-        &'a self,
-        position: Position,
-    ) -> Neighbors<Vec<(EntityID, &'a A)>> {
-        Neighbors::of_position(position)
-            .map(|pos| self.at(*pos))
-            .mapmap(&|e| (e.id(), *e))
-    }
-
-    pub fn neighbor_entities<'a>(
-        &'a self,
-        position: Position,
-    ) -> Neighbors<Vec<&'a A>> {
-        self.neighbors(position).mapmap(&|(_eid, ent)| *ent)
-    }
-
-    pub fn check_invariants(&self) {
-        for (id, ent) in &self.by_id {
-            assert_eq!(*id, ent.id());
-        }
-
-        for (pos, ents) in &self.by_position {
-            for eid in ents {
-                let ent = self.by_id.get(eid).unwrap();
-                assert_eq!(*pos, ent.position())
-            }
-        }
-    }
-}
-
-impl<'a, A: Positioned + Identified<EntityID>> IntoIterator
-    for &'a EntityMap<A>
-{
-    type Item = (&'a EntityID, &'a A);
-    type IntoIter = std::collections::hash_map::Iter<'a, EntityID, A>;
-    fn into_iter(self) -> Self::IntoIter {
-        (&self.by_id).iter()
-    }
-}
-
-impl<A: Positioned + Identified<EntityID>> IntoIterator for EntityMap<A> {
-    type Item = (EntityID, A);
-    type IntoIter = std::collections::hash_map::IntoIter<EntityID, A>;
-    fn into_iter(self) -> Self::IntoIter {
-        self.by_id.into_iter()
-    }
-}
-
-impl<A: Positioned + Identified<EntityID>> FromIterator<A> for EntityMap<A> {
-    fn from_iter<I: IntoIterator<Item = A>>(iter: I) -> Self {
-        let mut em = EntityMap::new();
-        for ent in iter {
-            em.insert(ent);
-        }
-        em
-    }
-}
-
-impl<A: Positioned + Identified<EntityID> + Eq + Clone> AbstractMagma<Additive>
-    for EntityMap<A>
-{
-    fn operate(&self, right: &Self) -> Self {
-        let mut by_position = self.by_position.clone();
-        by_position.append(&mut right.by_position.clone());
-
-        let mut by_id = self.by_id.clone();
-        for (k, v) in right.by_id.clone() {
-            by_id.insert(k, v);
-        }
-
-        EntityMap {
-            by_position,
-            by_id,
-            last_id: self.last_id.max(right.last_id),
-        }
-    }
-}
-
-impl<A: Positioned + Identified<EntityID> + Eq + Clone>
-    AbstractSemigroup<Additive> for EntityMap<A>
-{
-}
-
-impl<A: Positioned + Identified<EntityID> + Eq> Identity<Additive>
-    for EntityMap<A>
-{
-    fn identity() -> Self {
-        EntityMap::new()
-    }
-}
-
-impl<A: Positioned + Identified<EntityID> + Eq + Clone> AbstractMonoid<Additive>
-    for EntityMap<A>
-{
-}
-
-impl<A: PositionedMut> EntityMap<A> {
-    pub fn update_position(
-        &mut self,
-        entity_id: EntityID,
-        new_position: Position,
-    ) {
-        let mut old_pos = None;
-        if let Some(entity) = self.by_id.get_mut(&entity_id) {
-            if entity.position() == new_position {
-                return;
-            }
-            old_pos = Some(entity.position());
-            entity.set_position(new_position);
-        }
-
-        if let Some(p) = old_pos {
-            if let Some(es) = self.by_position.get_mut(&p) {
-                es.retain(|e| *e != entity_id);
-            }
-
-            self.by_position
-                .entry(new_position)
-                .or_insert_with(Vec::new)
-                .push(entity_id);
-        }
-    }
-}
-
-pub struct Drain<'a, A> {
-    map: &'a mut EntityMap<A>,
-    ids_iter: Box<dyn Iterator<Item = EntityID> + 'a>,
-}
-
-impl<A: Positioned + Identified<EntityID>> Iterator for Drain<'_, A> {
-    type Item = (EntityID, A);
-
-    fn next(&mut self) -> Option<Self::Item> {
-        self.ids_iter
-            .next()
-            .map(|eid| (eid, self.map.remove(eid).expect(BY_POS_INVARIANT)))
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::types::PositionedMut;
-    use proptest::prelude::*;
-    use proptest_derive::Arbitrary;
-
-    #[derive(Debug, Arbitrary, PartialEq, Eq, Clone)]
-    struct TestEntity {
-        _id: Option<EntityID>,
-        position: Position,
-        name: String,
-    }
-
-    impl Positioned for TestEntity {
-        fn position(&self) -> Position {
-            self.position
-        }
-    }
-
-    impl PositionedMut for TestEntity {
-        fn set_position(&mut self, pos: Position) {
-            self.position = pos
-        }
-    }
-
-    impl Identified<EntityID> for TestEntity {
-        fn opt_id(&self) -> Option<EntityID> {
-            self._id
-        }
-
-        fn set_id(&mut self, id: EntityID) {
-            self._id = Some(id);
-        }
-    }
-
-    fn gen_entity_map() -> BoxedStrategy<EntityMap<TestEntity>> {
-        any::<Vec<TestEntity>>()
-            .prop_map(|ents| {
-                ents.iter().cloned().collect::<EntityMap<TestEntity>>()
-            })
-            .boxed()
-    }
-
-    proptest! {
-        #![proptest_config(ProptestConfig::with_cases(10))]
-
-        #[test]
-        fn test_entity_map_len(items: Vec<TestEntity>) {
-            let mut map = EntityMap::new();
-            assert_eq!(map.len(), 0);
-            for ent in &items {
-                map.insert(ent.clone());
-            }
-            assert_eq!(map.len(), items.len());
-        }
-
-        #[test]
-        fn test_entity_map_getset(
-            mut em in gen_entity_map(),
-            ent: TestEntity
-        ) {
-            em.insert(ent.clone());
-            assert!(em.at(ent.position).iter().any(|e| e.name == ent.name))
-        }
-
-        #[test]
-        fn test_entity_map_set_iter_contains(
-            mut em in gen_entity_map(),
-            ent: TestEntity
-        ) {
-            em.insert(ent.clone());
-            assert!(em.entities().any(|e| e.name == ent.name))
-        }
-
-        #[test]
-        fn test_update_position(
-            mut em in gen_entity_map(),
-            ent: TestEntity,
-            new_position: Position,
-        ) {
-            let original_position = ent.position();
-            let entity_id = em.insert(ent.clone());
-            em.update_position(entity_id, new_position);
-
-            if new_position != original_position {
-                assert!(em.at(original_position).iter().all(|e| e.name != ent.name));
-            }
-            assert_eq!(
-                em.get(entity_id).map(|e| e.position()),
-                Some(new_position)
-            );
-            assert!(
-                em.at(new_position).iter().map(
-                    |e| e.name.clone()).any(|en| en == ent.name),
-            )
-        }
-
-        #[test]
-        fn test_remove_all_at(
-            mut em in gen_entity_map(),
-            pos: Position,
-        ) {
-            em.remove_all_at(pos);
-            assert_eq!(em.at(pos).len(), 0);
-        }
-
-        #[test]
-        fn test_entity_map_semigroup_laws(
-            em1 in gen_entity_map(),
-            em2 in gen_entity_map(),
-            em3 in gen_entity_map(),
-        ) {
-            assert!(AbstractSemigroup::prop_is_associative((em1, em2, em3)));
-        }
-
-        fn test_entity_map_monoid_laws(
-            em in gen_entity_map(),
-        ) {
-            assert!(
-                AbstractMonoid::prop_operating_identity_element_is_noop((em,))
-            );
-        }
-
-        #[test]
-        fn test_entity_map_append(
-            mut target in gen_entity_map(),
-            mut source in gen_entity_map(),
-        ) {
-            let orig_target = target.clone();
-            let orig_source = source.clone();
-
-            target.append(&mut source);
-            target.check_invariants();
-
-            assert_eq!(source, EntityMap::new());
-
-            for ent in orig_source.entities() {
-                assert!(
-                    target.at(ent.position()).iter().any(|e| e.name == ent.name)
-                );
-            }
-
-            for ent in orig_target.entities() {
-                assert!(
-                    target.at(ent.position()).iter().any(|e| e.name == ent.name)
-                );
-            }
-        }
-    }
-}
diff --git a/src/types/menu.rs b/src/types/menu.rs
deleted file mode 100644
index 63abc83778..0000000000
--- a/src/types/menu.rs
+++ /dev/null
@@ -1,31 +0,0 @@
-use crate::types::Dimensions;
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct MenuInfo {
-    pub prompt: String,
-    pub options: Vec<String>,
-}
-
-impl MenuInfo {
-    pub fn new(prompt: String, options: Vec<String>) -> Self {
-        MenuInfo { prompt, options }
-    }
-
-    /// Returns the inner dimensions of a box necessary to draw this menu. Will
-    /// not trim either dimension to the size of the terminal
-    pub fn dimensions(&self) -> Dimensions {
-        Dimensions {
-            w: self
-                .options
-                .iter()
-                .map(|s| s.len())
-                .max()
-                .unwrap_or(0)
-                .max(self.prompt.len()) as u16
-                + 4,
-            h: self.options.len() as u16
-                + if self.prompt.is_empty() { 0 } else { 2 }
-                + 4,
-        }
-    }
-}
diff --git a/src/types/mod.rs b/src/types/mod.rs
deleted file mode 100644
index d417e873d8..0000000000
--- a/src/types/mod.rs
+++ /dev/null
@@ -1,504 +0,0 @@
-#![allow(clippy::unit_arg)]
-#![allow(clippy::identity_conversion)]
-
-use std::cmp::max;
-use std::cmp::Ordering;
-use std::ops;
-use std::rc::Rc;
-
-pub mod collision;
-pub mod command;
-pub mod direction;
-pub mod entity_map;
-pub mod menu;
-
-pub use collision::Collision;
-pub use direction::Direction;
-pub use direction::Direction::*;
-use proptest_derive::Arbitrary;
-use termion::cursor;
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Arbitrary)]
-pub struct Dimensions {
-    #[proptest(strategy = "std::ops::Range::<u16>::from(0..100)")]
-    pub w: u16,
-
-    #[proptest(strategy = "std::ops::Range::<u16>::from(0..100)")]
-    pub h: u16,
-}
-
-pub const ZERO_DIMENSIONS: Dimensions = Dimensions { w: 0, h: 0 };
-pub const UNIT_DIMENSIONS: Dimensions = Dimensions { w: 1, h: 1 };
-
-impl Default for Dimensions {
-    fn default() -> Self {
-        Dimensions { w: 80, h: 20 }
-    }
-}
-
-impl ops::Sub<Dimensions> for Dimensions {
-    type Output = Dimensions;
-    fn sub(self, dims: Dimensions) -> Dimensions {
-        Dimensions {
-            w: self.w - dims.w,
-            h: self.h - dims.h,
-        }
-    }
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Arbitrary)]
-pub struct BoundingBox {
-    pub dimensions: Dimensions,
-    pub position: Position,
-}
-
-impl BoundingBox {
-    pub fn at_origin(dimensions: Dimensions) -> BoundingBox {
-        BoundingBox {
-            dimensions,
-            position: ORIGIN,
-        }
-    }
-
-    pub fn from_corners(
-        top_left: Position,
-        lower_right: Position,
-    ) -> BoundingBox {
-        BoundingBox {
-            position: top_left,
-            dimensions: Dimensions {
-                w: (lower_right.x - top_left.x) as u16,
-                h: (lower_right.y - top_left.y) as u16,
-            },
-        }
-    }
-
-    pub fn lr_corner(self) -> Position {
-        self.position
-            + (Position {
-                x: self.dimensions.w as i16,
-                y: self.dimensions.h as i16,
-            })
-    }
-
-    pub fn ll_corner(self) -> Position {
-        self.position
-            + (Position {
-                x: 0,
-                y: self.dimensions.h as i16,
-            })
-    }
-
-    /// Returns a bounding box representing the *inside* of this box if it was
-    /// drawn on the screen.
-    pub fn inner(self) -> BoundingBox {
-        self + UNIT_POSITION - UNIT_DIMENSIONS - UNIT_DIMENSIONS
-    }
-
-    /// Moves the top right corner of the bounding box by the offset specified
-    /// by the given position, keeping the lower right corner in place
-    pub fn move_tr_corner(self, offset: Position) -> BoundingBox {
-        self + offset
-            - Dimensions {
-                w: offset.x as u16,
-                h: offset.y as u16,
-            }
-    }
-}
-
-impl ops::Add<Position> for BoundingBox {
-    type Output = BoundingBox;
-    fn add(self, pos: Position) -> BoundingBox {
-        BoundingBox {
-            position: self.position + pos,
-            ..self
-        }
-    }
-}
-
-impl ops::Sub<Dimensions> for BoundingBox {
-    type Output = BoundingBox;
-    fn sub(self, dims: Dimensions) -> BoundingBox {
-        BoundingBox {
-            dimensions: self.dimensions - dims,
-            ..self
-        }
-    }
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Arbitrary, Hash, Ord)]
-pub struct Position {
-    /// x (horizontal) position
-    #[proptest(strategy = "std::ops::Range::<i16>::from(0..100)")]
-    pub x: i16,
-
-    #[proptest(strategy = "std::ops::Range::<i16>::from(0..100)")]
-    /// y (vertical) position
-    pub y: i16,
-}
-
-pub fn pos(x: i16, y: i16) -> Position {
-    Position { x, y }
-}
-
-pub const ORIGIN: Position = Position { x: 0, y: 0 };
-pub const UNIT_POSITION: Position = Position { x: 1, y: 1 };
-
-impl Position {
-    /// Returns true if this position exists within the bounds of the given box,
-    /// inclusive
-    pub fn within(self, b: BoundingBox) -> bool {
-        (self > b.position - UNIT_POSITION) && self < (b.lr_corner())
-    }
-
-    /// Returns a sequence of ASCII escape characters for moving the cursor to
-    /// this Position
-    pub fn cursor_goto(self) -> cursor::Goto {
-        // + 1 because Goto is 1-based, but position is 0-based
-        cursor::Goto(self.x as u16 + 1, self.y as u16 + 1)
-    }
-
-    /// Converts this position to the number of `Tiles` away from the origin it
-    /// represents. Usually done after subtracting two positions. Gives distance
-    /// as the crow flies
-    pub fn as_tiles(self) -> Tiles {
-        Tiles(max(self.x.abs(), self.y.abs()).into())
-    }
-}
-
-impl PartialOrd for Position {
-    fn partial_cmp(&self, other: &Position) -> Option<Ordering> {
-        if self.x == other.x && self.y == other.y {
-            Some(Ordering::Equal)
-        } else if self.x > other.x && self.y > other.y {
-            Some(Ordering::Greater)
-        } else if self.x < other.x && self.y < other.y {
-            Some(Ordering::Less)
-        } else {
-            None
-        }
-    }
-}
-
-/// Implements (bounded) addition of a Dimension to a position.
-///
-/// # Examples
-///
-/// ```
-/// let pos = Position { x: 1, y: 10 }
-///
-/// let left_pos = pos + Direction::Left
-/// assert_eq!(left, Position { x: 0, y: 10 })
-///
-/// let right_pos = pos + Direction::Right
-/// assert_eq!(right_pos, Position { x: 0, y: 10 })
-/// ```
-#[allow(clippy::suspicious_arithmetic_impl)]
-impl ops::Add<Direction> for Position {
-    type Output = Position;
-    fn add(self, dir: Direction) -> Position {
-        match dir {
-            Left => {
-                if self.x > std::i16::MIN {
-                    Position {
-                        x: self.x - 1,
-                        ..self
-                    }
-                } else {
-                    self
-                }
-            }
-            Right => {
-                if self.x < std::i16::MAX {
-                    Position {
-                        x: self.x + 1,
-                        ..self
-                    }
-                } else {
-                    self
-                }
-            }
-            Up => {
-                if self.y > std::i16::MIN {
-                    Position {
-                        y: self.y - 1,
-                        ..self
-                    }
-                } else {
-                    self
-                }
-            }
-            Down => {
-                if self.y < std::i16::MAX {
-                    Position {
-                        y: self.y + 1,
-                        ..self
-                    }
-                } else {
-                    self
-                }
-            }
-            UpLeft => self + Up + Left,
-            UpRight => self + Up + Right,
-            DownLeft => self + Down + Left,
-            DownRight => self + Down + Right,
-        }
-    }
-}
-
-impl ops::Add<Position> for Position {
-    type Output = Position;
-    fn add(self, pos: Position) -> Position {
-        Position {
-            x: self.x + pos.x,
-            y: self.y + pos.y,
-        }
-    }
-}
-
-impl ops::Sub<Position> for Position {
-    type Output = Position;
-    fn sub(self, pos: Position) -> Position {
-        Position {
-            x: self.x - pos.x,
-            y: self.y - pos.y,
-        }
-    }
-}
-
-impl Positioned for Position {
-    fn position(&self) -> Position {
-        *self
-    }
-}
-
-pub trait Positioned {
-    fn x(&self) -> i16 {
-        self.position().x
-    }
-
-    fn y(&self) -> i16 {
-        self.position().y
-    }
-
-    fn position(&self) -> Position {
-        Position {
-            x: self.x(),
-            y: self.y(),
-        }
-    }
-}
-
-pub trait PositionedMut: Positioned {
-    fn set_position(&mut self, pos: Position);
-}
-
-// impl<A, I> Positioned for A where A : Deref<Target = I>, I: Positioned {
-//     fn position(&self) -> Position {
-//         self.position()
-//     }
-// }
-
-impl<T: Positioned> Positioned for Box<T> {
-    fn position(&self) -> Position {
-        (**self).position()
-    }
-}
-
-impl<'a, T: Positioned> Positioned for &'a T {
-    fn position(&self) -> Position {
-        (**self).position()
-    }
-}
-
-impl<'a, T: Positioned> Positioned for &'a mut T {
-    fn position(&self) -> Position {
-        (**self).position()
-    }
-}
-
-impl<'a, T: Positioned> Positioned for Rc<T> {
-    fn position(&self) -> Position {
-        (**self).position()
-    }
-}
-
-impl<'a, T: PositionedMut> PositionedMut for &'a mut T {
-    fn set_position(&mut self, pos: Position) {
-        (**self).set_position(pos)
-    }
-}
-
-#[macro_export]
-macro_rules! positioned {
-    ($name:ident) => {
-        positioned!($name, position);
-    };
-    ($name:ident, $attr:ident) => {
-        impl $crate::types::Positioned for $name {
-            fn position(&self) -> $crate::types::Position {
-                self.$attr
-            }
-        }
-    };
-}
-
-#[macro_export]
-macro_rules! positioned_mut {
-    ($name:ident) => {
-        positioned_mut!($name, position);
-    };
-    ($name:ident, $attr:ident) => {
-        impl crate::types::PositionedMut for $name {
-            fn set_position(&mut self, pos: $crate::types::Position) {
-                self.$attr = pos;
-            }
-        }
-    };
-}
-
-/// A number of ticks
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Arbitrary)]
-pub struct Ticks(pub u16);
-
-/// A number of tiles
-///
-/// Expressed in terms of a float to allow moving partial tiles in a number of
-/// ticks
-#[derive(Clone, Copy, Debug, PartialEq, Arbitrary)]
-pub struct Tiles(pub f32);
-
-/// The speed of an entity, expressed in ticks per tile
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Arbitrary, Deserialize)]
-#[serde(transparent)]
-pub struct Speed(pub u32);
-
-impl Speed {
-    /// Returns the number of tiles that would be moved in the given number of
-    /// ticks at this speed
-    pub fn ticks_to_tiles(self, ticks: Ticks) -> Tiles {
-        Tiles(f32::from(ticks.0) / self.0 as f32)
-    }
-
-    /// Returns the number of ticks required to move the given number of tiles
-    /// at this speed
-    pub fn tiles_to_ticks(self, tiles: Tiles) -> Ticks {
-        Ticks(tiles.0 as u16 * self.0 as u16)
-    }
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Arbitrary)]
-pub struct Neighbors<A> {
-    pub top_left: A,
-    pub top: A,
-    pub top_right: A,
-    pub left: A,
-    pub right: A,
-    pub bottom_left: A,
-    pub bottom: A,
-    pub bottom_right: A,
-}
-
-impl Neighbors<Position> {
-    fn of_position(pos: Position) -> Self {
-        Neighbors {
-            top_left: pos + Direction::UpLeft,
-            top: pos + Direction::Up,
-            top_right: pos + Direction::UpRight,
-            left: pos + Direction::Left,
-            right: pos + Direction::Right,
-            bottom_left: pos + Direction::DownLeft,
-            bottom: pos + Direction::Down,
-            bottom_right: pos + Direction::DownRight,
-        }
-    }
-}
-
-impl<A> Neighbors<A> {
-    /// it's a functor, yo
-    pub fn map<B, F: Fn(&A) -> B>(&self, f: F) -> Neighbors<B> {
-        Neighbors {
-            top_left: f(&self.top_left),
-            top: f(&self.top),
-            top_right: f(&self.top_right),
-            left: f(&self.left),
-            right: f(&self.right),
-            bottom_left: f(&self.bottom_left),
-            bottom: f(&self.bottom),
-            bottom_right: f(&self.bottom_right),
-        }
-    }
-}
-
-impl<A> Neighbors<Vec<A>> {
-    pub fn mapmap<B, F: Fn(&A) -> B>(&self, f: &F) -> Neighbors<Vec<B>> {
-        self.map(|xs| xs.iter().map(f).collect())
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    #![allow(clippy::unnecessary_operation)]
-    use super::*;
-    use proptest::prelude::*;
-
-    proptest! {
-        #[test]
-        fn position_partialord_lt_transitive(
-            a: Position,
-            b: Position,
-            c: Position
-        ) {
-            if a < b && b < c {
-                assert!(a < c)
-            }
-        }
-
-        #[test]
-        fn position_partialord_eq_transitive(
-            a: Position,
-            b: Position,
-            c: Position
-        ) {
-            if a == b && b == c {
-                assert!(a == c)
-            }
-        }
-
-        #[test]
-        fn position_partialord_gt_transitive(
-            a: Position,
-            b: Position,
-            c: Position,
-        ) {
-            if a > b && b > c {
-                assert!(a > c)
-            }
-        }
-
-        #[test]
-        fn position_partialord_antisymmetric(a: Position, b: Position) {
-            if a < b {
-                assert!(!(a > b))
-            } else if a > b {
-                assert!(!(a < b))
-            }
-        }
-
-        #[test]
-        fn test_position_plus_dimension_as_tiles_monoid_action(
-            pos: Position,
-            dir: Direction,
-        ) {
-            prop_assume!(pos.y > 0 && pos.x > 0);
-            assert_eq!(((pos + dir) - pos).as_tiles(), Tiles(1.0));
-        }
-    }
-
-    #[test]
-    fn test_position_as_tiles() {
-        assert_eq!(pos(0, 0).as_tiles(), Tiles(0.0));
-        assert_eq!(pos(1, 1).as_tiles(), Tiles(1.0));
-        assert_eq!(pos(1, 2).as_tiles(), Tiles(2.0));
-    }
-}
diff --git a/src/util/mod.rs b/src/util/mod.rs
deleted file mode 100644
index dd5087a555..0000000000
--- a/src/util/mod.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-#[macro_use]
-pub mod static_cfg;
-#[macro_use]
-pub mod template;
-pub mod promise;
-#[macro_use]
-pub mod trait_impls;
diff --git a/src/util/promise.rs b/src/util/promise.rs
deleted file mode 100644
index 22f1e8b47f..0000000000
--- a/src/util/promise.rs
+++ /dev/null
@@ -1,160 +0,0 @@
-use std::future::Future;
-use std::pin::Pin;
-use std::sync::{Arc, RwLock};
-use std::task::{Context, Poll, Waker};
-
-type Waiter<Env, T> = Box<dyn Fn(&mut Env, &T)>;
-
-pub struct Promise<Env, T> {
-    inner: Arc<RwLock<Inner<T>>>,
-    waiters: Arc<RwLock<Vec<Waiter<Env, T>>>>,
-}
-
-pub struct Complete<T> {
-    inner: Arc<RwLock<Inner<T>>>,
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub struct Cancelled;
-
-struct Inner<T> {
-    value: Option<Arc<T>>,
-    waker: Option<Waker>,
-}
-
-pub fn promise<Env, T>() -> (Complete<T>, Promise<Env, T>) {
-    let inner = Arc::new(RwLock::new(Inner {
-        value: None,
-        waker: None,
-    }));
-    let promise = Promise {
-        inner: inner.clone(),
-        waiters: Arc::new(RwLock::new(Vec::new())),
-    };
-    let complete = Complete { inner };
-    (complete, promise)
-}
-
-impl<T> Complete<T> {
-    pub fn fulfill(&self, val: T) {
-        let mut inner = self.inner.write().unwrap();
-        inner.value = Some(Arc::new(val));
-        if let Some(waker) = inner.waker.take() {
-            waker.wake()
-        }
-    }
-}
-
-impl<T> Complete<Result<T, Cancelled>> {
-    pub fn cancel(&mut self) {
-        self.fulfill(Err(Cancelled))
-    }
-}
-
-impl<E, T> Complete<Result<T, E>> {
-    pub fn ok(&mut self, val: T) {
-        self.fulfill(Ok(val))
-    }
-
-    pub fn err(&mut self, e: E) {
-        self.fulfill(Err(e))
-    }
-}
-
-impl<Env, T> Promise<Env, T> {
-    pub fn on_fulfill<F: Fn(&mut Env, &T) + 'static>(&mut self, f: F) {
-        let mut waiters = self.waiters.write().unwrap();
-        waiters.push(Box::new(f));
-    }
-}
-
-impl<Env, T> Promise<Env, Result<T, Cancelled>> {
-    pub fn on_cancel<F: Fn(&mut Env) + 'static>(&mut self, f: F) {
-        self.on_err(move |env, _| f(env))
-    }
-}
-
-impl<Env, E, T> Promise<Env, Result<T, E>> {
-    pub fn on_ok<F: Fn(&mut Env, &T) + 'static>(&mut self, f: F) {
-        self.on_fulfill(move |env, r| {
-            if let Ok(val) = r {
-                f(env, val)
-            }
-        })
-    }
-
-    pub fn on_err<F: Fn(&mut Env, &E) + 'static>(&mut self, f: F) {
-        self.on_fulfill(move |env, r| {
-            if let Err(e) = r {
-                f(env, e)
-            }
-        })
-    }
-}
-
-pub trait Give<Env> {
-    fn give(&self, env: &mut Env) -> bool;
-}
-
-impl<Env, T> Give<Env> for Promise<Env, T> {
-    fn give(&self, env: &mut Env) -> bool {
-        let inner = self.inner.read().unwrap();
-        if let Some(value) = &inner.value {
-            let mut waiters = self.waiters.write().unwrap();
-            for waiter in waiters.iter() {
-                waiter(env, value);
-            }
-            waiters.clear();
-            true
-        } else {
-            false
-        }
-    }
-}
-
-impl<Env, T> Clone for Promise<Env, T> {
-    fn clone(&self) -> Self {
-        Promise {
-            inner: self.inner.clone(),
-            waiters: self.waiters.clone(),
-        }
-    }
-}
-
-impl<Env, P: Give<Env>> Give<Env> for &P {
-    fn give(&self, env: &mut Env) -> bool {
-        (*self).give(env)
-    }
-}
-
-impl<Env, T> Future for Promise<Env, T> {
-    type Output = Arc<T>;
-    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
-        let mut inner = self.inner.write().unwrap();
-        match inner.value {
-            Some(ref v) => Poll::Ready(v.clone()),
-            None => {
-                inner.waker = Some(cx.waker().clone());
-                Poll::Pending
-            }
-        }
-    }
-}
-
-pub struct Promises<'a, Env> {
-    ps: Vec<Box<dyn Give<Env> + 'a>>,
-}
-
-impl<'a, Env> Promises<'a, Env> {
-    pub fn new() -> Self {
-        Promises { ps: Vec::new() }
-    }
-
-    pub fn push(&mut self, p: Box<dyn Give<Env> + 'a>) {
-        self.ps.push(p);
-    }
-
-    pub fn give_all(&mut self, env: &mut Env) {
-        self.ps.retain(|p| !p.give(env));
-    }
-}
diff --git a/src/util/static_cfg.rs b/src/util/static_cfg.rs
deleted file mode 100644
index b20456fb3b..0000000000
--- a/src/util/static_cfg.rs
+++ /dev/null
@@ -1,147 +0,0 @@
-use include_dir::Dir;
-use serde::de;
-
-macro_rules! __static_cfg_include {
-    (toml_file, $filename:expr) => {
-        include_str!($filename)
-    };
-    (toml_dir, $filename:expr) => {
-        include_dir!($filename)
-    };
-    (json_file, $filename:expr) => {
-        include_str!($filename)
-    };
-    (json_dir, $filename:expr) => {
-        include_dir!($filename)
-    };
-    (cfg_dir, $filename:expr) => {
-        include_dir!($filename)
-    };
-}
-
-macro_rules! __static_cfg_type {
-    (toml_file) => (&'static str);
-    (json_file) => (&'static str);
-    (toml_dir) => (include_dir::Dir<'static>);
-    (json_dir) => (include_dir::Dir<'static>);
-    (cfg_dir) => (include_dir::Dir<'static>);
-}
-
-macro_rules! __static_cfg_parse {
-    (toml_file, $e:expr) => {
-        toml::from_str($e).unwrap()
-    };
-
-    (json_file, $e:expr) => {
-        serde_json::from_str($e).unwrap()
-    };
-
-    (toml_dir, $e:expr) => {
-        crate::util::static_cfg::parse_toml_dir($e)
-    };
-
-    (json_dir, $e:expr) => {
-        crate::util::static_cfg::parse_json_dir($e)
-    };
-
-    (cfg_dir, $e:expr) => {
-        crate::util::static_cfg::parse_cfg_dir($e);
-    };
-}
-
-macro_rules! __static_cfg_inner {
-    ($(#[$attr:meta])* ($($vis:tt)*) static ref $N:ident : $T:ty = $kind:ident($filename:expr); $($t:tt)*) => {
-        // static RAW: &'static str = __static_cfg_include!($kind, $filename);
-        static RAW: __static_cfg_type!($kind) = __static_cfg_include!($kind, $filename);
-        lazy_static! {
-            $(#[$attr])* static ref $N: $T = __static_cfg_parse!($kind, RAW);
-        }
-
-        static_cfg!($($t)*);
-    }
-}
-
-#[macro_export]
-macro_rules! static_cfg {
-    ($(#[$attr:meta])* static ref $N:ident : $T:ty = $kind:ident($filename:expr); $($t:tt)*) => {
-        __static_cfg_inner!($(#[$attr])* () static ref $N : $T = $kind($filename); $($t)*);
-    };
-
-    ($(#[$attr:meta])* pub static ref $N:ident : $T:ty = $kind:ident($filename:expr); $($t:tt)*) => {
-        __static_cfg_inner!($(#[$attr])* (pub) static ref $N : $T = $kind($filename); $($t)*);
-    };
-
-    ($(#[$attr:meta])* pub ($($vis:tt)+) static ref $N:ident : $T:ty = $kind:ident($filename:expr); $($t:tt)*) => {
-        __static_cfg_inner!($(#[$attr])* (pub ($($vis)+)) static ref $N : $T = $kind($filename); $($t)*);
-    };
-
-    () => ()
-}
-
-pub fn parse_cfg_dir<'a, T>(d: Dir<'a>) -> Vec<T>
-where
-    T: de::Deserialize<'a>,
-{
-    d.files()
-        .iter()
-        .filter_map(|f| {
-            let path = f.path();
-            let contents = f.contents_utf8().unwrap();
-            match path.extension().and_then(|e| e.to_str()) {
-                Some("toml") => {
-                    Some(toml::from_str(contents).unwrap_or_else(|e| {
-                        panic!(
-                            "Error parsing TOML file {}: {}",
-                            path.display(),
-                            e
-                        )
-                    }))
-                }
-                Some("json") => {
-                    Some(serde_json::from_str(contents).unwrap_or_else(|e| {
-                        panic!(
-                            "Error parsing JSON file {}: {}",
-                            path.display(),
-                            e
-                        )
-                    }))
-                }
-                // > YAML currently does not support zero-copy deserialization
-                // Some("yaml") => {
-                //     Some(serde_yaml::from_str(contents).unwrap_or_else(|e| {
-                //         panic!(
-                //             "Error parsing YAML file {}: {}",
-                //             path.display(),
-                //             e
-                //         )
-                //     }))
-                // }
-                _ => None,
-            }
-        })
-        .collect()
-}
-
-pub fn parse_toml_dir<'a, T>(d: Dir<'a>) -> Vec<T>
-where
-    T: de::Deserialize<'a>,
-{
-    d.files()
-        .iter()
-        .map(|f| {
-            toml::from_str(f.contents_utf8().unwrap()).unwrap_or_else(|e| {
-                panic!("Error parsing TOML file {}: {}", f.path, e)
-            })
-        })
-        .collect()
-}
-
-pub fn parse_json_dir<'a, T>(d: Dir<'a>) -> Vec<T>
-where
-    T: de::Deserialize<'a>,
-{
-    d.files()
-        .iter()
-        .map(|f| serde_json::from_str(f.contents_utf8().unwrap()).unwrap())
-        .collect()
-}
diff --git a/src/util/template.rs b/src/util/template.rs
deleted file mode 100644
index bb77f9b4d6..0000000000
--- a/src/util/template.rs
+++ /dev/null
@@ -1,362 +0,0 @@
-use nom::combinator::rest;
-use nom::error::ErrorKind;
-use nom::{Err, IResult};
-use std::collections::HashMap;
-use std::fmt::{self, Display};
-use std::marker::PhantomData;
-
-#[derive(Debug, PartialEq, Eq, Clone)]
-pub struct Path<'a> {
-    head: &'a str,
-    tail: Vec<&'a str>,
-}
-
-impl<'a> Path<'a> {
-    fn new(head: &'a str, tail: Vec<&'a str>) -> Self {
-        Path { head, tail }
-    }
-}
-
-impl<'a> Display for Path<'a> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "{}", self.head)?;
-        for part in &self.tail {
-            write!(f, ".{}", part)?;
-        }
-        Ok(())
-    }
-}
-
-// named!(path_ident, map_res!(is_not!(".}"), std::str::from_utf8));
-fn path_ident<'a>(input: &'a str) -> IResult<&'a str, &'a str> {
-    take_till!(input, |c| c == '.' || c == '}')
-}
-
-fn path<'a>(input: &'a str) -> IResult<&'a str, Path<'a>> {
-    map!(
-        input,
-        tuple!(
-            path_ident,
-            many0!(complete!(preceded!(char!('.'), path_ident)))
-        ),
-        |(h, t)| Path::new(h, t)
-    )
-}
-
-#[derive(Debug, PartialEq, Eq, Clone)]
-pub enum TemplateToken<'a> {
-    Literal(&'a str),
-    Substitution(Path<'a>),
-}
-
-fn token_substitution<'a>(
-    input: &'a str,
-) -> IResult<&'a str, TemplateToken<'a>> {
-    map!(
-        input,
-        delimited!(tag!("{{"), path, tag!("}}")),
-        TemplateToken::Substitution
-    )
-}
-
-fn template_token<'a>(input: &'a str) -> IResult<&'a str, TemplateToken<'a>> {
-    alt!(
-        input,
-        token_substitution
-            | map!(
-                alt!(complete!(take_until!("{{")) | complete!(rest)),
-                TemplateToken::Literal
-            )
-    )
-}
-
-#[derive(Debug, PartialEq, Eq, Clone)]
-pub struct Template<'a> {
-    tokens: Vec<TemplateToken<'a>>,
-}
-
-impl<'a> Template<'a> {
-    pub fn new(tokens: Vec<TemplateToken<'a>>) -> Self {
-        Template { tokens }
-    }
-}
-
-pub struct TemplateVisitor<'a> {
-    marker: PhantomData<fn() -> Template<'a>>,
-}
-
-impl<'a> TemplateVisitor<'a> {
-    pub fn new() -> Self {
-        TemplateVisitor {
-            marker: PhantomData,
-        }
-    }
-}
-
-impl<'a> serde::de::Visitor<'a> for TemplateVisitor<'a> {
-    type Value = Template<'a>;
-
-    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
-        formatter.write_str("a valid template string")
-    }
-
-    fn visit_borrowed_str<E: serde::de::Error>(
-        self,
-        v: &'a str,
-    ) -> Result<Self::Value, E> {
-        Template::parse(v).map_err(|_| {
-            serde::de::Error::invalid_value(
-                serde::de::Unexpected::Str(v),
-                &"a valid template string",
-            )
-        })
-    }
-}
-
-impl<'a> serde::Deserialize<'a> for Template<'a> {
-    fn deserialize<D: serde::Deserializer<'a>>(
-        deserializer: D,
-    ) -> Result<Self, D::Error> {
-        deserializer.deserialize_str(TemplateVisitor::new())
-    }
-}
-
-impl<'a> Template<'a> {
-    pub fn parse(
-        input: &'a str,
-    ) -> Result<Template<'a>, Err<(&'a str, ErrorKind)>> {
-        let (remaining, res) = template(input)?;
-        if !remaining.is_empty() {
-            unreachable!();
-        }
-        Ok(res)
-    }
-
-    pub fn format(
-        &self,
-        params: &TemplateParams<'a>,
-    ) -> Result<String, TemplateError<'a>> {
-        use TemplateToken::*;
-        let mut res = String::new();
-        for token in &self.tokens {
-            match token {
-                Literal(s) => res.push_str(s),
-                Substitution(p) => match params.get(p.clone()) {
-                    Some(s) => res.push_str(s),
-                    None => return Err(TemplateError::MissingParam(p.clone())),
-                },
-            }
-        }
-        Ok(res)
-    }
-}
-
-#[derive(Debug, PartialEq, Eq)]
-pub enum TemplateError<'a> {
-    MissingParam(Path<'a>),
-}
-
-impl<'a> Display for TemplateError<'a> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        use TemplateError::*;
-        match self {
-            MissingParam(path) => {
-                write!(f, "Missing template parameter: {}", path)
-            }
-        }
-    }
-}
-
-#[derive(Debug, PartialEq, Eq)]
-pub enum TemplateParams<'a> {
-    Direct(&'a str),
-    Nested(HashMap<&'a str, TemplateParams<'a>>),
-}
-
-impl<'a> TemplateParams<'a> {
-    fn get(&self, path: Path<'a>) -> Option<&'a str> {
-        use TemplateParams::*;
-        match self {
-            Direct(_) => None,
-            Nested(m) => m.get(path.head).and_then(|next| {
-                if path.tail.is_empty() {
-                    match next {
-                        Direct(s) => Some(*s),
-                        _ => None,
-                    }
-                } else {
-                    next.get(Path {
-                        head: path.tail[0],
-                        tail: path.tail[1..].to_vec(),
-                    })
-                }
-            }),
-        }
-    }
-}
-
-#[macro_export]
-macro_rules! template_params {
-    (@count $head: expr => $hv: tt, $($rest:tt)+) => { 1 + template_params!(@count $($rest)+) };
-    (@count $one:expr => $($ov: tt)*) => { 1 };
-    (@inner $ret: ident, ($key: expr => {$($v:tt)*}, $($r:tt)*)) => {
-        $ret.insert($key, template_params!({ $($v)* }));
-        template_params!(@inner $ret, ($($r)*));
-    };
-    (@inner $ret: ident, ($key: expr => $value: expr, $($r:tt)*)) => {
-        $ret.insert($key, template_params!($value));
-        template_params!(@inner $ret, ($($r)*));
-    };
-    (@inner $ret: ident, ()) => {};
-
-    ({ $($body: tt)* }) => {{
-        let _cap = template_params!(@count $($body)*);
-        let mut _m = ::std::collections::HashMap::with_capacity(_cap);
-        template_params!(@inner _m, ($($body)*));
-        TemplateParams::Nested(_m)
-    }};
-
-    ($direct:expr) => { TemplateParams::Direct($direct) };
-
-    () => { TemplateParams::Nested(::std::collections::HashMap::new()) };
-}
-
-fn template<'a>(input: &'a str) -> IResult<&'a str, Template<'a>> {
-    complete!(
-        input,
-        map!(many1!(complete!(template_token)), Template::new)
-    )
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    fn test_parse_path_ident() {
-        assert_eq!(path_ident("foo}}"), Ok(("}}", "foo")));
-        assert_eq!(path_ident("foo.bar}}"), Ok((".bar}}", "foo")));
-    }
-
-    #[test]
-    fn test_parse_path() {
-        assert_eq!(path("foo}}"), Ok(("}}", Path::new("foo", vec![]))));
-        assert_eq!(
-            path("foo.bar}}"),
-            Ok(("}}", Path::new("foo", vec!["bar"])))
-        );
-        assert_eq!(
-            path("foo.bar.baz}}"),
-            Ok(("}}", Path::new("foo", vec!["bar", "baz"])))
-        );
-    }
-
-    #[test]
-    fn test_parse_template_token() {
-        assert_eq!(
-            template_token("foo bar"),
-            Ok(("", TemplateToken::Literal("foo bar")))
-        );
-
-        assert_eq!(
-            template_token("foo bar {{baz}}"),
-            Ok(("{{baz}}", TemplateToken::Literal("foo bar ")))
-        );
-
-        assert_eq!(
-            template_token("{{baz}}"),
-            Ok((
-                "",
-                TemplateToken::Substitution(Path::new("baz", Vec::new()))
-            ))
-        );
-
-        assert_eq!(
-            template_token("{{baz}} foo bar"),
-            Ok((
-                " foo bar",
-                TemplateToken::Substitution(Path::new("baz", Vec::new()))
-            ))
-        );
-    }
-
-    #[test]
-    fn test_parse_template() {
-        assert_eq!(
-            template("foo bar"),
-            Ok((
-                "",
-                Template {
-                    tokens: vec![TemplateToken::Literal("foo bar")]
-                }
-            ))
-        );
-
-        assert_eq!(
-            template("foo bar {{baz}} qux"),
-            Ok((
-                "",
-                Template {
-                    tokens: vec![
-                        TemplateToken::Literal("foo bar "),
-                        TemplateToken::Substitution(Path::new(
-                            "baz",
-                            Vec::new()
-                        )),
-                        TemplateToken::Literal(" qux"),
-                    ]
-                }
-            ))
-        );
-    }
-
-    #[test]
-    fn test_template_params_literal() {
-        // trace_macros!(true);
-        let expected = template_params!({
-            "direct" => "hi",
-            "other" => "here",
-            "nested" => {
-                "one" => "1",
-                "two" => "2",
-                "double" => {
-                    "three" => "3",
-                },
-            },
-        });
-        // trace_macros!(false);
-        assert_eq!(
-            TemplateParams::Nested(hashmap! {
-                "direct" => TemplateParams::Direct("hi"),
-                "other" => TemplateParams::Direct("here"),
-                "nested" => TemplateParams::Nested(hashmap!{
-                    "one" => TemplateParams::Direct("1"),
-                    "two" => TemplateParams::Direct("2"),
-                    "double" => TemplateParams::Nested(hashmap!{
-                        "three" => TemplateParams::Direct("3"),
-                    })
-                })
-            }),
-            expected,
-        )
-    }
-
-    #[test]
-    fn test_format_template() {
-        assert_eq!(
-            "foo bar baz qux",
-            Template::parse("foo {{x}} {{y.z}} {{y.w.z}}")
-                .unwrap()
-                .format(&template_params!({
-                    "x" => "bar",
-                    "y" => {
-                        "z" => "baz",
-                        "w" => {
-                            "z" => "qux",
-                        },
-                    },
-                }))
-                .unwrap()
-        )
-    }
-}
diff --git a/src/util/trait_impls.rs b/src/util/trait_impls.rs
deleted file mode 100644
index ba15f7119d..0000000000
--- a/src/util/trait_impls.rs
+++ /dev/null
@@ -1,17 +0,0 @@
-macro_rules! ref_impl {
-    (impl<T: $traitb: ident $(+ $bound:ident)*> $traiti:ident for &T {
-        $($body:tt)*
-    }) => {
-        impl<'a, T: $traitb $(+ $bound)*> $traiti for &'a T {
-            $($body)*
-        }
-
-        impl<'a, T: $traitb $(+ $bound)*> $traiti for &'a mut T {
-            $($body)*
-        }
-
-        impl<T: $traitb $(+ $bound)*> $traiti for ::std::boxed::Box<T> {
-            $($body)*
-        }
-    };
-}