2023-12-10 18:37:39 +01:00
#![ feature(test) ]
extern crate test ;
2023-12-10 21:50:02 +01:00
use fnv ::FnvHashSet as HashSet ;
2023-12-10 18:37:39 +01:00
use std ::mem ::transmute ;
use aoc2023 ::{
boilerplate ,
common ::* ,
direction ::{ Direction , ALL_DIRECTIONS } ,
position ::{ Position2D , PositionND } ,
} ;
use itertools ::Itertools ;
const DAY : usize = 10 ;
#[ allow(dead_code) ]
#[ derive(Debug, Copy, Clone, PartialEq) ]
#[ repr(u8) ]
enum Pipe {
Vertical = b '|' ,
Horizontal = b '-' ,
TopRight = b 'L' ,
TopLeft = b 'J' ,
BottomRight = b 'F' ,
BottomLeft = b '7' ,
None = b '.' ,
Start = b 'S' ,
}
impl Pipe {
fn openings ( self ) -> [ Direction ; 2 ] {
match self {
Pipe ::Vertical = > [ Direction ::Up , Direction ::Down ] ,
Pipe ::Horizontal = > [ Direction ::Left , Direction ::Right ] ,
Pipe ::TopRight = > [ Direction ::Up , Direction ::Right ] ,
Pipe ::TopLeft = > [ Direction ::Up , Direction ::Left ] ,
Pipe ::BottomRight = > [ Direction ::Down , Direction ::Right ] ,
Pipe ::BottomLeft = > [ Direction ::Down , Direction ::Left ] ,
Pipe ::None | Pipe ::Start = > unimplemented! ( ) ,
}
}
}
type Parsed < ' a > = ( Pos , Vec < & ' a [ Pipe ] > ) ;
2023-12-10 21:50:02 +01:00
type Pos = Position2D < isize > ;
2023-12-10 18:37:39 +01:00
fn parse_input ( raw : & str ) -> Parsed {
let grid = raw . lines ( ) . rev ( ) . map ( | l | unsafe { transmute ::< & str , & [ Pipe ] > ( l ) } ) . collect_vec ( ) ;
let start = grid
. iter ( )
2023-12-10 21:50:02 +01:00
. zip ( 0 .. )
. find_map ( | ( line , y ) | line . iter ( ) . zip ( 0 .. ) . find_map ( | ( p , x ) | ( * p = = Pipe ::Start ) . then_some ( PositionND ( [ x , y ] ) ) ) )
2023-12-10 18:37:39 +01:00
. unwrap ( ) ;
( start , grid )
}
fn step ( PositionND ( [ x , y ] ) : Pos , dir : Direction ) -> Pos {
match dir {
Direction ::Up = > PositionND ( [ x , y + 1 ] ) ,
Direction ::Right = > PositionND ( [ x + 1 , y ] ) ,
Direction ::Left = > PositionND ( [ x - 1 , y ] ) ,
Direction ::Down = > PositionND ( [ x , y - 1 ] ) ,
}
}
fn part1 ( ( start , grid ) : & Parsed ) -> usize {
ALL_DIRECTIONS
. iter ( )
. cloned ( )
2023-12-10 21:50:02 +01:00
. filter_map ( | mut dir | {
2023-12-10 18:37:39 +01:00
let mut pos = * start ;
let mut steps = 0 ;
loop {
steps + = 1 ;
pos = step ( pos , dir ) ;
if & pos = = start {
2023-12-10 21:50:02 +01:00
return Some ( steps / 2 ) ;
2023-12-10 18:37:39 +01:00
}
2023-12-10 21:50:02 +01:00
dir = * grid . get ( pos . 0 [ 1 ] as usize ) ? . get ( pos . 0 [ 0 ] as usize ) ? . openings ( ) . iter ( ) . find ( | & & o | o ! = ! dir ) . unwrap ( ) ;
2023-12-10 18:37:39 +01:00
}
} )
. max ( )
. unwrap ( )
}
2023-12-10 21:50:02 +01:00
fn part2 ( ( start , grid ) : & Parsed ) -> usize {
let mut corners = Vec ::new ( ) ;
let mut dir = Direction ::Down ; // I got this from my part 1 solution.
let mut pos = * start ;
let mut points = HashSet ::default ( ) ;
loop {
pos = step ( pos , dir ) ;
let pipe = grid [ pos . 0 [ 1 ] as usize ] [ pos . 0 [ 0 ] as usize ] ;
points . insert ( pos ) ;
if matches! ( pipe , Pipe ::Start | Pipe ::TopLeft | Pipe ::TopRight | Pipe ::BottomLeft | Pipe ::BottomRight ) {
corners . push ( pos ) ;
}
if & pos = = start {
break ;
}
dir = * pipe . openings ( ) . iter ( ) . find ( | & & o | o ! = ! dir ) . unwrap ( ) ;
}
let ylen = grid . len ( ) as isize ;
let xlen = grid [ 0 ] . len ( ) as isize ;
( 0 .. ylen )
. flat_map ( | y | ( 0 .. xlen ) . map ( move | x | PositionND ( [ x , y ] ) ) )
. filter ( | p | ! points . contains ( p ) )
. filter ( | p | is_inside ( p , & corners ) )
. count ( )
}
fn is_inside ( p : & Pos , polygon : & Vec < Pos > ) -> bool {
let mut counter = 0 ;
let mut p1 = polygon [ 0 ] ;
let mut p2 ;
for i in 1 ..= polygon . len ( ) {
p2 = polygon [ i % polygon . len ( ) ] ;
if p [ 1 ] > p1 [ 1 ] . min ( p2 [ 1 ] ) {
if p [ 1 ] < = p1 [ 1 ] . max ( p2 [ 1 ] ) {
if p [ 0 ] < = p1 [ 0 ] . max ( p2 [ 0 ] ) {
if p1 [ 1 ] ! = p2 [ 1 ] {
let xinters = ( p [ 1 ] - p1 [ 1 ] ) * ( p2 [ 0 ] - p1 [ 0 ] ) / ( p2 [ 1 ] - p1 [ 1 ] ) + p1 [ 0 ] ;
if p1 [ 0 ] = = p2 [ 0 ] | | p [ 0 ] < = xinters {
counter + = 1 ;
}
}
}
}
}
p1 = p2 ;
}
println! ( " Counter for {p:?} was {counter} " ) ;
return counter % 2 ! = 0 ;
2023-12-10 18:37:39 +01:00
}
boilerplate! {
TEST_INPUT = = " \
- L | F7
7 S - 7 |
L | 7 | |
- L - J |
L | - JF " ,
tests : {
part1 : { TEST_INPUT = > 4 } ,
2023-12-10 21:50:02 +01:00
part2 : {
INPUT_P2 = > 4 ,
INPUT_P2_2 = > 10 ,
} ,
2023-12-10 18:37:39 +01:00
} ,
bench1 = = 6923 ,
2023-12-10 21:50:02 +01:00
bench2 = = 529 ,
2023-12-10 18:37:39 +01:00
bench_parse : | p : & Parsed | ( p . 0 , p . 1. len ( ) ) = > ( PositionND ( [ 114 , 117 ] ) , 140 ) ,
}
2023-12-10 21:50:02 +01:00
#[ cfg(test) ]
const INPUT_P2 : & str = " \
.. .. .. .. ..
. S - - - - - - 7.
. | F - - - - 7 | .
. | | .. .. | | .
. | | .. .. | | .
. | L - 7 F - J | .
. | .. | | .. | .
. L - - JL - - J .
.. .. .. .. .. " ;
#[ cfg(test) ]
const INPUT_P2_2 : & str = " \
FF7FSF7F7F7F7F7F - - - 7
L | LJ | | | | | | | | | | | | F - - J
FL - 7 LJLJ | | | | | | LJL - 77
F - - JF - - 7 | | LJLJIF7FJ -
L - - - JF - JLJIIIIFJLJJ7
| F | F - JF - - - 7 IIIL7L | 7 |
| FFJF7L7F - JF7IIL - - - 7
7 - L - JL7 | | F7 | L7F - 7 F7 |
L . L7LFJ | | | | | FJL7 | | LJ
L7JLJL - JLJLJL - - JLJ . L " ;