From 3f5492b2bb67326be43cd7c5ba02ccf0ba1ae0e3 Mon Sep 17 00:00:00 2001 From: Justin Wernick Date: Tue, 19 Apr 2022 21:27:56 +0200 Subject: Refile for merging repos --- 2019-worms/tests/official-runner-matching.rs | 238 +++++++++++++++++++++++++++ 1 file changed, 238 insertions(+) create mode 100644 2019-worms/tests/official-runner-matching.rs (limited to '2019-worms/tests/official-runner-matching.rs') diff --git a/2019-worms/tests/official-runner-matching.rs b/2019-worms/tests/official-runner-matching.rs new file mode 100644 index 0000000..1b62088 --- /dev/null +++ b/2019-worms/tests/official-runner-matching.rs @@ -0,0 +1,238 @@ +use steam_powered_wyrm::command::{Action, Command}; +use steam_powered_wyrm::constants::*; +use steam_powered_wyrm::game::*; +use steam_powered_wyrm::geometry::*; +use steam_powered_wyrm::json; + +use std::fs::File; +use std::io::prelude::*; +use std::path::Path; + +#[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)) + ); + + if round != 0 { + let player_move = read_move( + &player, + game_board.players[0].worms[game_board.players[0].active_worm].id, + ); + let opponent_move = read_move( + &opponent, + game_board.players[1].worms[game_board.players[1].active_worm].id, + ); + let _ = game_board.simulate([player_move, opponent_move]); + if player[1] == "invalid" { + game_board.players[0].moves_score -= INVALID_COMMAND_SCORE_PENALTY; + } + if opponent[1] == "invalid" { + game_board.players[1].moves_score -= INVALID_COMMAND_SCORE_PENALTY; + } + } + + 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], expected_worm_id: i32) -> Command { + let worm_id = csv_line[3].parse::().unwrap(); + let select = if worm_id == expected_worm_id { + None + } else { + Some(worm_id) + }; + match csv_line[1].as_ref() { + "move" => { + let (x, y) = read_xy_pair(&csv_line[2]); + Command { + worm: select, + action: Action::Move(Point2d::new(x, y)), + } + } + "dig" => { + let (x, y) = read_xy_pair(&csv_line[2]); + Command { + worm: select, + action: Action::Dig(Point2d::new(x, y)), + } + } + "banana" => { + let (x, y) = read_xy_pair(&csv_line[2]); + Command { + worm: select, + action: Action::Bomb(Point2d::new(x, y)), + } + } + "snowball" => { + let (x, y) = read_xy_pair(&csv_line[2]); + Command { + worm: select, + action: Action::Snowball(Point2d::new(x, y)), + } + } + "nothing" | "invalid" => Command { + worm: select, + action: 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 { + worm: select, + action: Action::Shoot(dir), + } + } + 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) +} -- cgit v1.2.3