pub trait Button: std::fmt::Debug + Eq { fn forbidden_position(&self) -> (i32, i32); fn to_position(&self) -> (i32, i32); fn distance_with(&self, other: &impl Button) -> Vec<(DirectionalButton, usize)> { let source = self.to_position(); let dest = other.to_position(); let (dist_v, dist_h) = (dest.0 - source.0, dest.1 - source.1); let (mul_v, mul_h) = (dist_v.abs(), dist_h.abs()); let mut movements = Vec::new(); if mul_v > 0 { movements.push(( DirectionalButton::from_direction(&(dist_v / mul_v, 0)), mul_v as usize, )); } if mul_h > 0 { movements.push(( DirectionalButton::from_direction(&(0, dist_h / mul_h)), mul_h as usize, )); } movements } fn possible_movements(&self, other: &impl Button) -> Vec> { let movements = self.distance_with(other); if movements.is_empty() { return vec![vec![DirectionalButton::A, DirectionalButton::A]]; } let source = self.to_position(); let &first_move = movements.first().unwrap(); let first_move_applied = first_move.0.apply_movement(&source, first_move.1 as i32); let &last_move = movements.last().unwrap(); let last_move_applied = last_move.0.apply_movement(&source, last_move.1 as i32); let mut possible_movements = Vec::new(); if first_move == last_move && first_move_applied != self.forbidden_position() { let mut possibility = vec![DirectionalButton::A]; possibility.extend(vec![first_move.0; first_move.1]); possible_movements.push(possibility); } else { let mut first_possibility = vec![DirectionalButton::A]; first_possibility.extend(vec![first_move.0; first_move.1]); first_possibility.extend(vec![last_move.0; last_move.1]); let mut last_possibility = vec![DirectionalButton::A]; last_possibility.extend(vec![last_move.0; last_move.1]); last_possibility.extend(vec![first_move.0; first_move.1]); if first_move_applied != self.forbidden_position() && last_move_applied != self.forbidden_position() { possible_movements.push(first_possibility); possible_movements.push(last_possibility); } else if first_move_applied != self.forbidden_position() && last_move_applied == self.forbidden_position() { possible_movements.push(first_possibility); } else if first_move_applied == self.forbidden_position() && last_move_applied != self.forbidden_position() { possible_movements.push(last_possibility); } else { unreachable!() } } for possible_movement in possible_movements.iter_mut() { possible_movement.push(DirectionalButton::A); } possible_movements } } #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] pub enum NumericButton { Zero, One, Two, Three, Four, Five, Six, Seven, Eight, Nine, A, } impl From for NumericButton { fn from(c: char) -> Self { match c { '0' => Self::Zero, '1' => Self::One, '2' => Self::Two, '3' => Self::Three, '4' => Self::Four, '5' => Self::Five, '6' => Self::Six, '7' => Self::Seven, '8' => Self::Eight, '9' => Self::Nine, 'A' => Self::A, _ => unreachable!(), } } } impl Button for NumericButton { fn forbidden_position(&self) -> (i32, i32) { (3, 0) } fn to_position(&self) -> (i32, i32) { match self { Self::Seven => (0, 0), Self::Eight => (0, 1), Self::Nine => (0, 2), Self::Four => (1, 0), Self::Five => (1, 1), Self::Six => (1, 2), Self::One => (2, 0), Self::Two => (2, 1), Self::Three => (2, 2), Self::Zero => (3, 1), Self::A => (3, 2), } } } #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] pub enum DirectionalButton { Up, Down, Left, Right, A, } impl Button for DirectionalButton { fn forbidden_position(&self) -> (i32, i32) { (0, 0) } fn to_position(&self) -> (i32, i32) { match self { Self::Up => (0, 1), Self::A => (0, 2), Self::Left => (1, 0), Self::Down => (1, 1), Self::Right => (1, 2), } } } impl DirectionalButton { fn from_direction(direction: &(i32, i32)) -> Self { match direction { (-1, 0) => Self::Up, (1, 0) => Self::Down, (0, 1) => Self::Right, (0, -1) => Self::Left, _ => unreachable!(), } } fn to_direction(&self) -> (i32, i32) { match self { Self::Up => (-1, 0), Self::Down => (1, 0), Self::Left => (0, -1), Self::Right => (0, 1), _ => unreachable!(), } } fn apply_movement(&self, position: &(i32, i32), times: i32) -> (i32, i32) { let direction = self.to_direction(); ( position.0 + direction.0 * times, position.1 + direction.1 * times, ) } }