/* Copyright 2022-2023, Savanni D'Gerinel This file is part of the Luminescent Dreams Tools. Luminescent Dreams Tools is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Luminescent Dreams Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Luminescent Dreams Tools. If not, see . */ use crate::{hex::AxialAddr, Error}; use nom::{ bytes::complete::tag, character::complete::alphanumeric1, multi::many1, sequence::{delimited, separated_pair}, Finish, IResult, Parser, }; use std::{ collections::{hash_map::Entry, HashMap}, path::PathBuf, }; #[derive(Clone, Debug, PartialEq)] pub struct Map { cells: HashMap, } impl Map { pub fn new_hexagonal(radius: usize) -> Map { let cells = vec![(AxialAddr::origin(), Default::default())] .into_iter() .chain( AxialAddr::origin() .addresses(radius) .map(|addr| (addr, Default::default())), ) .collect::>(); Map { cells } } pub fn contains(&self, coordinate: AxialAddr) -> bool { self.cells.contains_key(&coordinate) } pub fn cell(&mut self, addr: AxialAddr) -> Entry { self.cells.entry(addr) } pub fn get(&self, addr: &AxialAddr) -> Option<&A> { self.cells.get(addr) } } pub fn read_file>(path: PathBuf) -> Result, Error> { let data = std::fs::read_to_string(path)?; Ok(parse_data(data.lines())) } pub fn write_file(path: PathBuf, map: Map) -> Result<(), Error> where String: From, { std::fs::write(path, write_data(map).join("\n")).map_err(Error::from) } pub fn parse_data<'a, A: Default + From>( data: impl Iterator + 'a, ) -> Map { fn parse_line>(input: &str) -> Result<(AxialAddr, A), nom::error::Error<&str>> { fn parse>(input: &str) -> IResult<&str, (AxialAddr, A)> { let (input, addr) = parse_address(input)?; let (input, value) = cell_value::(input)?; Ok((input, (addr, value))) } parse(input).finish().map(|(_, pair)| pair) } let cells = data .map(|line| parse_line::(line).unwrap()) .collect::>(); let cells = cells.into_iter().collect::>(); Map { cells } } fn write_data(map: Map) -> Vec where String: From, { map.cells .iter() .map(|(addr, val)| format!("[{}, {}] {}", addr.q(), addr.r(), String::from(val.clone()))) .collect() } fn parse_address(input: &str) -> IResult<&str, AxialAddr> { let (input, (q, r)) = delimited( tag("["), separated_pair( nom::character::complete::i32, tag(", "), nom::character::complete::i32, ), tag("] "), )(input)?; Ok((input, (AxialAddr::new(q, r)))) } fn cell_value>(input: &str) -> IResult<&str, A> { many1(alphanumeric1) .or(tag(".").map(|v| vec![v])) .map(|v| v.join("")) .map(|v| A::from(v)) .parse(input) } #[cfg(test)] mod test { use std::collections::HashSet; use super::*; #[derive(Clone, Debug, PartialEq)] struct MapVal(char); impl Default for MapVal { fn default() -> MapVal { MapVal('.') } } impl From for String { fn from(v: MapVal) -> String { format!("{}", v.0) } } impl From for MapVal { fn from(s: String) -> MapVal { MapVal(s.chars().next().unwrap()) } } #[test] fn it_can_modify_a_map() { let mut map: Map = Map::new_hexagonal(1); map.cell(AxialAddr::new(0, 0)) .and_modify(|cell| *cell = MapVal('L')); assert_eq!(map.get(&AxialAddr::new(0, 0)), Some(&MapVal('L'))); assert_eq!(map.get(&AxialAddr::new(0, 1)), Some(&MapVal('.'))); } #[test] fn it_can_serialize_a_map() { let mut map: Map = Map::new_hexagonal(1); map.cell(AxialAddr::new(0, 0)) .and_modify(|cell| *cell = MapVal('L')); map.cell(AxialAddr::new(1, 0)) .and_modify(|cell| *cell = MapVal('A')); let mut expected: HashSet = HashSet::new(); expected.insert("[0, 0] L".to_owned()); expected.insert("[1, 0] A".to_owned()); expected.insert("[0, 1] .".to_owned()); expected.insert("[-1, 1] .".to_owned()); expected.insert("[-1, 0] .".to_owned()); expected.insert("[0, -1] .".to_owned()); expected.insert("[1, -1] .".to_owned()); let map_rows = write_data(map); assert_eq!(map_rows.len(), expected.len()); map_rows .iter() .for_each(|row| assert!(expected.contains(row))); expected .iter() .for_each(|expected| assert!(map_rows.contains(expected))); } #[test] fn it_can_deserialize_a_map() { let map_data = "[0, 0] L [1, 0] A [0, 1] . [-1, 1] . [-1, 0] . [0, -1] . [1, -1] ."; let map: Map = parse_data(map_data.lines()); assert_eq!(map.get(&AxialAddr::new(0, 0)), Some(&MapVal('L'))); assert_eq!(map.get(&AxialAddr::new(1, 0)), Some(&MapVal('A'))); assert_eq!(map.get(&AxialAddr::new(0, 1)), Some(&MapVal('.'))); } }