178 lines
5.1 KiB
Rust
178 lines
5.1 KiB
Rust
pub struct DialPosition {
|
|
pub current_position: i16,
|
|
pub zero_hits_count: u32,
|
|
}
|
|
|
|
impl DialPosition {
|
|
const DIAL_MAX: i16 = 99;
|
|
const DIAL_COUNT: i16 = 100;
|
|
|
|
pub fn new(current_position: i16) -> Self {
|
|
Self {
|
|
current_position,
|
|
zero_hits_count: 0,
|
|
}
|
|
}
|
|
|
|
pub fn rotated_left(&self, rotation: u64) -> Self {
|
|
let (remaining, reduce_hits_count) = DialPosition::reduce_rotation(rotation);
|
|
let current_position_u64 = self.current_position;
|
|
let (result, clamp_hits_count) = self.clamp(current_position_u64 - remaining);
|
|
|
|
Self {
|
|
current_position: result,
|
|
zero_hits_count: self.zero_hits_count + clamp_hits_count + reduce_hits_count,
|
|
}
|
|
}
|
|
|
|
pub fn rotated_right(&self, rotation: u64) -> Self {
|
|
let (remaining, reduce_hits_count) = DialPosition::reduce_rotation(rotation);
|
|
let (result, clamp_hits_count) = self.clamp(self.current_position + remaining);
|
|
|
|
Self {
|
|
current_position: result,
|
|
zero_hits_count: self.zero_hits_count + clamp_hits_count + reduce_hits_count,
|
|
}
|
|
}
|
|
|
|
fn reduce_rotation(rotation: u64) -> (i16, u32) {
|
|
let mut remaining = rotation;
|
|
let mut zero_hits_count = 0;
|
|
while remaining > u64::try_from(DialPosition::DIAL_MAX).unwrap() {
|
|
remaining -= u64::try_from(DialPosition::DIAL_COUNT).unwrap();
|
|
zero_hits_count += 1;
|
|
}
|
|
|
|
(i16::try_from(remaining).unwrap(), zero_hits_count)
|
|
}
|
|
|
|
fn clamp(&self, value: i16) -> (i16, u32) {
|
|
if value > DialPosition::DIAL_MAX {
|
|
(value - 100, 1)
|
|
} else if value < 0 {
|
|
(100 + value, if self.current_position != 0 { 1 } else { 0 })
|
|
} else {
|
|
(value, if value == 0 { 1 } else { 0 })
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn dial_rotated_right_from_0_by_2_gives_2() {
|
|
let result = DialPosition::new(0).rotated_right(2);
|
|
assert_eq!(result.current_position, 2);
|
|
}
|
|
|
|
#[test]
|
|
fn dial_rotated_right_from_0_by_102_gives_2() {
|
|
let result = DialPosition::new(0).rotated_right(102);
|
|
assert_eq!(result.current_position, 2);
|
|
}
|
|
|
|
#[test]
|
|
fn dial_rotated_left_from_0_by_2_gives_98() {
|
|
let result = DialPosition::new(0).rotated_left(2);
|
|
assert_eq!(result.current_position, 98);
|
|
}
|
|
|
|
#[test]
|
|
fn dial_rotated_left_from_0_by_102_gives_98() {
|
|
let result = DialPosition::new(0).rotated_left(102);
|
|
assert_eq!(result.current_position, 98);
|
|
}
|
|
|
|
#[test]
|
|
fn dial_rotated_left_from_5_by_105_gives_0() {
|
|
let result = DialPosition::new(5).rotated_left(105);
|
|
assert_eq!(result.current_position, 0);
|
|
}
|
|
|
|
#[test]
|
|
fn dial_rotated_right_from_5_by_105_gives_10() {
|
|
let result = DialPosition::new(5).rotated_right(105);
|
|
assert_eq!(result.current_position, 10);
|
|
}
|
|
}
|
|
|
|
// part 2
|
|
#[test]
|
|
fn dial_from_50_right_1000_hits_zero_10_times() {
|
|
let result = DialPosition::new(50).rotated_right(1000);
|
|
assert_eq!(result.current_position, 50);
|
|
assert_eq!(result.zero_hits_count, 10);
|
|
}
|
|
|
|
#[test]
|
|
fn dial_from_50_left_68_hits_zero_once() {
|
|
let result = DialPosition::new(50).rotated_left(68);
|
|
assert_eq!(result.current_position, 82);
|
|
assert_eq!(result.zero_hits_count, 1);
|
|
}
|
|
|
|
#[test]
|
|
fn dial_from_82_left_30_no_zero_hit() {
|
|
let result = DialPosition::new(82).rotated_left(30);
|
|
assert_eq!(result.current_position, 52);
|
|
assert_eq!(result.zero_hits_count, 0);
|
|
}
|
|
|
|
#[test]
|
|
fn dial_from_52_right_48_lands_on_zero() {
|
|
let result = DialPosition::new(52).rotated_right(48);
|
|
assert_eq!(result.current_position, 0);
|
|
assert_eq!(result.zero_hits_count, 1);
|
|
}
|
|
|
|
#[test]
|
|
fn dial_from_0_left_5_no_zero_hit() {
|
|
let result = DialPosition::new(0).rotated_left(5);
|
|
assert_eq!(result.current_position, 95);
|
|
assert_eq!(result.zero_hits_count, 0);
|
|
}
|
|
|
|
#[test]
|
|
fn dial_from_95_right_60_hits_zero_once() {
|
|
let result = DialPosition::new(95).rotated_right(60);
|
|
assert_eq!(result.current_position, 55);
|
|
assert_eq!(result.zero_hits_count, 1);
|
|
}
|
|
|
|
#[test]
|
|
fn dial_from_55_left_55_lands_on_zero() {
|
|
let result = DialPosition::new(55).rotated_left(55);
|
|
assert_eq!(result.current_position, 0);
|
|
assert_eq!(result.zero_hits_count, 1);
|
|
}
|
|
|
|
#[test]
|
|
fn dial_from_0_left_1_no_zero_hit() {
|
|
let result = DialPosition::new(0).rotated_left(1);
|
|
assert_eq!(result.current_position, 99);
|
|
assert_eq!(result.zero_hits_count, 0);
|
|
}
|
|
|
|
#[test]
|
|
fn dial_from_99_left_99_lands_on_zero() {
|
|
let result = DialPosition::new(99).rotated_left(99);
|
|
assert_eq!(result.current_position, 0);
|
|
assert_eq!(result.zero_hits_count, 1);
|
|
}
|
|
|
|
#[test]
|
|
fn dial_from_0_right_14_no_zero_hit() {
|
|
let result = DialPosition::new(0).rotated_right(14);
|
|
assert_eq!(result.current_position, 14);
|
|
assert_eq!(result.zero_hits_count, 0);
|
|
}
|
|
|
|
#[test]
|
|
fn dial_from_14_left_82_hits_zero_once() {
|
|
let result = DialPosition::new(14).rotated_left(82);
|
|
assert_eq!(result.current_position, 32);
|
|
assert_eq!(result.zero_hits_count, 1);
|
|
}
|