.for_each(|(i,l)| {
let mut row: Vec<char> = Vec::new();
- row.push('.');
+ row.push(' ');
l.chars().for_each(|c| { row.push(c); });
- row.push('.');
+ row.push(' ');
// ajout d'une première ligne de '.'
if i == 0 {
- map.push( (0..row.len()).map(|_| '.').collect::<Vec<char>>() );
+ map.push( (0..row.len()).map(|_| ' ').collect::<Vec<char>>() );
}
map.push(row);
});
// ajout d'une dernière ligne
- map.push( (0..map[0].len()).map(|_| '.').collect::<Vec<char>>() );
+ map.push( (0..map[0].len()).map(|_| ' ').collect::<Vec<char>>() );
Self {
start,
- map
+ map,
}
}
pub fn get_char(self: &Self, pos: (usize, usize)) -> char {
self.map[pos.0][pos.1]
}
+
+ pub fn frontier_coord(self: &Self) -> Vec<(usize, usize)> {
+ let mut current = self.start;
+ let (row, col) = current;
+ // on recherche dans les 4 directions
+ let start = [
+ (row - 1, col),
+ (row + 1, col),
+ (row, col - 1),
+ (row, col + 1),
+ ];
+
+ let mut frontier_coord: Vec<(usize, usize)> = Vec::new();
+ for mut next in start {
+ frontier_coord = Vec::new();
+ while self.get_char(next) != 'S' && is_connected(self.get_char(next), current, next) {
+ frontier_coord.push((current.0, current.1));
+ let next_next = get_next_next(self.get_char(next), current, next);
+ current = next;
+ next = next_next;
+ }
+ if self.get_char(next) == 'S' {
+ frontier_coord.push((current.0, current.1));
+ break;
+ }
+ }
+
+ frontier_coord
+ }
+
+ // crée une nouvelle carte agrandie en ajoutant un caractère intermédiaire
+ // pour "décoller" les tuyaux qui se toucheraient
+ // et ainsi permettre de toujours trouver soit
+ // - un chemin qui amène au bord
+ // - pas de chemin (et donc ensemble fermé)
+ pub fn extend(self: &Self) -> Self {
+ let mut new_map: Vec<Vec<char>> = Vec::new();
+ let mut new_start = (0, 0);
+
+ let frontier_coord = self.frontier_coord();
+
+ for row in 0..self.map.len() {
+ let mut new_row: Vec<char> = Vec::new();
+ // ajout d'une colonne intemédiaire
+ // attention à n'ajouter que des éléments de la frontière
+ self.map[row].iter()
+ .enumerate()
+ .for_each(|(col, &c)| {
+ let mut new_c = match c {
+ 'S' => {
+ let right_char = self.map[self.start.0][self.start.1 + 1];
+ match right_char {
+ '-' | 'j' | '7' => '-',
+ '|' | 'L' | 'F' | '.' => ' ',
+ _ => unreachable!(),
+ }
+ }
+ '-' | 'L' | 'F' => '-',
+ '|' | 'J' | '7' | '.' | ' ' => ' ',
+ _ => unreachable!(),
+ };
+ if !is_frontier(&frontier_coord, (row, col)) {
+ new_c = ' ';
+ }
+ new_row.push(c);
+ new_row.push(new_c);
+ });
+ new_map.push(new_row.clone());
+ //println!("row[{:3}]: {}", row, new_row.iter().collect::<String>());
+
+ // ajouter une ligne intermédiaire
+ // attention à n'ajouter que des éléments de la frontière
+ let mut intermediary_row: Vec<char> = Vec::new();
+ new_row.iter()
+ .enumerate()
+ .for_each(|(col, &c)| {
+ let new_c = match c {
+ 'S' => {
+ new_start = (2 * row, col);
+ let bottom_char = self.map[self.start.0 + 1][self.start.1];
+ match bottom_char {
+ '|' | 'L' | 'J' => '|',
+ '-' | '7' | 'F' | '.' => ' ',
+ _ => unreachable!(),
+ }
+ },
+ '|' | '7' | 'F' => {
+ if is_frontier(&frontier_coord, (row, col / 2)) {
+ '|'
+ } else {
+ ' '
+ }
+ }
+ '-' | 'L' | 'J' | '.' | ' ' => ' ',
+ _ => unreachable!(),
+ };
+ intermediary_row.push(new_c);
+ });
+ new_map.push(intermediary_row);
+ }
+
+ Self {
+ map: new_map,
+ start: new_start,
+ }
+ }
}
// a -> b
'J' => (a.1 == b.1 && (a.0 + 1) == b.0) || (a.0 == b.0 && (a.1 + 1) == b.1),
'7' => (a.1 == b.1 && a.0 == (b.0 + 1)) || (a.0 == b.0 && (a.1 + 1) == b.1),
'F' => (a.1 == b.1 && a.0 == (b.0 + 1)) || (a.0 == b.0 && a.1 == (b.1 + 1)),
- '.' => false,
+ '.' | ' ' => false,
_ => unreachable!(),
}
}
+fn is_frontier(frontier: &Vec<(usize, usize)>, x: (usize, usize)) -> bool {
+ frontier.contains(&x)
+}
+
// besoin de connaitre le sens
// c le char du tuyau de connexion
// current: la position actuelle
}
},
- '.' | 'S' => (0, 0),
+ '.' | 'S' | ' ' => (0, 0),
_ => unreachable!(),
}
}
+fn print_map(map: &Vec<Vec<char>>) {
+ map.into_iter()
+ .for_each(|row| {
+ let s: String = row.into_iter().collect::<String>();
+ println!("{}", s);
+ });
+}
+
fn run_part1(input: &str) -> Result<u32, Box<dyn Error>> {
println!("Running day10 - part 1");
let puzzle = Puzzle::new(input);
- let mut current = puzzle.start;
- let (row, col) = current;
- // on recherche dans les 4 directions
- let start = [
- (row - 1, col),
- (row + 1, col),
- (row, col - 1),
- (row, col + 1),
- ];
- let mut count: u32 = 0;
- for mut next in start {
- count = 1;
- while puzzle.get_char(next) != 'S' && is_connected(puzzle.get_char(next), current, next) {
- count += 1;
- let next_next = get_next_next(puzzle.get_char(next), current, next);
- current = next;
- next = next_next;
- }
- if puzzle.get_char(next) == 'S' {
- break;
- }
- }
-
- let res = count.div_ceil(2);
+ let res = (puzzle.frontier_coord().len() as u32).div_ceil(2);
Ok(res)
}
fn run_part2(input: &str) -> Result<u32, Box<dyn Error>> {
println!("Running day10 - part 2");
- let res = 0;
+
+ let puzzle = Puzzle::new(input);
+ let puzzle_frontier_length = puzzle.frontier_coord().len() as u32;
+
+ // pour chaque point qui n'est pas la frontière
+ // si on atteint la bordure dans au moins 1 des 4 directions, alors
+ // ce point est à l'exterieur
+ // sinon 2 cas
+ // - cas de bordures qui se touchent, mais le point à à l'exterieur de
+ // l'ensemble
+ // - cas où le point à à l'intérieur de la partie fermée => OK
+ //
+ // comment déterminer si un point est à l'extérieur de la partie fermée
+ // et en particulier du fait que 2 tuyaux se touchent ?
+ //
+ // déterminer qu'un point est à l'extérieur
+ // - on atteint la bordure sans traverser la frontière
+
+
+ // idée: modifier le pattern pour ajouter les interstices
+ // comme cela il n'y aura plus qu'à touver un chemin vers une bordure ou non
+ // attention cela ajoute de la complexité car des formes ne sont pas à compter
+ // utiliser un caractère non défini pour ne pas avoir à le compter (par ex un espace ' ')
+
+ //print_map(&puzzle.map);
+
+ let puzzle_extended = puzzle.extend();
+ let map_extended = &puzzle_extended.map;
+ //println!("new start: {:?}", &puzzle_extended.start);
+ //print_map(map_extended);
+
+ // pour chaque case qui n'est pas sur la frontière, on regarde si on peut
+ // atteindre le bord en partant d'elle sans avoir à passer par la frontière
+ // si c'est le cas, la case est à l'extérieur
+ // sinon elle est à l'intérieur
+
+ // on check que la case n'a pas déjà été visitée
+ let mut seen: Vec<Vec<bool>> = Vec::new();
+ map_extended.iter()
+ .for_each(|row| {
+ seen.push(vec![false; row.len()]);
+ });
+
+ // récupérer les coordonnées de la frontière de la boucle
+ let frontier_coord = puzzle_extended.frontier_coord();
+
+ fn is_border(map: &Vec<Vec<char>>, x: (usize, usize)) -> bool {
+ x.0 == 0 || x.1 == 0 || x.0 == map.len() - 1 || x.1 == map[0].len()
+ }
+
+ // prendre un point non encore visité
+ // chercher la bordure
+ //
+ // avec l'agrandissement on devrait avoir exactement 3 ensembles disjoints
+ // - la frontière
+ // - l'extérieur
+ // - l'intérieur
+ //
+
+ // on retire de "seen" la bordure fictive ajoutée autour du puzzle
+ (0..map_extended.len()).for_each(|i| {
+ seen[i][0] = true;
+ seen[i][map_extended[0].len() - 1] = true;
+ });
+ (0..seen[0].len()).for_each(|i| {
+ seen[0][i] = true;
+ seen[map_extended.len() - 1][i] = true;
+ });
+
+ // on part de start et on visite tous les nœuds adjacents en dehors de la frontière
+ // on compte le nombre de nœuds (d'origine) visité
+ fn travel((r, c): (usize, usize), seen: &mut Vec<Vec<bool>>, map: &Vec<Vec<char>>, frontier: &Vec<(usize, usize)>) -> u32 {
+ if is_border(map, (r, c)) || is_frontier(frontier, (r,c)) {
+ return 0;
+ }
+ seen[r][c] = true;
+ let mut count: u32 = match map[r][c] {
+ ' ' => 0,
+ _ => 1,
+ };
+
+ // on va dans les 4 directions
+ if !seen[r - 1][c] {
+ count += travel((r - 1, c), seen, map, frontier);
+ }
+ if !seen[r + 1][c] {
+ count += travel((r + 1, c), seen, map, frontier);
+ }
+ if !seen[r][c - 1] {
+ count += travel((r, c - 1), seen, map, frontier);
+ }
+ if !seen[r][c + 1] {
+ count += travel((r, c + 1), seen, map, frontier);
+ }
+ count
+ }
+
+ let mut start = (1, 1);
+ let count_outside = travel(start, &mut seen, map_extended, &frontier_coord);
+
+ // penser à retirer 2 lignes et 2 colonnes (les bordures en plus)
+ let total: u32 = (puzzle.map[0].len() - 2) as u32 * (puzzle.map.len() - 2) as u32;
+
+ let res = total - puzzle_frontier_length - count_outside;
Ok(res)
}
}
#[test]
- fn day10_part2() {
- let input = "";
+ fn day10_part2_example1() {
+ let input = "\
+...........
+.S-------7.
+.|F-----7|.
+.||.....||.
+.||.....||.
+.|L-7.F-J|.
+.|..|.|..|.
+.L--J.L--J.
+...........";
+ let res = run_part2(&input);
+ assert_eq!(4, res.unwrap());
+ }
+
+ #[test]
+ fn day10_part2_example2() {
+ let input = "\
+..........
+.S------7.
+.|F----7|.
+.||....||.
+.||....||.
+.|L-7F-J|.
+.|..||..|.
+.L--JL--J.
+..........";
+ let res = run_part2(&input);
+ assert_eq!(4, res.unwrap());
+ }
+
+ #[test]
+ fn day10_part2_example3() {
+ let input = "\
+.F----7F7F7F7F-7....
+.|F--7||||||||FJ....
+.||.FJ||||||||L7....
+FJL7L7LJLJ||LJ.L-7..
+L--J.L7...LJS7F-7L7.
+....F-J..F7FJ|L7L7L7
+....L7.F7||L7|.L7L7|
+.....|FJLJ|FJ|F7|.LJ
+....FJL-7.||.||||...
+....L---J.LJ.LJLJ...";
+ let res = run_part2(&input);
+ assert_eq!(8, res.unwrap());
+ }
+
+ #[test]
+ fn day10_part2_example4() {
+ let input = "\
+FF7FSF7F7F7F7F7F---7
+L|LJ||||||||||||F--J
+FL-7LJLJ||||||LJL-77
+F--JF--7||LJLJ7F7FJ-
+L---JF-JLJ.||-FJLJJ7
+|F|F-JF---7F7-L7L|7|
+|FFJF7L7F-JF7|JL---7
+7-L-JL7||F7|L7F-7F7|
+L.L7LFJ|||||FJL7||LJ
+L7JLJL-JLJLJL--JLJ.L";
let res = run_part2(&input);
- assert_eq!(0, res.unwrap());
+ assert_eq!(10, res.unwrap());
}
}