battleship game in rust

Add ships parser

Changed files
+171
src
+171
src/parser.rs
···
+
use std::{fs::File, io::Read, path::Path};
+
+
#[derive(Debug)]
+
pub enum ParserError {
+
InvalidShipCount,
+
InvalidCoordCount,
+
InvalidCoord,
+
IO(std::io::Error),
+
}
+
+
impl From<std::io::Error> for ParserError {
+
fn from(value: std::io::Error) -> Self {
+
ParserError::IO(value)
+
}
+
}
+
+
#[derive(Debug, PartialEq, Eq)]
+
pub struct Vector2u16 {
+
x: u16,
+
y: u16,
+
}
+
+
impl Vector2u16 {
+
pub fn new(x: u16, y: u16) -> Self {
+
Vector2u16 {x: x, y: y}
+
}
+
}
+
+
#[derive(Debug, PartialEq, Eq)]
+
pub struct Boat {
+
first_pos: Vector2u16,
+
last_pos: Vector2u16,
+
}
+
+
impl Boat {
+
pub fn new(first_pos: Vector2u16, last_pos: Vector2u16) -> Self {
+
Boat { first_pos: first_pos, last_pos: last_pos }
+
}
+
}
+
+
pub fn parse_file(file_path: &Path) -> Result<[Boat; 4], ParserError> {
+
let mut file = File::open(file_path)?;
+
Ok(parse_ships(&mut file)?)
+
}
+
+
pub fn parse_ships<T: Read>(file: &mut T) -> Result<[Boat; 4], ParserError> {
+
let mut content = String::new();
+
+
file.read_to_string(&mut content)?;
+
let lines: Vec<&str> = content.lines().collect();
+
+
if lines.len() != 4 {
+
return Err(ParserError::InvalidShipCount);
+
}
+
+
let boats = [
+
create_ship(lines[0])?,
+
create_ship(lines[1])?,
+
create_ship(lines[2])?,
+
create_ship(lines[3])?,
+
];
+
+
Ok(boats)
+
}
+
+
fn create_ship(line: &str) -> Result<Boat, ParserError> {
+
let coordinates: Vec<&str> = line.split(":").collect();
+
+
if coordinates.len() != 2 {
+
return Err(ParserError::InvalidCoordCount);
+
}
+
+
+
Ok(Boat::new(create_coord(coordinates[0])?, create_coord(coordinates[1])?))
+
}
+
+
fn create_coord(coord_str: &str) -> Result<Vector2u16, ParserError> {
+
let mut str_iter = coord_str.chars();
+
+
let Some(col_c) = str_iter.next() else {
+
return Err(ParserError::InvalidCoord);
+
};
+
let col = (col_c as u16) - ('A' as u16);
+
+
let Some(row_c) = str_iter.next() else {
+
return Err(ParserError::InvalidCoord);
+
};
+
let row = (row_c as u16) - ('0' as u16) - 1;
+
+
if col > 7 || row > 7 {
+
return Err(ParserError::InvalidCoord);
+
}
+
+
Ok(Vector2u16::new(col, row))
+
+
}
+
+
#[cfg(test)]
+
mod tests {
+
use super::*;
+
+
#[test]
+
fn test_create_coord() -> Result<(), ParserError> {
+
let coord1 = create_coord("D5")?;
+
+
assert_eq!(coord1.x, 3);
+
assert_eq!(coord1.y, 4);
+
+
let coord2 = create_coord("A1")?;
+
+
assert_eq!(coord2.x, 0);
+
assert_eq!(coord2.y, 0);
+
+
let coord3 = create_coord("H8")?;
+
+
assert_eq!(coord3.x, 7);
+
assert_eq!(coord3.y, 7);
+
+
Ok(())
+
}
+
+
#[test]
+
fn test_create_install_coord() {
+
let coord = create_coord("G9");
+
+
assert!(coord.is_err());
+
}
+
+
#[test]
+
fn test_create_bad_coord() {
+
let coord = create_coord("bad");
+
+
assert!(coord.is_err());
+
}
+
+
#[test]
+
fn test_create_ship() -> Result<(), ParserError> {
+
let ship = create_ship("C4:B1")?;
+
+
assert_eq!(ship.first_pos.x, 2);
+
assert_eq!(ship.first_pos.y, 3);
+
assert_eq!(ship.last_pos.x, 1);
+
assert_eq!(ship.last_pos.y, 0);
+
Ok(())
+
}
+
+
#[test]
+
fn test_create_bad_ship() {
+
let ship = create_ship("invalid");
+
assert!(ship.is_err());
+
+
let ship2 = create_ship("C9:B4");
+
assert!(ship2.is_err());
+
}
+
+
#[test]
+
fn test_parse_ships() -> Result<(), ParserError> {
+
let file = b"B4:C2\nD1:F3\nF1:F5\nA1:A2";
+
+
let ships = parse_ships(&mut &file[..])?;
+
+
assert_eq!(ships[0], Boat::new(Vector2u16::new(1, 3), Vector2u16::new(2, 1)));
+
assert_eq!(ships[1], Boat::new(Vector2u16::new(3, 0), Vector2u16::new(5, 2)));
+
assert_eq!(ships[2], Boat::new(Vector2u16::new(5, 0), Vector2u16::new(5, 4)));
+
assert_eq!(ships[3], Boat::new(Vector2u16::new(0, 0), Vector2u16::new(0, 1)));
+
+
Ok(())
+
}
+
+
+
}