bitsy-parser/src/main.rs

833 lines
27 KiB
Rust

use std::collections::HashMap;
const IMAGE_DIMENSION_SD: usize = 8;
const IMAGE_DIMENSION_HD: usize = 16;
#[derive(Debug, Eq, PartialEq)]
struct Colour {
red: u8,
green: u8,
blue: u8,
}
#[derive(Debug, Eq, PartialEq)]
struct Palette {
id: String, // base36 string (why??)
name: Option<String>,
colours: Vec<Colour>,
}
#[derive(Debug, Eq, PartialEq)]
struct Image {
pixels: Vec<u8>, // 64 for SD, 256 for HD
}
#[derive(Debug, Eq, PartialEq)]
struct Tile {
id: String, // base36 string
name: Option<String>,
wall: bool,
animation_frames: Vec<Image>,
}
#[derive(Debug, Eq, PartialEq, Hash)]
struct Position {
x: u8,
y: u8,
}
#[derive(Debug, Eq, PartialEq)]
struct Dialogue {
id: String,
contents: String,
}
#[derive(Debug, Eq, PartialEq)]
struct Sprite {
id: String, // lowercase base36
name: Option<String>,
animation_frames: Vec<Image>,
dialogue: Option<String>, /// dialogue id
room: String, /// room id
position: Position,
}
/// avatar is a "sprite" in the game data but with a specific id
#[derive(Debug, Eq, PartialEq)]
struct Avatar {
animation_frames: Vec<Image>,
room: String, /// room id
position: Position,
}
#[derive(Debug, Eq, PartialEq)]
struct Item {
id: String,
animation_frames: Vec<Image>,
name: Option<String>,
dialogue: Option<String>, // dialogue id
}
#[derive(Debug, Eq, PartialEq)]
struct Exit {
/// destination
room: String, /// id
position: Position,
}
// same as a dialogue basically
#[derive(Debug, Eq, PartialEq)]
struct Ending {
id: String,
dialogue: String,
}
#[derive(Debug, Eq, PartialEq)]
struct Room {
id: String,
palette: String, // id
name: Option<String>,
tiles: Vec<String>, // tile ids
items: HashMap<Position, String>, // item id
exits: HashMap<Position, Exit>,
endings: HashMap<Position, String>, // ending id
}
#[derive(Debug, Eq, PartialEq)]
struct Variable {
id: String,
initial_value: String,
}
#[derive(Debug, PartialEq)]
struct Game {
name: String,
version: f64,
room_format: u8,
hd: bool,
palettes: Vec<Palette>,
variables: Vec<Variable>,
}
fn test_image_chequers_1() -> Image {
Image {
pixels: vec![
1,0,1,0,1,0,1,0,
0,1,0,1,0,1,0,1,
1,0,1,0,1,0,1,0,
0,1,0,1,0,1,0,1,
1,0,1,0,1,0,1,0,
0,1,0,1,0,1,0,1,
1,0,1,0,1,0,1,0,
0,1,0,1,0,1,0,1,
]
}
}
fn test_image_chequers_2() -> Image {
Image {
pixels: vec![
0,1,0,1,0,1,0,1,
1,0,1,0,1,0,1,0,
0,1,0,1,0,1,0,1,
1,0,1,0,1,0,1,0,
0,1,0,1,0,1,0,1,
1,0,1,0,1,0,1,0,
0,1,0,1,0,1,0,1,
1,0,1,0,1,0,1,0,
]
}
}
fn test_sprite() -> Sprite {
Sprite {
id: "a".to_string(),
name: Some("hatch".to_string()),
animation_frames: vec![
Image {
pixels: vec![
0,0,0,0,0,0,0,0,
0,1,1,1,1,0,0,0,
0,1,0,0,1,0,0,0,
0,0,1,1,1,1,0,0,
0,0,1,1,1,1,0,0,
0,1,0,1,1,1,1,0,
0,1,0,1,1,1,1,0,
0,1,1,0,1,1,1,1,
]
}
],
dialogue: Some("SPR_0".to_string()),
room: "4".to_string(),
position: Position {
x: 9,
y: 7
}
}
}
fn test_item() -> Item {
Item {
id: "6".to_string(),
animation_frames: vec![
Image {
pixels: vec![
0,1,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,1,0,0,
0,0,1,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,1,0,
]
}
],
name: Some("door".to_string()),
dialogue: Some("ITM_2".to_string())
}
}
fn image_from_string(string: String) -> Image {
let string = string.replace("\n", "");
let pixels: Vec<&str> = string.split("").collect();
// the above seems to add an extra "" at the start and end of the vec, so strip them below
let pixels = &pixels[1..(pixels.len() - 1)];
let pixels: Vec<u8> = pixels.iter().map(|&pixel| { pixel.parse::<u8>().unwrap() }).collect();
Image { pixels }
}
#[test]
fn test_image_from_string() {
let output = image_from_string(
"11111111\n11001111\n10111111\n11111111\n11111111\n11111111\n11111111\n11111111".to_string()
);
let expected = Image {
pixels: vec![
1,1,1,1,1,1,1,1,
1,1,0,0,1,1,1,1,
1,0,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
]
};
assert_eq!(output, expected)
}
fn image_to_string(image: Image) -> String {
image_to_string_opts(image, false)
}
fn image_to_string_opts(image: Image, hd: bool) -> String {
let mut string = String::new();
let chunk_size = if hd {IMAGE_DIMENSION_HD} else {IMAGE_DIMENSION_SD};
for line in image.pixels.chunks(chunk_size) {
for pixel in line {
string.push_str(&format!("{}", *pixel));
}
string.push('\n');
}
string.pop(); // remove trailing newline
string
}
#[test]
fn test_image_to_string() {
let output = image_to_string(test_image_chequers_1());
let expected = "10101010\n01010101\n10101010\n01010101\n10101010\n01010101\n10101010\n01010101".to_string();
assert_eq!(output, expected);
}
fn animation_frames_to_string(animation_frames: Vec<Image>) -> String {
let mut string = String::new();
let last_frame = animation_frames.len() - 1;
for (i, frame) in animation_frames.into_iter().enumerate() {
string.push_str(&image_to_string(frame));
if i < last_frame {
string.push_str(&"\n>\n".to_string());
}
}
string
}
fn tile_from_string(string: String) -> Tile {
let mut lines: Vec<&str> = string.split("\n").collect();
let id = lines[0].replace("TIL ", "");
let last_line = lines.pop().unwrap();
let wall = match last_line == "WAL true" {
true => true,
false => {
lines.push(last_line);
false
}
};
let last_line = lines.pop().unwrap();
let name = match last_line.starts_with("NAME") {
true => Some(last_line.replace("NAME ", "").to_string()),
false => {
lines.push(last_line);
None
}
};
let animation_frames = lines[1..].join("");
let animation_frames: Vec<&str> = animation_frames.split("\n>\n").collect();
let animation_frames: Vec<Image> = animation_frames.iter().map(|&frame| {
image_from_string(frame.to_string())
}).collect();
Tile {id, name, wall, animation_frames}
}
#[test]
fn test_tile_from_string() {
let output = tile_from_string("TIL z\n11111111\n11111111\n11111111\n11111111\n11111111\n11111111\n11111111\n11111111\nNAME concrete 1\nWAL true".to_string());
let expected = Tile {
id: "z".to_string(),
name: Some("concrete 1".to_string()),
wall: true,
animation_frames: vec![
Image {
pixels: vec![1; 64]
}
],
};
assert_eq!(output, expected);
}
fn tile_to_string(tile: Tile) -> String {
format!(
"TIL {}\n{}{}{}",
tile.id,
animation_frames_to_string(tile.animation_frames),
if tile.name.is_some() {format!("\nNAME {}", tile.name.unwrap())} else {"".to_string()},
if tile.wall {"\nWAL true"} else {""}
)
}
#[test]
fn test_tile_to_string() {
let output = tile_to_string(Tile {
id: "7a".to_string(),
name: Some("chequers".to_string()),
wall: false,
animation_frames: vec![
test_image_chequers_1(),
test_image_chequers_2(),
]
});
let expected = "TIL 7a\n10101010\n01010101\n10101010\n01010101\n10101010\n01010101\n10101010\n01010101\n>\n01010101\n10101010\n01010101\n10101010\n01010101\n10101010\n01010101\n10101010\nNAME chequers".to_string();
assert_eq!(output, expected);
}
fn colour_from_string(colour: String) -> Colour {
let values: Vec<&str> = colour.split(',').collect();
let red: u8 = values[0].parse().unwrap_or(0);
let green: u8 = values[1].parse().unwrap_or(0);
let blue: u8 = values[2].parse().unwrap_or(0);
Colour { red, green, blue }
}
#[test]
fn test_colour_from_string() {
assert_eq!(
colour_from_string("0,255,0".to_string()),
Colour { red: 0, green: 255, blue: 0 }
);
}
fn colour_to_string(colour: Colour) -> String {
format!("{},{},{}", colour.red, colour.green, colour.blue)
}
#[test]
fn test_colour_to_string() {
assert_eq!(
colour_to_string(Colour { red: 22, green: 33, blue: 44 }),
"22,33,44".to_string()
);
}
fn palette_from_string(palette: String) -> Palette {
let lines: Vec<&str> = palette.split('\n').collect();
let id = lines[0].replace("PAL ", "");
let name = match lines[1].starts_with("NAME") {
true => Some(lines[1].replace("NAME ", "").to_string()),
false => None,
};
let colour_start_index = if name.is_some() {2} else {1};
let colours = lines[colour_start_index..].iter().map(|&line| {
colour_from_string(line.to_string())
}).collect();
Palette { id, name, colours }
}
#[test]
fn test_palette_from_string() {
let output = palette_from_string(
"PAL 1\nNAME lamplight\n45,45,59\n66,60,39\n140,94,1".to_string()
);
let expected = Palette {
id: "1".to_string(),
name: Some("lamplight".to_string()),
colours: vec![
Colour {red: 45, green: 45, blue: 59},
Colour {red: 66, green: 60, blue: 39},
Colour {red: 140, green: 94, blue: 1 },
],
};
assert_eq!(output, expected);
}
#[test]
fn test_palette_from_string_no_name() {
let output = palette_from_string(
"PAL 9\n45,45,59\n66,60,39\n140,94,1".to_string()
);
let expected = Palette {
id: "9".to_string(),
name: None,
colours: vec![
Colour {red: 45, green: 45, blue: 59},
Colour {red: 66, green: 60, blue: 39},
Colour {red: 140, green: 94, blue: 1 },
],
};
assert_eq!(output, expected);
}
fn position_from_string(string: String) -> Position {
// e.g. "2,5"
let xy: Vec<&str> = string.split(',').collect();
let x = xy[0].parse().unwrap();
let y = xy[1].parse().unwrap();
Position {x, y}
}
#[test]
fn test_position_from_string() {
assert_eq!(
position_from_string("4,12".to_string()),
Position { x: 4, y: 12 }
)
}
fn position_to_string(position: Position) -> String {
format!("{},{}", position.x, position.y)
}
#[test]
fn test_position_to_string() {
assert_eq!(
position_to_string(Position { x: 4, y: 12 }),
"4,12".to_string()
)
}
fn sprite_from_string(string: String) -> Sprite {
let mut lines: Vec<&str> = string.split("\n").collect();
let id = lines[0].replace("SPR ", "");
let mut name = None;
let mut dialogue = None;
let mut room: Option<String> = None;
let mut position: Option<Position> = None;
for _ in 0..3 {
let last_line = lines.pop().unwrap();
if last_line.starts_with("NAME") {
name = Some(last_line.replace("NAME ", "").to_string());
} else if last_line.starts_with("DLG") {
dialogue = Some(last_line.replace("DLG ", "").to_string());
} else if last_line.starts_with("POS") {
let last_line = last_line.replace("POS ", "");
let room_position: Vec<&str> = last_line.split(' ').collect();
room = Some(room_position[0].to_string());
position = Some(position_from_string(room_position[1].to_string()));
} else {
lines.push(last_line); break;
}
}
let room = room.unwrap();
let position = position.unwrap();
// todo dedupe
let animation_frames = lines[1..].join("");
let animation_frames: Vec<&str> = animation_frames.split("\n>\n").collect();
let animation_frames: Vec<Image> = animation_frames.iter().map(|&frame| {
image_from_string(frame.to_string())
}).collect();
Sprite { id, name, animation_frames, dialogue, room, position }
}
#[test]
fn test_sprite_from_string() {
let output = sprite_from_string("SPR a\n00000000\n01111000\n01001000\n00111100\n00111100\n01011110\n01011110\n01101111\nNAME hatch\nDLG SPR_0\nPOS 4 9,7".to_string());
let expected = test_sprite();
assert_eq!(output, expected);
}
fn sprite_to_string(sprite: Sprite) -> String {
format!(
"SPR {}\n{}{}{}\nPOS {}",
sprite.id,
animation_frames_to_string(sprite.animation_frames),
if sprite.name.is_some() {format!("\nNAME {}", sprite.name.unwrap())} else {"".to_string()},
if sprite.dialogue.is_some() {format!("\nDLG {}", sprite.dialogue.unwrap())} else {"".to_string()},
position_to_string(sprite.position),
)
}
#[test]
fn test_sprite_to_string() {
let output = sprite_to_string(test_sprite());
let expected = "SPR a\n00000000\n01111000\n01001000\n00111100\n00111100\n01011110\n01011110\n01101111\nNAME hatch\nDLG SPR_0\nPOS 4 9,7".to_string();
assert_eq!(output, expected);
}
fn item_from_string(string: String) -> Item {
let mut lines: Vec<&str> = string.split("\n").collect();
let id = lines[0].replace("ITM ", "");
let mut name = None;
let mut dialogue = None;
for _ in 0..2 {
let last_line = lines.pop().unwrap();
if last_line.starts_with("NAME") {
name = Some(last_line.replace("NAME ", "").to_string());
} else if last_line.starts_with("DLG") {
dialogue = Some(last_line.replace("DLG ", "").to_string());
} else {
lines.push(last_line); break;
}
}
// todo dedupe
let animation_frames = lines[1..].join("");
let animation_frames: Vec<&str> = animation_frames.split("\n>\n").collect();
let animation_frames: Vec<Image> = animation_frames.iter().map(|&frame| {
image_from_string(frame.to_string())
}).collect();
Item { id, name, animation_frames, dialogue }
}
#[test]
fn test_item_from_string() {
let output = item_from_string("ITM 6\n01000000\n00000000\n00000000\n00000100\n00100000\n00000000\n00000000\n00000010\nNAME door\nDLG ITM_2".to_string());
let expected = test_item();
assert_eq!(output, expected);
}
fn item_to_string(item: Item) -> String {
format!(
"ITM {}\n{}{}{}",
item.id,
animation_frames_to_string(item.animation_frames),
if item.name.is_some() {format!("\nNAME {}", item.name.unwrap())} else {"".to_string()},
if item.dialogue.is_some() {format!("\nDLG {}", item.dialogue.unwrap())} else {"".to_string()},
)
}
#[test]
fn test_item_to_string() {
let output = item_to_string(test_item());
let expected = "ITM 6\n01000000\n00000000\n00000000\n00000100\n00100000\n00000000\n00000000\n00000010\nNAME door\nDLG ITM_2".to_string();
assert_eq!(output, expected);
}
fn exit_from_string(string: String) -> Exit {
// e.g. "4 3,3"
let room_position: Vec<&str> = string.split(' ').collect();
let room = room_position[0].to_string();
let position = position_from_string(room_position[1].to_string());
Exit { room, position }
}
#[test]
fn test_exit_from_string() {
assert_eq!(
exit_from_string("a 12,13".to_string()),
Exit { room: "a".to_string(), position: Position { x: 12, y: 13}}
);
}
fn exit_to_string(exit: Exit) -> String {
format!("{} {}", exit.room, position_to_string(exit.position))
}
#[test]
fn test_exit_to_string() {
assert_eq!(
exit_to_string(Exit { room: "8".to_string(), position: Position { x: 5, y: 6 }}),
"8 5,6".to_string()
);
}
fn ending_from_string(string: String) -> Ending {
let string = string.replace("END ", "");
let id_dialogue: Vec<&str> = string.split('\n').collect();
let id = id_dialogue[0].to_string();
let dialogue = id_dialogue[1].to_string();
Ending { id, dialogue }
}
#[test]
fn test_ending_from_string() {
assert_eq!(
ending_from_string("END a\nThis is a long line of dialogue. Blah blah blah".to_string()),
Ending { id: "a".to_string(), dialogue: "This is a long line of dialogue. Blah blah blah".to_string() }
);
}
fn ending_to_string(ending: Ending) -> String {
format!("END {}\n{}", ending.id, ending.dialogue)
}
#[test]
fn test_ending_to_string() {
assert_eq!(
ending_to_string(
Ending {
id: "7".to_string(),
dialogue: "This is another long ending. So long, farewell, etc.".to_string()
}
),
"END 7\nThis is another long ending. So long, farewell, etc.".to_string()
);
}
fn dialogue_from_string(string: String) -> Dialogue {
let lines: Vec<&str> = string.split("\n").collect();
let id = lines[0].replace("DLG ", "").to_string();
let contents = lines[1..].join("\n");
Dialogue { id, contents }
}
#[test]
fn test_dialogue_from_string() {
assert_eq!(
dialogue_from_string("DLG h\nhello\ngoodbye".to_string()),
Dialogue { id: "h".to_string(), contents: "hello\ngoodbye".to_string()}
)
}
fn dialogue_to_string(dialogue: Dialogue) -> String {
format!("DLG {}\n{}", dialogue.id, dialogue.contents)
}
#[test]
fn test_dialogue_to_string() {
assert_eq!(
dialogue_to_string(
Dialogue {
id: "y".to_string(),
contents: "This is a bit of dialogue,\nblah blah\nblah blah".to_string()
}
),
"DLG y\nThis is a bit of dialogue,\nblah blah\nblah blah".to_string()
);
}
fn variable_from_string(string: String) -> Variable {
let id_value: Vec<&str> = string.split('\n').collect();
let id = id_value[0].replace("VAR ", "").to_string();
let initial_value = id_value[1].to_string();
Variable { id, initial_value }
}
#[test]
fn test_variable_from_string() {
assert_eq!(
variable_from_string("VAR a\n42".to_string()),
Variable { id: "a".to_string(), initial_value: "42".to_string()}
);
}
fn variable_to_string(variable: Variable) -> String {
format!("VAR {}\n{}", variable.id, variable.initial_value)
}
#[test]
fn test_variable_to_string() {
let output = variable_to_string(
Variable { id: "c".to_string(), initial_value: "57".to_string() }
);
let expected = "VAR c\n57".to_string();
assert_eq!(output, expected);
}
fn room_from_string(string: String) -> Room {
// todo handle room_format?
let mut lines: Vec<&str> = string.split("\n").collect();
let id = lines[0].replace("ROOM ", "");
let mut name = None;
let mut palette = "0".to_string();
let mut items: HashMap<Position, String> = HashMap::new();
let mut exits: HashMap<Position, Exit> = HashMap::new();
let mut endings: HashMap<Position, String> = HashMap::new();
loop {
let last_line = lines.pop().unwrap();
if last_line.starts_with("NAME") {
name = Some(last_line.replace("NAME ", "").to_string());
} else if last_line.starts_with("PAL") {
palette = last_line.replace("PAL ", "").to_string();
} else if last_line.starts_with("ITM") {
let last_line = last_line.replace("ITM ", "");
let item_position: Vec<&str> = last_line.split(' ').collect();
let item_id = item_position[0];
let position = item_position[1];
let position = position_from_string(position.to_string());
items.insert(position, item_id.to_string());
} else if last_line.starts_with("EXT") {
let last_line = last_line.replace("EXT ", "");
let parts: Vec<&str> = last_line.split(' ').collect();
let position = position_from_string(parts[0].to_string());
let exit = exit_from_string(format!("{} {}", parts[1], parts[2]));
exits.insert(position, exit);
} else if last_line.starts_with("END") {
let last_line = last_line.replace("END ", "");
let ending_position: Vec<&str> = last_line.split(' ').collect();
let ending= ending_position[0].to_string();
let position = ending_position[1].to_string();
let position = position_from_string(position);
endings.insert(position, ending);
} else {
lines.push(last_line); break;
}
}
let lines= &lines[1..];
let mut tiles : Vec<String> = Vec::new();
for line in lines.into_iter() {
let line: Vec<&str> = line.split(",").collect();
for tile_id in line {
tiles.push(tile_id.to_string());
}
}
Room { id, palette, name, tiles, items, exits, endings }
}
#[test]
fn test_room_from_string() {
let output = room_from_string("ROOM a\n0,0,0,0,0,0,0,0,1l,0,0,0,0,0,0,0\ny,x,0,0,1j,0,0,1j,1l,0,1j,0,0,1j,0,0\ny,y,x,k,k,1c,1x,1y,1m,0,0,0,0,0,0,0\ny,y,y,x,k,s,s,s,k,k,k,k,k,1g,1f,k\nk,z,z,z,1i,1u,1u,1u,1v,11,19,1b,1a,1e,10,k\nk,z,z,11,12,z,z,z,z,10,17,z,18,1e,12,k\nk,z,z,z,z,z,z,z,z,1k,14,15,16,1h,z,k\nk,z,z,z,z,z,z,10,1d,1v,1r,1s,1r,1q,1z,k\nk,z,z,12,10,z,z,z,1i,1n,1o,1o,1o,1p,z,k\nk,z,z,z,z,z,z,z,z,z,z,z,10,z,z,k\nk,z,z,z,z,z,11,z,z,z,z,z,z,z,z,k\nk,z,z,z,z,z,z,z,z,z,12,z,z,10,12,k\nk,k,k,k,k,k,k,k,k,k,k,k,k,k,k,k\n0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\n0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\n0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\nNAME cellar 7\nITM d 11,5\nITM e 8,3\nITM 5 1,0\nITM 6 2,1\nITM 6 3,2\nEXT 3,3 3 10,6\nEND undefined 8,7\nPAL 9".to_string());
// todo can I instantiate these inline?
let mut items = HashMap::new();
let mut exits = HashMap::new();
let mut endings = HashMap::new();
items.insert(Position { x: 11, y: 5}, "d".to_string());
items.insert(Position { x: 8, y: 3}, "e".to_string());
items.insert(Position { x: 1, y: 0}, "5".to_string());
items.insert(Position { x: 2, y: 1}, "6".to_string());
items.insert(Position { x: 3, y: 2}, "6".to_string());
exits.insert(
Position { x: 3, y: 3},
Exit { room: "3".to_string(), position: Position { x: 10, y: 6}}
);
endings.insert(Position { x: 8, y: 7 }, "undefined".to_string());
let expected = Room {
id: "a".to_string(),
palette: "9".to_string(),
name: Some("cellar 7".to_string()),
tiles: vec![
"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"1l".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),
"y".to_string(),"x".to_string(),"0".to_string(),"0".to_string(),"1j".to_string(),"0".to_string(),"0".to_string(),"1j".to_string(),"1l".to_string(),"0".to_string(),"1j".to_string(),"0".to_string(),"0".to_string(),"1j".to_string(),"0".to_string(),"0".to_string(),
"y".to_string(),"y".to_string(),"x".to_string(),"k".to_string(),"k".to_string(),"1c".to_string(),"1x".to_string(),"1y".to_string(),"1m".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),
"y".to_string(),"y".to_string(),"y".to_string(),"x".to_string(),"k".to_string(),"s".to_string(),"s".to_string(),"s".to_string(),"k".to_string(),"k".to_string(),"k".to_string(),"k".to_string(),"k".to_string(),"1g".to_string(),"1f".to_string(),"k".to_string(),
"k".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"1i".to_string(),"1u".to_string(),"1u".to_string(),"1u".to_string(),"1v".to_string(),"11".to_string(),"19".to_string(),"1b".to_string(),"1a".to_string(),"1e".to_string(),"10".to_string(),"k".to_string(),
"k".to_string(),"z".to_string(),"z".to_string(),"11".to_string(),"12".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"10".to_string(),"17".to_string(),"z".to_string(),"18".to_string(),"1e".to_string(),"12".to_string(),"k".to_string(),
"k".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"1k".to_string(),"14".to_string(),"15".to_string(),"16".to_string(),"1h".to_string(),"z".to_string(),"k".to_string(),
"k".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"10".to_string(),"1d".to_string(),"1v".to_string(),"1r".to_string(),"1s".to_string(),"1r".to_string(),"1q".to_string(),"1z".to_string(),"k".to_string(),
"k".to_string(),"z".to_string(),"z".to_string(),"12".to_string(),"10".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"1i".to_string(),"1n".to_string(),"1o".to_string(),"1o".to_string(),"1o".to_string(),"1p".to_string(),"z".to_string(),"k".to_string(),
"k".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"10".to_string(),"z".to_string(),"z".to_string(),"k".to_string(),
"k".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"11".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"k".to_string(),
"k".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"12".to_string(),"z".to_string(),"z".to_string(),"10".to_string(),"12".to_string(),"k".to_string(),
"k".to_string(),"k".to_string(),"k".to_string(),"k".to_string(),"k".to_string(),"k".to_string(),"k".to_string(),"k".to_string(),"k".to_string(),"k".to_string(),"k".to_string(),"k".to_string(),"k".to_string(),"k".to_string(),"k".to_string(),"k".to_string(),
"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),
"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),
"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string()
],
items,
exits,
endings
};
assert_eq!(output, expected);
}
// fn game_from_string(game: String ) -> Game {
// // probably needs to split the game data into different segments starting from the end
// // e.g. VAR... then END... then DLG...
// // then split all these up into their individual items
//
// // no - let's try going from the beginning and popping off elements from the start?
// }
// fn game_to_string(game: Game) -> String {
//
// }
fn main() {
println!("why does a crate need a main function?")
}