From 30674b752723ab494127a00e0494758300d821ef Mon Sep 17 00:00:00 2001 From: oabrivard Date: Sat, 16 Dec 2023 23:36:56 +0100 Subject: [PATCH] Completed both parts of Day 16 puzzle --- day16/day16.go | 222 ++++++++++++++++++++++++++++++++++++++++++++ day16/day16_test.go | 69 ++++++++++++++ day16/input.txt | 110 ++++++++++++++++++++++ utils/stack.go | 40 ++------ utils/stack_test.go | 30 +----- 5 files changed, 416 insertions(+), 55 deletions(-) create mode 100644 day16/day16.go create mode 100644 day16/day16_test.go create mode 100644 day16/input.txt diff --git a/day16/day16.go b/day16/day16.go new file mode 100644 index 0000000..3589971 --- /dev/null +++ b/day16/day16.go @@ -0,0 +1,222 @@ +package day16 + +import ( + "fmt" + "sync" + + "gitea.paas.celticinfo.fr/oabrivard/aoc2023/utils" +) + +type Move struct { + row int + col int + direction rune +} + +func nextMoves(origin *Move, lines []string) []*Move { + result := []*Move{} + + switch lines[origin.row][origin.col] { + case '/': + switch origin.direction { + case 'R': + result = append(result, &Move{origin.row - 1, origin.col, 'U'}) + case 'L': + result = append(result, &Move{origin.row + 1, origin.col, 'D'}) + case 'U': + result = append(result, &Move{origin.row, origin.col + 1, 'R'}) + case 'D': + result = append(result, &Move{origin.row, origin.col - 1, 'L'}) + } + case '\\': + switch origin.direction { + case 'R': + result = append(result, &Move{origin.row + 1, origin.col, 'D'}) + case 'L': + result = append(result, &Move{origin.row - 1, origin.col, 'U'}) + case 'U': + result = append(result, &Move{origin.row, origin.col - 1, 'L'}) + case 'D': + result = append(result, &Move{origin.row, origin.col + 1, 'R'}) + } + case '|': + switch origin.direction { + case 'R', 'L': + result = append(result, &Move{origin.row - 1, origin.col, 'U'}) + result = append(result, &Move{origin.row + 1, origin.col, 'D'}) + case 'U': + result = append(result, &Move{origin.row - 1, origin.col, 'U'}) + case 'D': + result = append(result, &Move{origin.row + 1, origin.col, 'D'}) + } + case '-': + switch origin.direction { + case 'R': + result = append(result, &Move{origin.row, origin.col + 1, 'R'}) + case 'L': + result = append(result, &Move{origin.row, origin.col - 1, 'L'}) + case 'U', 'D': + result = append(result, &Move{origin.row, origin.col - 1, 'L'}) + result = append(result, &Move{origin.row, origin.col + 1, 'R'}) + } + default: + switch origin.direction { + case 'R': + result = append(result, &Move{origin.row, origin.col + 1, 'R'}) + case 'L': + result = append(result, &Move{origin.row, origin.col - 1, 'L'}) + case 'U': + result = append(result, &Move{origin.row - 1, origin.col, 'U'}) + case 'D': + result = append(result, &Move{origin.row + 1, origin.col, 'D'}) + } + } + + return result +} + +func Part1(lines []string, startRow int, startCol int, direction rune) int { + height := len(lines) + width := len(lines[0]) + cache := map[Move]bool{} + isEnergized := make([][]bool, height) + beamMoves := utils.NewStack[*Move]() + + for row := range lines { + isEnergized[row] = make([]bool, width) + } + + beamMoves.Push(&Move{startRow, startCol, direction}) + i := 0 + for beamMoves.HasElement() { + currOrigin := beamMoves.Pop() + isEnergized[currOrigin.row][currOrigin.col] = true + + _, hasKey := cache[*currOrigin] + if hasKey { + continue + } + + cache[*currOrigin] = true + moves := nextMoves(currOrigin, lines) + + for _, move := range moves { + if move.row >= 0 && move.row < height && move.col >= 0 && move.col < width { + beamMoves.Push(move) + } + } + + i++ + if i > 1000000 { + fmt.Println("loop ?") + return -1 + } + } + + count := 0 + for row := range isEnergized { + for col := range isEnergized[row] { + if isEnergized[row][col] { + count++ + } + } + } + + return count +} + +func Part2(lines []string) int { + height := len(lines) + width := len(lines[0]) + max := -1 + + var wgD sync.WaitGroup + chD := make(chan int, width) + + for i := 0; i < width; i++ { + wgD.Add(1) + go func(col int) { + defer wgD.Done() + + result := Part1(lines, 0, col, 'D') + + chD <- result + }(i) + } + wgD.Wait() + close(chD) + + for i := range chD { + if i > max { + max = i + } + } + + var wgU sync.WaitGroup + chU := make(chan int, width) + + for i := 0; i < width; i++ { + wgU.Add(1) + go func(col int) { + defer wgU.Done() + + result := Part1(lines, height-1, col, 'U') + + chU <- result + }(i) + } + wgU.Wait() + close(chU) + + for i := range chU { + if i > max { + max = i + } + } + + var wgL sync.WaitGroup + chL := make(chan int, height) + + for i := 0; i < height; i++ { + wgL.Add(1) + go func(row int) { + defer wgL.Done() + + result := Part1(lines, row, width-1, 'L') + + chL <- result + }(i) + } + wgL.Wait() + close(chL) + + for i := range chL { + if i > max { + max = i + } + } + + var wgR sync.WaitGroup + chR := make(chan int, height) + + for i := 0; i < height; i++ { + wgR.Add(1) + go func(row int) { + defer wgR.Done() + + result := Part1(lines, row, width-1, 'L') + + chR <- result + }(i) + } + wgR.Wait() + close(chR) + + for i := range chR { + if i > max { + max = i + } + } + + return max +} diff --git a/day16/day16_test.go b/day16/day16_test.go new file mode 100644 index 0000000..2bba7f0 --- /dev/null +++ b/day16/day16_test.go @@ -0,0 +1,69 @@ +package day16 + +import ( + "testing" + + "gitea.paas.celticinfo.fr/oabrivard/aoc2023/utils" +) + +func TestPart1(t *testing.T) { + lines := []string{ + `.|...\....`, + `|.-.\.....`, + `.....|-...`, + `........|.`, + `..........`, + `.........\`, + `..../.\\..`, + `.-.-/..|..`, + `.|....-|.\`, + `..//.|....`, + } + + result := Part1(lines, 0, 0, 'R') + + if result != 46 { + t.Fatalf("expected 46, got %v", result) + } +} + +func TestPart1WithInput(t *testing.T) { + lines := utils.ReadLines("input.txt") + + result := Part1(lines, 0, 0, 'R') + + if result != 8021 { + t.Fatalf("expected 8021, got %v", result) + } +} + +func TestPart2(t *testing.T) { + lines := []string{ + `.|...\....`, + `|.-.\.....`, + `.....|-...`, + `........|.`, + `..........`, + `.........\`, + `..../.\\..`, + `.-.-/..|..`, + `.|....-|.\`, + `..//.|....`, + } + + result := Part2(lines) + + if result != 51 { + t.Fatalf("expected 51, got %v", result) + } +} + +func TestPart2WithInput(t *testing.T) { + lines := utils.ReadLines("input.txt") + + result := Part2(lines) + + if result != 8216 { + t.Fatalf("expected 8216, got %v", result) + } +} diff --git a/day16/input.txt b/day16/input.txt new file mode 100644 index 0000000..cb1916e --- /dev/null +++ b/day16/input.txt @@ -0,0 +1,110 @@ +\....../..-......-.\.........\.......................-....../......../....................................|... +......\../../.....\-................../...........|................-../.............-./........|.|............ +..-...-..../..............................-..............\.........-../...........................-.\.......\. +............./-..........|..............\/.................../........../............\.......|................ +....|.....\.../...|..............\/..|-.................-..........\.\........................\............... +...........\...........-..|..../..\.-|........................-/.|/....../.........\.....|................/... +./....-|./.......|......-...-............-.......-......................................../....-............\. +....\...........|............-............-..\..|...|.........-..||.................\/...........|-|.|-....... +-............................-....................|........-...............\..................\............... +...............\......................-....../......|..||.|.-........................./......-............\... +.-...|........-.......|.........|....|.....-.........|..|............................./.../\...-........\..... +....................../...........-......-..--.|-...............-....................\......\................. +-....................-......-......./.......\|./......./........|...........|..................|.............. +..-............../....................|.............-.......-.................-..................\......\.|... +....................\...............................................|.....\................../..-/............ +...-.-/...\.//...|.............|........................\.............-|..|...........--..\........./...\..... +....................................................................\..........-......................-/...... +..................................\......-.....-...........-.............-...........\...................|...- +......................\........|../..............-.........../......-...|......../.\......./..||.............. +................................/......-................................./.........-......\./......|.......... +........../....../....\.\|......-..............................\.....................--......|.............\.. +..-.................-............../\............|.......................................\...|....\........... +./................\.|....\.........................../................|..............-....-..../.............. +../...........\.\/./........\........-\...-/...........-............../........................|.............. +......\.||.....................|..................\..............|........../......\/........../.....-........ +..|/...........\.....|../.......\...........-.............|..............-........|...............-.....|..... +....................../.....-..|.......-...-...|.....................|./...\.........-........................ +......................../........../\../.........-...\........./...............................|..\........... +.......................|\............./...|...............\/-...............|........................../..\... +..|....-.......\..................................\................................|......../.|..............- +........../...\.\........-......|.\.|.....................-............/..\..........|.../...-.............../ +..\.-..............-............/..-..\..............\.........\....../...|........|........\....../.......... +.....................|........................../..|....|.-.../........................-.\../-................ +./..........-....|......|.|.................-....|........\......|............../.........................../. +....-..................................................................................-............|..-....// +......./...-.......................-..-.........-.............|........................../.................... +.-...................-.......................................|.......-.................../..\...............\| +.........|.\......-.....\............./-....................../.........-..|....|......-...................... +......./............./..........-......-......\...................................../............/..\......... +.....\......-................................/..........|.............\........\............/................. +......../..............\.........................|..........\......-............\.....|.....\....|...\|....-.. +.....\.......\/................/...\.......|./......|............\./..........|/.....\\..\...\...|..-......... +..................|.....................-..........\....................../...../..............|......../..../ +........./...........|....-..\.....................--..|......|...\|.......|...............|...-.||........... +..............-.....\...|...|./...|..........-......../......./..........-...../..|........../................ +..|.../...................................|...................|......./..-.....-..............-............... +...-.........../..................-..........|.........................\..-................//\............/... +|...............\.........\.../.....................|.....-........................\...........-../.-......-.. +........//..........-......................-....\...-.......-./.../......../........./...........|.|.......... +...................\..../...../...|........--.....\../..........|...........................|.\............... +.\......................-........|\.....\....|.........\...............|.......\...-...|.................-.... +.....................|......./..-........................../......-.........../........................../.... +--.............\..........\-..../..-........\....-..........|./....|.\.................-..--.....\../......... +.......|........../|...................|.................\..-..........\../....|\.....|...-.......\..-........ +........../|.|.....\....................|\..\...........-............./.......|.\............................. +...........|..........-..............|.....|-................/.........|..../.........................-....... +......-..........................|............-.......|/...-....--/................/..-.........|............. +......../.-.............|....\.../............................|.|.....|....../...............\............./.. +........./.......\..........\...........\.....\..|....-...../........./........................./..|.......... +...|...|........\.......-.......\..................\....................../............-...|.........\........ +.........-...........|.........\....................../..........-.....\|.\...................\............... +.....|..........|................................../......................|............/.....|.........|..-|.. +.../.......\....//....../....\....../..........\........................./....................\....-.../...... +/.......\..\...........\................-|............/...-|.|...-..........\.................\.....|.../..... +.....-\.........|....../.......\.............-..........................\...........................\......... +.|.............|................\....\...........-................./............./............................ +./...|\...........-\....................\..............-....|....|..........-.........|.../.....-.....\.\..... +....-...............|........-............./.....\....../..\.|.....|./.\......-...........\....../.\.......\.. +..........\.....\................../......|.\..../.\..-......-.........-............/....\...................\ +..\.........|.....\..........\............./...-....../....|.......-|\-............................../........ +..|...\/........\..-\.|.\......./.....|.........-..|........|......................|.......................... +............|..........................--.-....\..............-................................|...|.......... +.-............../.....................|........../.................-....../.-...........................\-.... +.-.........................-......|-./\.........................\.....|.................|......|..\.......\... +.....\...|....-......................................-...............\...........\..\...\..-|.....\........... +|...........|................|.........-............|....................|............-..-.............../.... +.................../.|...........\.......\....|......-..|.....................\...................|..\.....\.. +.|........-.../...|....../...|......|.|.|..-......\............../....|...........\|.......|.......\\......... +.......|.....-..-.-./....-/\.|./.............|.../......\...|/............/........./...-..........|\.\....... +..|.........-....\.........\.......\....-................|-..........................-.|............|......... +......-..\......|..\.../......\|..........|............../........../............/..\........................| +|/.|................../....|............../..........-........-............................|.\................ +........|.....|/................../....\....../....../...................--..............\/..\................ +.../-..........................|...-.........-.................\\.......-\.........\..|.....|................. +-./...-.....-....................../.../........./........-........../.........-...........\.................. +.\../............|.\..........-.................................\.............../............../.........\./.. +...-.....-......-......../.\...\................\...........|................................................. +...........-...//.....-/...-...........\|./......................\.........................|.-|...|........... +....../.....................-.......|........|-..................\............-.....................|......... +.....................\.\................../..|/......\........|.\..............|\..............\..-........... +....\................|\......................./.....-...............................-......../....-........... +...\....................-....|......|........-.\..|.......................\...\.|..............|............/. +...................-/.....................................-..\............-............-.............-....\... +.|....../.....\.....-.../......../..../..\.-...........\.../................|........\.................../.... +.......|........./........................\.....-........-...............\....../....|../....\......./........ +..........|.........../|......|......................-..............|./......................................\ +....................../...../..............|.||............./......-......\................................... +\........|...-..................\./.................\.\.........-....\........../.../.-...\................... +....-..........|.......-......../................................./.../....-.............-..........\-........ +..............................-.............\..\......................................-......../.............. +......./.\|......../...-........|.........|................/.........................-....|...\.........|..... +......|..........-.......-............./........|..\...|.|..-.............................-.-...\............. +.||...........-.......\...../.....................-././..../.............-................|../.............--. +................\.-.....-\..........\.............\............/.........\.......-........\................... +......./...........\........\./.|..............|......-...........\................\...../.\...../..........|. +.-..-...|.....................-....../..............-.\|....\.......|..|....././.......|...................... +./...-..................../................/....-/..\............/..................................\......... +....\.-..........\-......../............|...\.............|..................|.....\.................../...... +...........-/..\..-................-....|............/...-.|.......-.|........./.....-/......./............... +..../...-.....\..............-..\.....\.|............./...|.\..................\...|.........\-./..|...../.... diff --git a/utils/stack.go b/utils/stack.go index e396cdc..db5a1a8 100644 --- a/utils/stack.go +++ b/utils/stack.go @@ -22,28 +22,14 @@ IN THE SOFTWARE. package utils -// stack head == 0 if stack is empty. -// stack head == size if stack is full. type Stack[T any] struct { - size int - head int data []T } -// New creates an empty stack that can contain a maximum of size elements. -func NewStack[T any](size int) Stack[T] { - if size < 0 { - panic("stack size must be >= 0") - } - +// New creates an empty stack +func NewStack[T any]() Stack[T] { result := Stack[T]{ - size, - 0, - nil, - } - - if size > 0 { - result.data = make([]T, size) + []T{}, } return result @@ -51,12 +37,7 @@ func NewStack[T any](size int) Stack[T] { // Push pushes the value v on top of stack s. func (s *Stack[T]) Push(v T) { - if s.head == s.size { - panic("called Push() on a full stack") - } - - s.data[s.head] = v - s.head++ + s.data = append(s.data, v) } // Pop removes the value v from the top of stack s and returns it. @@ -65,27 +46,26 @@ func (s *Stack[T]) Pop() T { panic("called Pop() on an empty stack") } - s.head-- - return s.data[s.head] + value := s.data[len(s.data)-1] + s.data = s.data[0 : len(s.data)-1] + return value } // HasElement returns true if the stack s contains at least one element. func (s *Stack[T]) HasElement() bool { - return s.head > 0 + return len(s.data) > 0 } // Count returns the number of elements contained in s. func (s *Stack[T]) Count() int { - return s.head + return len(s.data) } // Reversed returns a new stack built from stack s, with its elements in reverse order. // s will be emptied by this operation. func (s *Stack[T]) Reversed() Stack[T] { result := Stack[T]{ - s.size, - 0, - make([]T, s.size), + make([]T, len(s.data)), } for s.HasElement() { diff --git a/utils/stack_test.go b/utils/stack_test.go index 0574b7a..a2bf816 100644 --- a/utils/stack_test.go +++ b/utils/stack_test.go @@ -24,38 +24,18 @@ package utils import "testing" -func TestNegativeSize(t *testing.T) { - defer func() { _ = recover() }() // turn off the panic - - _ = NewStack[int](-1) - - // Never reaches here if New() panics. - t.Errorf("New() with negative size should panic") -} - func TestEmpty(t *testing.T) { defer func() { _ = recover() }() // turn off the panic - s := NewStack[int](0) + s := NewStack[int]() s.Pop() // Never reaches here if Pop() panics. t.Errorf("Pop() on empty stack should panic") } -func TestFull(t *testing.T) { - defer func() { _ = recover() }() // turn off the panic - - s := NewStack[int](1) - s.Push(1) - s.Push(2) - - // Never reaches here if Push() panics. - t.Errorf("Push() on full stack should panic") -} - func TestHasElements(t *testing.T) { - s := NewStack[int](1) + s := NewStack[int]() if s.HasElement() { t.Errorf("HasElement() should return false for empty stack") @@ -69,7 +49,7 @@ func TestHasElements(t *testing.T) { } func TestCount(t *testing.T) { - s := NewStack[int](1) + s := NewStack[int]() if s.Count() != 0 { t.Errorf("Count() should be 0 for empty stack") @@ -83,7 +63,7 @@ func TestCount(t *testing.T) { } func TestPushPop(t *testing.T) { - s1 := NewStack[int](3) + s1 := NewStack[int]() s1.Push(1) s1.Push(2) s1.Push(3) @@ -100,7 +80,7 @@ func TestPushPop(t *testing.T) { } func TestReversed(t *testing.T) { - s1 := NewStack[int](3) + s1 := NewStack[int]() s1.Push(1) s1.Push(2) s1.Push(3)