battleship game in rust

Add ships parser

authored by pebloop.dev and committed by

knot.nasso.dev 994c4400 e03a5dc1

+171
+171
src/parser.rs
··· 1 + use std::{fs::File, io::Read, path::Path}; 2 + 3 + #[derive(Debug)] 4 + pub enum ParserError { 5 + InvalidShipCount, 6 + InvalidCoordCount, 7 + InvalidCoord, 8 + IO(std::io::Error), 9 + } 10 + 11 + impl From<std::io::Error> for ParserError { 12 + fn from(value: std::io::Error) -> Self { 13 + ParserError::IO(value) 14 + } 15 + } 16 + 17 + #[derive(Debug, PartialEq, Eq)] 18 + pub struct Vector2u16 { 19 + x: u16, 20 + y: u16, 21 + } 22 + 23 + impl Vector2u16 { 24 + pub fn new(x: u16, y: u16) -> Self { 25 + Vector2u16 {x: x, y: y} 26 + } 27 + } 28 + 29 + #[derive(Debug, PartialEq, Eq)] 30 + pub struct Boat { 31 + first_pos: Vector2u16, 32 + last_pos: Vector2u16, 33 + } 34 + 35 + impl Boat { 36 + pub fn new(first_pos: Vector2u16, last_pos: Vector2u16) -> Self { 37 + Boat { first_pos: first_pos, last_pos: last_pos } 38 + } 39 + } 40 + 41 + pub fn parse_file(file_path: &Path) -> Result<[Boat; 4], ParserError> { 42 + let mut file = File::open(file_path)?; 43 + Ok(parse_ships(&mut file)?) 44 + } 45 + 46 + pub fn parse_ships<T: Read>(file: &mut T) -> Result<[Boat; 4], ParserError> { 47 + let mut content = String::new(); 48 + 49 + file.read_to_string(&mut content)?; 50 + let lines: Vec<&str> = content.lines().collect(); 51 + 52 + if lines.len() != 4 { 53 + return Err(ParserError::InvalidShipCount); 54 + } 55 + 56 + let boats = [ 57 + create_ship(lines[0])?, 58 + create_ship(lines[1])?, 59 + create_ship(lines[2])?, 60 + create_ship(lines[3])?, 61 + ]; 62 + 63 + Ok(boats) 64 + } 65 + 66 + fn create_ship(line: &str) -> Result<Boat, ParserError> { 67 + let coordinates: Vec<&str> = line.split(":").collect(); 68 + 69 + if coordinates.len() != 2 { 70 + return Err(ParserError::InvalidCoordCount); 71 + } 72 + 73 + 74 + Ok(Boat::new(create_coord(coordinates[0])?, create_coord(coordinates[1])?)) 75 + } 76 + 77 + fn create_coord(coord_str: &str) -> Result<Vector2u16, ParserError> { 78 + let mut str_iter = coord_str.chars(); 79 + 80 + let Some(col_c) = str_iter.next() else { 81 + return Err(ParserError::InvalidCoord); 82 + }; 83 + let col = (col_c as u16) - ('A' as u16); 84 + 85 + let Some(row_c) = str_iter.next() else { 86 + return Err(ParserError::InvalidCoord); 87 + }; 88 + let row = (row_c as u16) - ('0' as u16) - 1; 89 + 90 + if col > 7 || row > 7 { 91 + return Err(ParserError::InvalidCoord); 92 + } 93 + 94 + Ok(Vector2u16::new(col, row)) 95 + 96 + } 97 + 98 + #[cfg(test)] 99 + mod tests { 100 + use super::*; 101 + 102 + #[test] 103 + fn test_create_coord() -> Result<(), ParserError> { 104 + let coord1 = create_coord("D5")?; 105 + 106 + assert_eq!(coord1.x, 3); 107 + assert_eq!(coord1.y, 4); 108 + 109 + let coord2 = create_coord("A1")?; 110 + 111 + assert_eq!(coord2.x, 0); 112 + assert_eq!(coord2.y, 0); 113 + 114 + let coord3 = create_coord("H8")?; 115 + 116 + assert_eq!(coord3.x, 7); 117 + assert_eq!(coord3.y, 7); 118 + 119 + Ok(()) 120 + } 121 + 122 + #[test] 123 + fn test_create_install_coord() { 124 + let coord = create_coord("G9"); 125 + 126 + assert!(coord.is_err()); 127 + } 128 + 129 + #[test] 130 + fn test_create_bad_coord() { 131 + let coord = create_coord("bad"); 132 + 133 + assert!(coord.is_err()); 134 + } 135 + 136 + #[test] 137 + fn test_create_ship() -> Result<(), ParserError> { 138 + let ship = create_ship("C4:B1")?; 139 + 140 + assert_eq!(ship.first_pos.x, 2); 141 + assert_eq!(ship.first_pos.y, 3); 142 + assert_eq!(ship.last_pos.x, 1); 143 + assert_eq!(ship.last_pos.y, 0); 144 + Ok(()) 145 + } 146 + 147 + #[test] 148 + fn test_create_bad_ship() { 149 + let ship = create_ship("invalid"); 150 + assert!(ship.is_err()); 151 + 152 + let ship2 = create_ship("C9:B4"); 153 + assert!(ship2.is_err()); 154 + } 155 + 156 + #[test] 157 + fn test_parse_ships() -> Result<(), ParserError> { 158 + let file = b"B4:C2\nD1:F3\nF1:F5\nA1:A2"; 159 + 160 + let ships = parse_ships(&mut &file[..])?; 161 + 162 + assert_eq!(ships[0], Boat::new(Vector2u16::new(1, 3), Vector2u16::new(2, 1))); 163 + assert_eq!(ships[1], Boat::new(Vector2u16::new(3, 0), Vector2u16::new(5, 2))); 164 + assert_eq!(ships[2], Boat::new(Vector2u16::new(5, 0), Vector2u16::new(5, 4))); 165 + assert_eq!(ships[3], Boat::new(Vector2u16::new(0, 0), Vector2u16::new(0, 1))); 166 + 167 + Ok(()) 168 + } 169 + 170 + 171 + }