Solve day 16 part 2

master
Nick Krichevsky 2023-12-18 10:56:29 -05:00
parent 34d2d7533e
commit eee5e391ba
2 changed files with 75 additions and 13 deletions

View File

@ -38,9 +38,9 @@ type Beam struct {
} }
type TileGrid struct { type TileGrid struct {
height int Height int
width int Width int
tiles map[Coordinate]Tile Tiles map[Coordinate]Tile
} }
func (dir Direction) Horizontal() bool { func (dir Direction) Horizontal() bool {
@ -56,10 +56,10 @@ func (grid TileGrid) Print() {
} }
func (grid TileGrid) PrintWithBeams(beams []Beam) { func (grid TileGrid) PrintWithBeams(beams []Beam) {
for row := 0; row < grid.height; row++ { for row := 0; row < grid.Height; row++ {
for col := 0; col < grid.width; col++ { for col := 0; col < grid.Width; col++ {
position := Coordinate{row: row, col: col} position := Coordinate{row: row, col: col}
tile, ok := grid.tiles[position] tile, ok := grid.Tiles[position]
beamIdx := slices.IndexFunc(beams, func(beam Beam) bool { beamIdx := slices.IndexFunc(beams, func(beam Beam) bool {
return beam.position == position return beam.position == position
}) })
@ -88,10 +88,12 @@ func (grid TileGrid) PrintWithBeams(beams []Beam) {
} }
} }
// InBounds checks if the given position is in bounds of the grid
func (grid TileGrid) InBounds(position Coordinate) bool { func (grid TileGrid) InBounds(position Coordinate) bool {
return position.row >= 0 && position.col >= 0 && position.row < grid.height && position.col < grid.width return position.row >= 0 && position.col >= 0 && position.row < grid.Height && position.col < grid.Width
} }
// MovedInDirection returns a new beam, which is the result of this beam having moved in the given direction.
func (beam Beam) MovedInDirection(dir Direction) Beam { func (beam Beam) MovedInDirection(dir Direction) Beam {
updPosition := beam.position updPosition := beam.position
switch dir { switch dir {
@ -113,6 +115,8 @@ func (beam Beam) MovedInDirection(dir Direction) Beam {
} }
} }
// RotatedInDirection returns a new beam, which is the result of this beam rotated to face that direction.
// Its position is not updated.
func (beam Beam) RotatedInDirection(dir Direction) Beam { func (beam Beam) RotatedInDirection(dir Direction) Beam {
return Beam{ return Beam{
position: beam.position, position: beam.position,
@ -146,13 +150,30 @@ func main() {
panic(fmt.Sprintf("failed to parse input: %s", err)) panic(fmt.Sprintf("failed to parse input: %s", err))
} }
// grid.Print()
fmt.Printf("Part 1: %d\n", part1(grid)) fmt.Printf("Part 1: %d\n", part1(grid))
fmt.Printf("Part 2: %d\n", part2(grid))
} }
func part1(grid TileGrid) int { func part1(grid TileGrid) int {
startingBeam := Beam{position: Coordinate{row: 0, col: 0}, direction: DirectionEast} startingBeam := Beam{position: Coordinate{row: 0, col: 0}, direction: DirectionEast}
return simulate(grid, startingBeam)
}
func part2(grid TileGrid) int {
startingBeams := allStartingBeams(grid)
maxEnergy := 0
for _, startingBeam := range startingBeams {
energy := simulate(grid, startingBeam)
if energy > maxEnergy {
maxEnergy = energy
}
}
return maxEnergy
}
// simulate will simulate the beam's movement starting at the given beam, returning the number of energized tiles
func simulate(grid TileGrid, startingBeam Beam) int {
beams := []Beam{startingBeam} beams := []Beam{startingBeam}
nextBeams := []Beam{} nextBeams := []Beam{}
beamHistory := map[Beam]struct{}{ beamHistory := map[Beam]struct{}{
@ -162,7 +183,7 @@ func part1(grid TileGrid) int {
for len(beams) > 0 { for len(beams) > 0 {
for _, beam := range beams { for _, beam := range beams {
updBeam := beam.MovedInDirection(beam.direction) updBeam := beam.MovedInDirection(beam.direction)
tile, ok := grid.tiles[updBeam.position] tile, ok := grid.Tiles[updBeam.position]
if ok { if ok {
resultingBeams := beamsFromCollision(tile, updBeam) resultingBeams := beamsFromCollision(tile, updBeam)
nextBeams = append(nextBeams, resultingBeams...) nextBeams = append(nextBeams, resultingBeams...)
@ -191,6 +212,41 @@ func part1(grid TileGrid) int {
return len(visitedPositions) return len(visitedPositions)
} }
// allStartingBeams gets all possible starting beams around the edges of the grid
func allStartingBeams(grid TileGrid) []Beam {
startingBeams := []Beam{}
for col := 0; col < grid.Width; col++ {
topBeam := Beam{
direction: DirectionSouth,
position: Coordinate{row: 0, col: col},
}
bottomBeam := Beam{
direction: DirectionNorth,
position: Coordinate{row: grid.Height - 1, col: col},
}
startingBeams = append(startingBeams, topBeam, bottomBeam)
}
for row := 0; row < grid.Height; row++ {
leftBeam := Beam{
direction: DirectionEast,
position: Coordinate{row: row, col: 0},
}
rightBeam := Beam{
direction: DirectionWest,
position: Coordinate{row: row, col: grid.Width - 1},
}
startingBeams = append(startingBeams, leftBeam, rightBeam)
}
return startingBeams
}
// beamsFromCollision will get the resulting beams when a beam collides with a tile
func beamsFromCollision(tile Tile, beam Beam) []Beam { func beamsFromCollision(tile Tile, beam Beam) []Beam {
switch tile { switch tile {
case TileMirrorLeft, TileMirrorRight: case TileMirrorLeft, TileMirrorRight:
@ -202,6 +258,8 @@ func beamsFromCollision(tile Tile, beam Beam) []Beam {
} }
} }
// beamsFromSplitter gets the resulting beams when a bema collides with a splitter. Panics if the given tile
// is not a splitter.
func beamsFromSplitter(tile Tile, beam Beam) []Beam { func beamsFromSplitter(tile Tile, beam Beam) []Beam {
if tile != TileSplitterHorizontal && tile != TileSplitterVertical { if tile != TileSplitterHorizontal && tile != TileSplitterVertical {
panic("cannot treat a non-splitter tile as a splitter") panic("cannot treat a non-splitter tile as a splitter")
@ -227,6 +285,8 @@ func beamsFromSplitter(tile Tile, beam Beam) []Beam {
} }
} }
// beamsFromMirror gets the resulting beams (well, beam) when a beam collides with a mirror. Panics if the given tile
// is not a mirror.
func beamsFromMirror(tile Tile, beam Beam) []Beam { func beamsFromMirror(tile Tile, beam Beam) []Beam {
switch tile { switch tile {
case TileMirrorRight: case TileMirrorRight:
@ -303,8 +363,8 @@ func parseTileGrid(inputLines []string) (TileGrid, error) {
} }
return TileGrid{ return TileGrid{
height: height, Height: height,
width: width, Width: width,
tiles: tiles, Tiles: tiles,
}, nil }, nil
} }

2
go.mod
View File

@ -1,3 +1,5 @@
module github.com/ollien/advent-of-code-2023 module github.com/ollien/advent-of-code-2023
go 1.21.4 go 1.21.4
require golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611 // indirect