From 78327e8658366e000509f536ca8c0501775db136 Mon Sep 17 00:00:00 2001 From: Sebastian Tobie Date: Wed, 11 Dec 2024 00:59:44 +0100 Subject: [PATCH] solved day 4 and 10 --- examples/2024/04.txt | 27 +- examples/2024/10.txt | 8 + src/bin/2024/04.rs | 276 ++++++------------ src/bin/2024/10.rs | 119 ++++++++ src/coordinate_systems/kartesian.rs | 419 ++++++++++++++++++++++++---- 5 files changed, 592 insertions(+), 257 deletions(-) create mode 100644 examples/2024/10.txt create mode 100644 src/bin/2024/10.rs diff --git a/examples/2024/04.txt b/examples/2024/04.txt index 1bb64a4..c41c5ea 100644 --- a/examples/2024/04.txt +++ b/examples/2024/04.txt @@ -1,17 +1,10 @@ -S.MX -.A.M -S.MA -M.MS -.A.. -S.S. -S.S. -.A.. -M.M. -M.SS -.A.A -M.SM -SAMX -.... -M... -AA.. -S.S. +MMMSXXMASM +MSAMXMSMSA +AMXSXMAAMM +MSAMASMSMX +XMASAMXAMM +XXAMMXXAMA +SMSMSASXSS +SAXAMASAAA +MAMMMXMMMM +MXMXAXMASX \ No newline at end of file diff --git a/examples/2024/10.txt b/examples/2024/10.txt new file mode 100644 index 0000000..cada9b3 --- /dev/null +++ b/examples/2024/10.txt @@ -0,0 +1,8 @@ +89010123 +78121874 +87430965 +96549874 +45678903 +32019012 +01329801 +10456732 diff --git a/src/bin/2024/04.rs b/src/bin/2024/04.rs index 37d6dcc..99b708c 100644 --- a/src/bin/2024/04.rs +++ b/src/bin/2024/04.rs @@ -1,220 +1,122 @@ -use std::ops::Neg; +#![allow(unused_variables, unused_assignments)] +#![allow(unused_imports)] +use std::ops::Add; -#[allow(unused_variables)] -#[allow(unused_imports)] use advent_of_code::{include_data, include_example}; use advent_of_code::{ - matrix, strings::{convert_to_array, line_to_char}, + Kartesian, KartesianDirection, MaximumFromMap, Table, }; -include_example!(DATA 2024 04); +include_data!(DATA 2024 04); #[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)] struct TextPos { - startx: usize, - starty: usize, - endx: usize, - endy: usize, - length: usize, -} - -#[inline] -fn dist(a: (usize, usize), b: (usize, usize)) -> (i32, i32) { - (a.0 as i32 - b.0 as i32, a.1 as i32 - b.1 as i32) -} - -#[inline] -fn dist_abs(a: (usize, usize), b: (usize, usize)) -> (usize, usize) { - let t = dist(a, b); - (t.0.abs() as usize, t.1.abs() as usize) + start: Kartesian, + end: Kartesian, } impl TextPos { - const fn center(self) -> (usize, usize) { - ( - self.startx.saturating_add(self.endx).div_ceil(2), - self.starty.saturating_add(self.endy).div_ceil(2), - ) - } - - fn crossing(self, other: &&Self) -> bool { - if self.length != other.length && self.length != 3 { - todo!() - } - if self.center() != other.center() { - return false; - } - true + fn center(self) -> Kartesian { + (self.start + self.end) / 2 } } -#[test] -fn test_crossing() { - let cases = [ - ( - TextPos { - startx: 0, - starty: 0, - endx: 2, - endy: 2, - length: 3, - }, - TextPos { - startx: 0, - starty: 0, - endx: 2, - endy: 2, - length: 3, - }, - true, - ), - ( - TextPos { - startx: 1, - starty: 0, - endx: 1, - endy: 2, - length: 3, - }, - TextPos { - startx: 0, - starty: 1, - endx: 2, - endy: 1, - length: 3, - }, - true, - ), - ( - TextPos { - startx: 0, - starty: 0, - endx: 2, - endy: 0, - length: 3, - }, - TextPos { - startx: 1, - starty: 0, - endx: 2, - endy: 0, - length: 3, - }, - false, - ), - ]; - for case in cases { - assert_eq!(case.0.crossing(&&case.1), case.2) - } -} - -fn valid(x: usize, y: i32, max: usize) -> Option { - let tmp = if y < 0 { - x.checked_sub(y.neg() as usize) - } else { - x.checked_add(y as usize) - }; - if let Some(xi) = tmp { - if xi < max { - return Some(xi); +fn get_x(list: Vec) -> usize { + let mut found = Vec::<(TextPos, TextPos)>::new(); + let (mut higher, mut lower); + for outer in list.clone() { + for inner in list.clone() { + if outer == inner { + continue; + } + higher = outer.max(inner); + lower = outer.min(inner); + if lower.center() == higher.center() { + let diff = lower.start.diff(higher.start).0; + if diff.x == 0 && diff.y == 2 || diff.y == 0 && diff.x == 2 { + let tuple = (lower, higher); + if !found.contains(&tuple) { + found.push((lower, higher)); + } + } + } } } - None + found.len() } -fn validxy( - x: usize, - y: usize, - modx: i32, - mody: i32, - heigth: usize, - length: usize, -) -> Option<(usize, usize)> { - if let Some(xi) = valid(x, modx, length) { - if let Some(yi) = valid(y, mody, heigth) { - return Some((xi, yi)); - } +fn follow_text(data: &Table, start_point: Kartesian, direction: KartesianDirection, current_pos: Kartesian, chars: Vec, list: &mut Vec) { + let mut iter = chars.iter().cloned(); + match iter.next() { + None => list.push(TextPos { start: start_point, end: current_pos }), + Some(c) => match current_pos.move_dir_max(direction.vector_abs(), direction, Kartesian::maximum(data)) { + None => return, + Some(new) => { + if data[new.x][new.y] != c { + return; + } + follow_text(data, start_point, direction, new, iter.collect(), list) + }, + }, } - None } -fn find_text(data: Vec>, length: usize, heigth: usize, text: &str) -> (usize, usize) { - let first: char = text.chars().nth(0).unwrap(); - let mut amount = 0; - let mut found; - let mut character; - let mut chars; +fn find_text(data: Table, text: &str) -> Vec { + let length = data[0].len(); + let height = data.len(); + let mut iter = text.chars(); + let first = iter.next().unwrap(); + let chars: Vec = iter.collect(); let mut coords = Vec::new(); - let mut currentpos; - for row in 0..heigth { + let mut start_point; + for row in 0..height { for column in 0..length { - let char = data[row][column]; - if char != first { + start_point = Kartesian::new(row, column); + if data[start_point.x][start_point.y] != first { continue; } - - for (x, y) in matrix(false, true, true) { - if x == y && y == 0 { - continue; - } - found = 1; - chars = text.chars(); - chars.next(); - loop { - character = chars.next(); - if character == None { - amount += 1; - currentpos = TextPos { - startx: row, - starty: column, - endx: (row as i32 + x * found) as usize, - endy: (column as i32 + y * found) as usize, - length: text.len(), - }; - coords.push(currentpos); - break; - } - if let Some((posmodx, posmody)) = - validxy(row, column, x * found, y * found, length, heigth) - { - if data[posmodx][posmody] == character.unwrap() { - found += 1; - } else { - break; - } - } else { - break; - } - } + for dir in KartesianDirection::iter(false, true, true) { + follow_text(&data, start_point, dir, start_point, chars.clone(), &mut coords); } } } - let mut xtexts = Vec::new(); - for node in coords.clone() { - for o in coords.iter().filter(|other| node.crossing(other)) { - let other = *o; - if node == other { - continue; - } - let x = (node.min(other), node.max(other)); - if !xtexts.contains(&x) { - xtexts.push(x); - } - } - } - println!("{:?}", xtexts); - (amount, xtexts.len()) + coords } fn main() { - let table = convert_to_array(DATA, line_to_char); - let length = table[0].len(); - let (amount, _) = find_text(table.clone(), length, table.len(), "XMAS"); - println!("There are {} XMAS in text", amount); - let (amount, amount_cross) = find_text(table.clone(), length, table.len(), "MAS"); - println!( - "There are {} MAS in text, are {} crossed", - amount, amount_cross - ); + let mut coords; + let table = convert_to_array::<_, _, '\n'>(DATA, line_to_char); + coords = find_text(table.clone(), "XMAS"); + println!("There are {} XMAS in text", coords.len()); + coords = find_text(table.clone(), "MAS"); + let amount_cross = get_x(coords.clone()); + println!("There are {} MAS in text, are {} crossed", coords.len(), amount_cross); +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_crossing() { + let cases = [ + ( + TextPos { + start: Kartesian::new(0, 1), + end: Kartesian::new(2, 1), + length: 3, + }, + TextPos { + start: Kartesian::new(1, 0), + end: Kartesian::new(1, 2), + length: 3, + }, + true, + ), + ]; + for case in cases { + assert_eq!(case.0.crossing(&&case.1), case.2) + } + } } diff --git a/src/bin/2024/10.rs b/src/bin/2024/10.rs new file mode 100644 index 0000000..09695c2 --- /dev/null +++ b/src/bin/2024/10.rs @@ -0,0 +1,119 @@ +use advent_of_code::{ + strings::{char_to_num, convert_to_array}, + Kartesian, KartesianDirection, MaximumFromMap, +}; +#[allow(unused_imports)] +use advent_of_code_macros::{include_data, include_example}; + +include_data!(DATA 2024 10); + +type Trail = Vec>; +type Map = Vec>; + +fn get_height(line: &str) -> Vec { + let mut numbers = Vec::new(); + for c in line.chars() { + numbers.push(match c { + '.' => 11, + c => char_to_num(c), + }); + } + numbers +} + +fn follow_trail(position: Kartesian, number: u32, current: Trail, trails: &mut Vec, map: &Map) { + let max = Kartesian::maximum(map); + let mut new; + #[cfg(debug_assertions)] + if map[position.x as usize][position.y as usize] != number { + panic!("Positition is not correct") + } + let mut newposition; + let mut found = false; + for dir in KartesianDirection::iter(false, false, true) { + newposition = match position.move_dir(dir.vector_abs(), dir) { + None => continue, + Some(pos) => { + if pos.x >= max.x || pos.y >= max.y { + continue; + } + pos + }, + }; + if map[newposition.x as usize][newposition.y as usize] == number + 1 { + found = true; + new = current.clone(); + new.push(newposition); + follow_trail(newposition, number + 1, new, trails, map); + } + } + if !found { + trails.push(current); + } +} + +fn find_trails(map: &Map) -> Vec> { + let mut trails = Vec::new(); + for (x, line) in map.iter().enumerate() { + for (y, column) in line.iter().cloned().enumerate() { + if column == 0 { + let trailhead = Kartesian::new(x as u32, y as u32); + let mut traillist = Vec::::new(); + follow_trail(trailhead, 0, vec![trailhead], &mut traillist, map); + trails.push(traillist); + } + } + } + trails +} + +fn score_trail(trails: &Vec) -> u32 { + let mut highest_points = Vec::new(); + let mut last; + for trail in trails { + last = trail.last().unwrap().clone(); + if trail.len() == 10 && !highest_points.contains(&last) { + highest_points.push(last); + } + } + highest_points.len() as u32 +} + +fn main() { + let map = convert_to_array::<_, _, '\n'>(DATA, get_height); + let trails = find_trails(&map); + let mut score = 0; + let mut rating = 0; + for traillist in trails { + score += score_trail(&traillist); + rating += traillist.iter().filter(|x| x.len() == 10).count(); + } + println!("Total Score of Map: {}", score); + println!("Total Rating of Map: {}", rating); +} + +#[cfg(test)] +mod test { + use super::*; + + include_example!(MAP 2024 10); + const SCORE: u32 = 36; + + #[test] + fn test_simple() { + let mut score = 0; + for trails in find_trails(&convert_to_array::<_, _, '\n'>(MAP, get_height)).values().cloned() { + for trail in trails.clone() { + for (i, point) in trail.iter().enumerate() { + print!("{}", point); + if i != trail.len() - 1 { + print!(" -> ") + } + } + println!(); + } + score += score_trail(trails); + } + assert!(score == SCORE, "Score of the map did not match the expected value: {} == {}\n{}", score, SCORE, MAP); + } +} diff --git a/src/coordinate_systems/kartesian.rs b/src/coordinate_systems/kartesian.rs index 5323145..2d797e2 100644 --- a/src/coordinate_systems/kartesian.rs +++ b/src/coordinate_systems/kartesian.rs @@ -1,7 +1,11 @@ use num::*; -use std::ops::*; +use std::{fmt::Display, ops::*}; -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)] +pub trait MaximumFromMap { + fn maximum(map: &Vec>) -> Kartesian; +} + +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Kartesian where T: Integer, @@ -12,10 +16,7 @@ where impl Add for Kartesian { fn add(self, rhs: Self) -> Self::Output { - Kartesian { - x: self.x + rhs.x, - y: self.y + rhs.y, - } + Kartesian { x: self.x + rhs.x, y: self.y + rhs.y } } type Output = Kartesian; @@ -30,15 +31,45 @@ impl AddAssign for Kartesian { impl Sub for Kartesian { fn sub(self, rhs: Self) -> Self::Output { - Kartesian { - x: self.x - rhs.x, - y: self.y - rhs.y, - } + Kartesian { x: self.x - rhs.x, y: self.y - rhs.y } } type Output = Kartesian; } +impl MaximumFromMap for Kartesian { + fn maximum(map: &Vec>) -> Kartesian { + Kartesian { + x: map.len().to_u32().unwrap(), + y: map[0].len().to_u32().unwrap(), + } + } +} + +impl MaximumFromMap for Kartesian { + fn maximum(map: &Vec>) -> Kartesian { + Kartesian { + x: map.len().to_u64().unwrap(), + y: map[0].len().to_u64().unwrap(), + } + } +} + +impl MaximumFromMap for Kartesian { + fn maximum(map: &Vec>) -> Kartesian { + Kartesian { + x: map.len().to_u128().unwrap(), + y: map[0].len().to_u128().unwrap(), + } + } +} + +impl MaximumFromMap for Kartesian { + fn maximum(map: &Vec>) -> Kartesian { + Kartesian { x: map.len(), y: map[0].len() } + } +} + impl SubAssign for Kartesian { fn sub_assign(&mut self, rhs: Self) { self.x -= rhs.x; @@ -52,7 +83,7 @@ impl Kartesian { } } -impl Kartesian { +impl Kartesian { pub fn checked_add_max(self, rhs: Kartesian, max: Kartesian) -> Option> { let mut new = Kartesian::default(); new.x = match self.x.checked_add(&rhs.x) { @@ -62,7 +93,7 @@ impl Kartesian { return None; } x - } + }, }; new.y = match self.y.checked_add(&rhs.y) { None => return None, @@ -71,10 +102,120 @@ impl Kartesian { return None; } y - } + }, }; Some(new) } + pub fn checked_sub(self, rhs: Kartesian) -> Option> { + let mut new = Kartesian::default(); + new.x = match self.x.checked_sub(&rhs.x) { + None => return None, + Some(x) => x, + }; + new.y = match self.y.checked_sub(&rhs.y) { + None => return None, + Some(y) => y, + }; + Some(new) + } + + pub fn diff(self, rhs: Kartesian) -> (Kartesian, KartesianDirection) { + let mut k = Kartesian::::default(); + let mut dir = KartesianDirection::None; + k.x = match (self.x.checked_sub(&rhs.x), rhs.x.checked_sub(&self.x)) { + (Some(d), _) => { + dir = dir.up(); + d + }, + (_, Some(d)) => { + dir = dir.down(); + d + }, + (None, None) => { + unreachable!() + }, + }; + k.y = match (self.y.checked_sub(&rhs.y), rhs.y.checked_sub(&self.y)) { + (Some(d), _) => { + dir = dir.left(); + d + }, + (_, Some(d)) => { + dir = dir.right(); + d + }, + (None, None) => { + unreachable!() + }, + }; + (k, dir) + } + + #[inline] + pub fn move_dir(self, vector: Kartesian, dir: KartesianDirection) -> Option> { + let mut new = self; + match dir { + KartesianDirection::TopLeft | KartesianDirection::Top | KartesianDirection::TopRight => { + new.x = match new.x.checked_sub(&vector.x) { + None => return None, + Some(d) => d, + } + }, + KartesianDirection::BottomLeft | KartesianDirection::Bottom | KartesianDirection::BottomRight => { + new.x = match new.x.checked_add(&vector.x) { + None => return None, + Some(d) => d, + } + }, + _ => {}, + } + match dir { + KartesianDirection::TopLeft | KartesianDirection::Left | KartesianDirection::BottomLeft => { + new.y = match new.y.checked_sub(&vector.y) { + None => return None, + Some(d) => d, + } + }, + KartesianDirection::TopRight | KartesianDirection::Right | KartesianDirection::BottomRight => { + new.y = match new.y.checked_add(&vector.y) { + None => return None, + Some(d) => d, + } + }, + _ => {}, + } + Some(new) + } + + #[inline] + pub fn move_dir_max(self, vector: Kartesian, dir: KartesianDirection, max: Kartesian) -> Option> { + match self.move_dir(vector, dir) { + None => None, + Some(new) => { + if new.x < max.x && new.y < max.y { + Some(new) + } else { + None + } + }, + } + } +} + +impl + Copy> Display for Kartesian { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!("{}:{}", self.x + T::from(1), self.y + T::from(1))) + } +} + +impl Div for Kartesian { + type Output = Kartesian; + fn div(self, rhs: T) -> Self::Output { + Kartesian { + x: self.x.div_ceil(&rhs), + y: self.y.div_ceil(&rhs), + } + } } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] @@ -91,59 +232,200 @@ pub enum KartesianDirection { } impl KartesianDirection { - pub fn vector>(self) -> Kartesian { - const BELOW: i8 = -1; - const ON: i8 = 0; - const UPPER: i8 = 1; - Kartesian:: { + pub fn vector(self) -> Kartesian { + Kartesian { x: match self { - Self::TopLeft | Self::Top | Self::TopRight => BELOW.into(), - Self::Left | Self::None | Self::Right => ON.into(), - Self::BottomLeft | Self::Bottom | Self::BottomRight => UPPER.into(), + Self::TopLeft | Self::Top | Self::TopRight => -1, + Self::Left | Self::None | Self::Right => 0, + Self::BottomLeft | Self::Bottom | Self::BottomRight => 1, }, y: match self { - Self::TopLeft | Self::Left | Self::BottomLeft => BELOW.into(), - Self::Top | Self::None | Self::Bottom => ON.into(), - Self::TopRight | Self::Right | Self::BottomRight => UPPER.into(), + Self::TopLeft | Self::Left | Self::BottomLeft => -1, + Self::Top | Self::None | Self::Bottom => 0, + Self::TopRight | Self::Right | Self::BottomRight => 1, }, } } - pub fn clockwise(self, diagonal: bool) -> KartesianDirection { - match (self, diagonal) { - (KartesianDirection::TopLeft, _) => Self::Top, - (KartesianDirection::Top, true) => Self::TopRight, - (KartesianDirection::Top, false) => Self::Right, - (KartesianDirection::TopRight, _) => Self::Right, - (KartesianDirection::Left, true) => Self::TopLeft, - (KartesianDirection::Left, false) => Self::Top, - (KartesianDirection::None, _) => Self::None, - (KartesianDirection::Right, true) => Self::BottomRight, - (KartesianDirection::Right, false) => Self::Bottom, - (KartesianDirection::BottomLeft, _) => Self::Left, - (KartesianDirection::Bottom, true) => Self::BottomLeft, - (KartesianDirection::Bottom, false) => Self::Left, - (KartesianDirection::BottomRight, _) => Self::Bottom, + pub fn vector_abs>(self) -> Kartesian { + Kartesian { + x: match self { + Self::Left | Self::None | Self::Right => From::from(0), + _ => From::from(1), + }, + y: match self { + Self::Top | Self::None | Self::Bottom => From::from(0), + _ => From::from(1), + }, } } - pub fn anticlockwise(self, diagonal: bool) -> KartesianDirection { + pub fn clockwise(self, diagonal: bool) -> Self { match (self, diagonal) { - (KartesianDirection::TopLeft, _) => Self::Left, - (KartesianDirection::Top, true) => Self::TopLeft, - (KartesianDirection::Top, false) => Self::Left, - (KartesianDirection::TopRight, _) => Self::Top, - (KartesianDirection::Left, true) => Self::BottomLeft, - (KartesianDirection::Left, false) => Self::Bottom, - (KartesianDirection::None, _) => Self::None, - (KartesianDirection::Right, true) => Self::TopRight, - (KartesianDirection::Right, false) => Self::Top, - (KartesianDirection::BottomLeft, _) => Self::Bottom, - (KartesianDirection::Bottom, true) => Self::BottomRight, - (KartesianDirection::Bottom, false) => Self::Right, - (KartesianDirection::BottomRight, _) => Self::Right, + (Self::TopLeft, _) => Self::Top, + (Self::Top, true) => Self::TopRight, + (Self::Top, false) => Self::Right, + (Self::TopRight, _) => Self::Right, + (Self::Right, true) => Self::BottomRight, + (Self::Right, false) => Self::Bottom, + (Self::BottomRight, _) => Self::Bottom, + (Self::Bottom, true) => Self::BottomLeft, + (Self::Bottom, false) => Self::Left, + (Self::BottomLeft, _) => Self::Left, + (Self::Left, true) => Self::TopLeft, + (Self::Left, false) => Self::Top, + (Self::None, _) => Self::None, } } + + pub fn anticlockwise(self, diagonal: bool) -> Self { + match (self, diagonal) { + (Self::TopLeft, _) => Self::Left, + (Self::Left, true) => Self::BottomLeft, + (Self::Left, false) => Self::Bottom, + (Self::BottomLeft, _) => Self::Bottom, + (Self::Bottom, true) => Self::BottomRight, + (Self::Bottom, false) => Self::Right, + (Self::BottomRight, _) => Self::Right, + (Self::Right, true) => Self::TopRight, + (Self::Right, false) => Self::Top, + (Self::TopRight, _) => Self::Top, + (Self::Top, true) => Self::TopLeft, + (Self::Top, false) => Self::Left, + (Self::None, _) => Self::None, + } + } + + pub fn up(self) -> Self { + match self { + Self::Top | Self::TopLeft | Self::TopRight => self, + Self::Left => Self::TopLeft, + Self::None => Self::Top, + Self::Right => Self::TopRight, + Self::BottomLeft => Self::Left, + Self::Bottom => Self::None, + Self::BottomRight => Self::Right, + } + } + pub fn down(self) -> Self { + match self { + Self::Bottom | Self::BottomLeft | Self::BottomRight => self, + Self::TopLeft => Self::Left, + Self::Top => Self::None, + Self::TopRight => Self::Right, + Self::Left => Self::BottomLeft, + Self::None => Self::Bottom, + Self::Right => Self::BottomRight, + } + } + pub fn left(self) -> Self { + match self { + Self::TopLeft | Self::Left | Self::BottomLeft => self, + Self::Top => Self::TopLeft, + Self::None => Self::None, + Self::Bottom => Self::BottomLeft, + Self::TopRight => Self::Top, + Self::Right => Self::None, + Self::BottomRight => Self::Bottom, + } + } + pub fn right(self) -> Self { + match self { + Self::TopRight | Self::Right | Self::BottomRight => self, + Self::Top => Self::TopRight, + Self::None => Self::Right, + Self::Bottom => Self::BottomRight, + Self::TopLeft => Self::Top, + Self::Left => Self::None, + Self::BottomLeft => Self::Bottom, + } + } + pub fn neg(self) -> Self { + match self { + Self::TopLeft => Self::BottomRight, + Self::Top => Self::Bottom, + Self::TopRight => Self::BottomLeft, + Self::Left => Self::Right, + Self::None => Self::None, + Self::Right => Self::Left, + Self::BottomLeft => Self::TopRight, + Self::Bottom => Self::Top, + Self::BottomRight => Self::TopLeft, + } + } + + pub fn iter(none: bool, diagonal: bool, nondiagonal: bool) -> KartesianIterator { + KartesianIterator { + i: 0, + none: none, + diagonal: diagonal, + nondiagonal: nondiagonal, + } + } +} + +impl Display for KartesianDirection { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let _ = match *self { + Self::Top | Self::TopLeft | Self::TopRight => f.write_str("top"), + Self::Bottom | Self::BottomLeft | Self::BottomRight => f.write_str("bottom"), + Self::None => return f.write_str("none"), + _ => std::fmt::Result::Ok(()), + }; + let _ = match *self { + Self::TopLeft | Self::Left | Self::BottomLeft => f.write_str("left"), + Self::TopRight | Self::Right | Self::BottomRight => f.write_str("right"), + _ => std::fmt::Result::Ok(()), + }; + std::fmt::Result::Ok(()) + } +} + +pub struct KartesianIterator { + i: u8, + diagonal: bool, + none: bool, + nondiagonal: bool, +} + +impl Iterator for KartesianIterator { + type Item = KartesianDirection; + + fn next(&mut self) -> Option { + let mut i = self.i; + if self.nondiagonal { + if i < 4 { + self.i += 1; + } + match i { + 0 => return Some(KartesianDirection::Right), + 1 => return Some(KartesianDirection::Top), + 2 => return Some(KartesianDirection::Left), + 3 => return Some(KartesianDirection::Bottom), + _ => i -= 4, + } + } + if self.none { + if i == 0 { + self.i += 1; + return Some(KartesianDirection::None); + } + i -= 1; + } + if self.diagonal { + if i < 4 { + self.i += 1; + } + match i { + 0 => return Some(KartesianDirection::TopLeft), + 1 => return Some(KartesianDirection::TopRight), + 2 => return Some(KartesianDirection::BottomLeft), + 3 => return Some(KartesianDirection::BottomRight), + _ => {}, + } + } + None + } } #[cfg(test)] @@ -186,4 +468,35 @@ mod test { current = current.anticlockwise(true); } } + + #[test] + fn test_kartesian_iter() { + let test: Vec<_> = [ + KartesianDirection::Right, + KartesianDirection::Top, + KartesianDirection::Left, + KartesianDirection::Bottom, + KartesianDirection::None, + KartesianDirection::TopLeft, + KartesianDirection::TopRight, + KartesianDirection::BottomLeft, + KartesianDirection::BottomRight, + ] + .into(); + let i: Vec<_> = KartesianDirection::iter(true, true, true).collect(); + assert_eq!(i, test); + let test: Vec<_> = [ + KartesianDirection::Right, + KartesianDirection::Top, + KartesianDirection::Left, + KartesianDirection::Bottom, + KartesianDirection::TopLeft, + KartesianDirection::TopRight, + KartesianDirection::BottomLeft, + KartesianDirection::BottomRight, + ] + .into(); + let i: Vec<_> = KartesianDirection::iter(false, true, true).collect(); + assert_eq!(i, test); + } }