use steam_powered_wyrm::json; use steam_powered_wyrm::game::*; use steam_powered_wyrm::command::{Command, Action}; use steam_powered_wyrm::geometry::*; use std::path::Path; use std::fs::File; use std::io::prelude::*; #[test] fn simulates_the_same_match() { let replays = Path::new("tests/replays/"); for replay in replays.read_dir().expect("read_dir failed") { let replay = replay.expect("error on replay").path(); let mut game_board = GameBoard::new( json::read_state_from_json_file(&replay.join(Path::new("A-init.json"))).expect("Failed to read initial state") ); let player_csv = read_file_lines(&replay.join(Path::new("A-log.csv")), 1); let opponent_csv = read_file_lines(&replay.join(Path::new("B-log.csv")), 1); for round in 0..player_csv.len() { println!("Testing round {}", round); let player = split_csv(&player_csv[round]); let opponent = split_csv(&opponent_csv[round]); assert_eq!(round + 1, player[0].parse::().expect(&format!("Invalid player input on round {}", round))); assert_eq!(round + 1, opponent[0].parse::().expect(&format!("Invalid opponent input on round {}", round))); // active worm id field in CSV refers to the worm of the // move. The rest of the state refers to after the move // has happened. assert_eq!(player[3].parse::().unwrap(), game_board.players[0].worms[game_board.players[0].active_worm].id, "Active worm is incorrect for player 0"); assert_eq!(opponent[3].parse::().unwrap(), game_board.players[1].worms[game_board.players[1].active_worm].id, "Active worm is incorrect for player 1"); if round != 0 { let player_move = read_move(&player); let opponent_move = read_move(&opponent); let _ = game_board.simulate([player_move, opponent_move]); } for player_index in 0..2 { let csv_row = match player_index { 0 => &player, _ => &opponent }; assert_eq!(csv_row[4].parse::().unwrap(), game_board.players[player_index].score(), "Score is incorrect for player {}, Row: {:?}", player_index, csv_row); for worm_index in 0..3 { let worm_id = worm_index as i32 + 1; match game_board.players[player_index].find_worm(worm_id) { Some(worm) => { assert_eq!(csv_row[6 + worm_index * 3].parse::().unwrap(), worm.health, "Worm health is incorrect for worm {} on player {}, Row: {:?}", worm_id, player_index, csv_row); assert_eq!(csv_row[7 + worm_index * 3].parse::().unwrap(), worm.position.x, "Worm x is incorrect for worm {} on player {}, Row: {:?}", worm_id, player_index, csv_row); assert_eq!(csv_row[8 + worm_index * 3].parse::().unwrap(), worm.position.y, "Worm y is incorrect for worm {} on player {}, Row: {:?}", worm_id, player_index, csv_row); }, None => { // If the worms don't appear in my state, they should be dead assert!(csv_row[6 + worm_index * 3].parse::().unwrap() <= 0, "Worm is not actually dead"); } } } } } } } fn read_file_lines(path: &Path, skip: usize) -> Vec { let mut file = File::open(path).unwrap(); let mut contents = String::new(); file.read_to_string(&mut contents).unwrap(); contents.split("\n").skip(skip).map(String::from).filter(|s| !s.is_empty()).collect() } fn split_csv(input: &str) -> Vec { let mut result = Vec::new(); let mut next = Vec::new(); let mut quoted = false; for c in input.chars() { match c { '"' => { quoted = !quoted; }, ',' if !quoted => { result.push(next.iter().collect()); next = Vec::new(); }, c => { next.push(c); } } } result.push(next.iter().collect()); result } fn read_move(csv_line: &[String]) -> Command { match csv_line[1].as_ref() { "move" => { let (x, y) = read_xy_pair(&csv_line[2]); Command::new(Action::Move(Point2d::new(x, y))) }, "dig" => { let (x, y) = read_xy_pair(&csv_line[2]); Command::new(Action::Dig(Point2d::new(x, y))) }, "nothing" => { Command::new(Action::DoNothing) }, "shoot" => { use steam_powered_wyrm::geometry::Direction::*; let dir = match csv_line[2].as_ref() { "shoot N" => North, "shoot NE" => NorthEast, "shoot E" => East, "shoot SE" => SouthEast, "shoot S" => South, "shoot SW" => SouthWest, "shoot W" => West, "shoot NW" => NorthWest, _ => panic!("Unknown shoot direction: {}", csv_line[2]) }; Command::new(Action::Shoot(dir)) }, // TODO: Parsing of new commands x => { panic!("Unknown command {}", x); } } } fn read_xy_pair(input: &str) -> (i8, i8) { let mut char_iter = input.chars(); let _ = char_iter.by_ref().take_while(|c| *c != '(').collect::(); let x = char_iter.by_ref().take_while(|c| *c != ',').collect::().trim().parse::().unwrap(); let y = char_iter.by_ref().take_while(|c| *c != ')').collect::().trim().parse::().unwrap(); (x, y) }