use std::error::Error;
use std::fs::File;
+use std::collections::VecDeque;
+
pub fn run(input_file: &str) -> Result<(), Box<dyn Error>> {
let mut f = File::open(input_file)?;
let mut input = String::new();
}
pub fn print(&self) {
- self.rows.iter().for_each(|r| { println!("{:?}", r); });
+ self.rows.iter().for_each(|r| {
+ println!("{:?}", r.iter().collect::<String>());
+ });
}
pub fn roll_north(&mut self) {
}
}
+ pub fn roll_south(&mut self) {
+ self.rows.reverse();
+ self.roll_north();
+ self.rows.reverse();
+ }
+
+ // fonctionne par ligne
+ pub fn roll_west(&mut self) {
+ for j in 0..self.height {
+ let mut i = 0;
+ while i < self.width {
+ while i < self.width && self.rows[j][i] != 'O' {
+ i += 1;
+ }
+ if i >= self.width {
+ continue;
+ }
+ if self.rows[j][i] == 'O' {
+ if i == 0 {
+ i += 1;
+ continue;
+ }
+ let mut k = i;
+ while k > 0 && self.rows[j][k - 1] == '.' {
+ k -= 1;
+ }
+ self.rows[j][i] = '.';
+ self.rows[j][k] = 'O';
+ //println!("{:2} swap: {} -> {}", j, i, k);
+ //self.print();
+ }
+ i += 1;
+ }
+ }
+ }
+
+ pub fn roll_east(&mut self) {
+ self.reverse_columns();
+ self.roll_west();
+ self.reverse_columns();
+ }
+
+ pub fn reverse_columns(&mut self) {
+ for j in 0..self.height {
+ for i in 0..(self.width / 2) {
+ let tmp = self.rows[j][i];
+ self.rows[j][i] = self.rows[j][self.width - 1 - i];
+ self.rows[j][self.width - 1 - i] = tmp;
+ }
+ }
+ }
+
+ pub fn cycle(&mut self) {
+ self.roll_north();
+ self.roll_west();
+ self.roll_south();
+ self.roll_east();
+ }
+
pub fn total_load_north(&self) -> u64 {
self.rows.iter().enumerate()
.map(|(i, r)| {
})
.sum()
}
+
+ pub fn to_string(&self) -> String {
+ self.rows.iter()
+ .map(|r| format!("{}\n", r.iter().collect::<String>()))
+ .collect::<String>()
+ }
}
fn run_part1(input: &str) -> Result<u64, Box<dyn Error>> {
fn run_part2(input: &str) -> Result<u64, Box<dyn Error>> {
println!("Running day14 - part 2");
- let res = 0;
+ let mut puzzle = Puzzle::new(input);
+
+ // on suppose que la succession de cycle converge vers une boucle de cycles
+ // trouver la boucle
+ //
+ // un état A donnera toujours l'état B
+ //
+ // si on stocke les états à chaque tour, et qu'on le retrouve alors
+ // on a la boucle
+ //
+ // i - loop_start
+ // <-------------------------->
+ // 1 2 3 4 5 6 7 8 9 10 11 12 13
+ // a - b - c - d - e - f - g - h - i - h - j - i - f - g ...
+ // \ |
+ // +----<--------<---------<-+
+ //
+
+ let n_cycle = 1000000000;
+ let mut seen: VecDeque<(String, u64)> = VecDeque::new();
+ let mut loop_start = 1; // 1-based
+ let mut loop_len = 0;
+ for i in 1..=n_cycle {
+ puzzle.cycle();
+ let v = (puzzle.to_string(), puzzle.total_load_north());
+ //println!("puzzle string: {:?}", v.0);
+ //println!("cycle {} - {:3}", i, v.1);
+ if seen.contains(&v) {
+ // pop_front() retire le premier item de la boucle
+ // il y aura un item en moins à retirer lorsqu'on récupère le résultat
+ while seen.pop_front() != Some(v.clone()) {
+ loop_start += 1;
+ }
+ loop_len = (i - 1) - loop_start + 1;
+ break;
+ }
+ seen.push_back(v);
+ }
+ //dbg!(&seen);
+
+ // nombre de cycle à partir du début
+ let idx = (n_cycle - loop_start) % loop_len;
+
+ // on avance jusqu'à l'item idx (sachant que certains items ont déjà été retirés)
+ for i in 1..idx {
+ seen.pop_front();
+ }
+
+ let res = seen.pop_front().unwrap().1;
Ok(res)
}
assert_eq!(136, res.unwrap());
}
+ #[test]
+ fn day14_part2_cycle1() {
+ let mut puzzle = Puzzle::new(TEXT_INPUT);
+ puzzle.cycle();
+ //println!("puzzle string: {:?}", puzzle.to_string());
+ let expected = vec![ ".....#....",
+ "....#...O#",
+ "...OO##...",
+ ".OO#......",
+ ".....OOO#.",
+ ".O#...O#.#",
+ "....O#....",
+ "......OOOO",
+ "#...O###..",
+ "#..OO#....",
+ ];
+ puzzle.rows.iter().zip(expected)
+ .for_each(|(res, exp)| {
+ assert_eq!(res.iter().collect::<String>(), exp);
+ });
+ }
+
+ #[test]
+ fn day14_part2_cycle2() {
+ let mut puzzle = Puzzle::new(TEXT_INPUT);
+ puzzle.cycle();
+ puzzle.cycle();
+ let expected = vec![ ".....#....",
+ "....#...O#",
+ ".....##...",
+ "..O#......",
+ ".....OOO#.",
+ ".O#...O#.#",
+ "....O#...O",
+ ".......OOO",
+ "#..OO###..",
+ "#.OOO#...O",
+ ];
+ puzzle.rows.iter().zip(expected)
+ .for_each(|(res, exp)| {
+ assert_eq!(res.iter().collect::<String>(), exp);
+ });
+ }
+
+ #[test]
+ fn day14_part2_cycle3() {
+ let mut puzzle = Puzzle::new(TEXT_INPUT);
+ puzzle.cycle();
+ puzzle.cycle();
+ puzzle.cycle();
+ let expected = vec![ ".....#....",
+ "....#...O#",
+ ".....##...",
+ "..O#......",
+ ".....OOO#.",
+ ".O#...O#.#",
+ "....O#...O",
+ ".......OOO",
+ "#...O###.O",
+ "#.OOO#...O",
+ ];
+ puzzle.rows.iter().zip(expected)
+ .for_each(|(res, exp)| {
+ assert_eq!(res.iter().collect::<String>(), exp);
+ });
+ }
+
#[test]
fn day14_part2() {
let res = run_part2(TEXT_INPUT);
- assert_eq!(0, res.unwrap());
+ assert_eq!(64, res.unwrap());
}
}