diff --git a/README.md b/README.md index 3c7a07f..b81b00d 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ go test ./cmd/01 | 04 | [Problem](https://adventofcode.com/2025/day/4) | [Solution](./cmd/04/) | Part 1 was by far the most straight-forward challenge so far. This was easiest day of AoC this year by far. | | 05 | [Problem](https://adventofcode.com/2025/day/5) | [Solution](./cmd/05/) | Part 1 was fairly straight forward. Merging ranges was trickiest bit. I feel like I'm being led into a trap with how straight forward today was. | | 06 | [Problem](https://adventofcode.com/2025/day/6) | [Solution](./cmd/06/) | Part 1 I don't think it is the best solution, but it does solve it. I kicked ass on part 2. 🥳 | -| 07 | [Problem](https://adventofcode.com/2025/day/7) | [Solution](./cmd/07/) | FIFO for life. 🤣 | +| 07 | [Problem](https://adventofcode.com/2025/day/7) | [Solution](./cmd/07/) | FIFO for life. 🤣. Fuck me...part 2 I found really difficult and the use of DFS was obvious, but doing the memoizing was not. | | 08 | [Problem](https://adventofcode.com/2025/day/8) | [Solution](./cmd/08/) | | | 09 | [Problem](https://adventofcode.com/2025/day/9) | [Solution](./cmd/09/) | | | 10 | [Problem](https://adventofcode.com/2025/day/10) | [Solution](./cmd/10/) | | diff --git a/cmd/07/main.go b/cmd/07/main.go index e60eb1f..a8e0997 100644 --- a/cmd/07/main.go +++ b/cmd/07/main.go @@ -6,6 +6,7 @@ import ( "github.com/StevanFreeborn/advent-of-code-2025/internal/move" "github.com/StevanFreeborn/advent-of-code-2025/internal/position" "github.com/StevanFreeborn/advent-of-code-2025/internal/queue" + "github.com/StevanFreeborn/advent-of-code-2025/internal/stack" ) const StartCharacter = "S" @@ -68,3 +69,79 @@ func SolvePartOne(filePath string) int { return total } + +func SolvePartTwo(filePath string) int { + input := file.ReadAllLines(filePath) + grid := grid.From(input) + + var startPosition position.Position + + for p, v := range grid.Positions() { + if v == StartCharacter { + startPosition = p + break + } + } + + if startPosition == nil { + panic("unable to find start location") + } + + beamStartPosition := startPosition.Move(move.Down) + beamsStack := stack.New[position.Position]() + beamsStack.Push(beamStartPosition) + + positionsToTimelinesMap := map[position.Position]int{} + + for beamsStack.IsEmpty() == false { + currentBeam, _ := beamsStack.Peek() + + _, hasBeenProcessed := positionsToTimelinesMap[currentBeam] + + if hasBeenProcessed { + beamsStack.Pop() + continue + } + + value := grid.GetValueAt(currentBeam) + nextTimelinePositions := []position.Position{} + + if value == SplitterCharacter { + rightBeam := currentBeam.Move(move.Right) + leftBeam := currentBeam.Move(move.Left) + nextTimelinePositions = append(nextTimelinePositions, rightBeam, leftBeam) + } else { + nextBeam := currentBeam.Move(move.Down) + nextTimelinePositions = append(nextTimelinePositions, nextBeam) + } + + numberOfTimelinesForPosition := 0 + finalizePosition := true + + for _, newTimelinePosition := range nextTimelinePositions { + if grid.InBounds(newTimelinePosition) == false { + numberOfTimelinesForPosition++ + continue + } + + v, existing := positionsToTimelinesMap[newTimelinePosition] + + if existing { + numberOfTimelinesForPosition += v + continue + } + + beamsStack.Push(newTimelinePosition) + finalizePosition = false + } + + if finalizePosition == false { + continue + } + + beamsStack.Pop() + positionsToTimelinesMap[currentBeam] = numberOfTimelinesForPosition + } + + return positionsToTimelinesMap[beamStartPosition] +} diff --git a/cmd/07/main_test.go b/cmd/07/main_test.go index b8b49bc..b6b4b55 100644 --- a/cmd/07/main_test.go +++ b/cmd/07/main_test.go @@ -25,3 +25,23 @@ func TestSolvePartOneWithInput(t *testing.T) { t.Errorf("SolvePartOne() = %d; want %d", result, expected) } } + +func TestSolvePartTwoWithExampleInput(t *testing.T) { + expected := 40 + + result := solution.SolvePartTwo("EXAMPLE.txt") + + if result != expected { + t.Errorf("SolveTwoOne() = %d; want %d", result, expected) + } +} + +func TestSolvePartTwoWithInput(t *testing.T) { + expected := 73_007_003_089_792 + + result := solution.SolvePartTwo("INPUT.txt") + + if result != expected { + t.Errorf("SolveTwoOne() = %d; want %d", result, expected) + } +} diff --git a/internal/stack/stack.go b/internal/stack/stack.go new file mode 100644 index 0000000..49407fc --- /dev/null +++ b/internal/stack/stack.go @@ -0,0 +1,75 @@ +// Package stack provides a thread-safe LIFO stack implementation. +package stack + +import "sync" + +type Stack[T any] interface { + Push(item T) + Peek() (T, bool) + Pop() (T, bool) + IsEmpty() bool + Size() int +} + +type stack[T any] struct { + items []T + lock sync.Mutex +} + +func New[T any]() Stack[T] { + return &stack[T]{items: []T{}} +} + +func (s *stack[T]) Push(item T) { + s.lock.Lock() + defer s.lock.Unlock() + + s.items = append(s.items, item) +} + +func (s *stack[T]) Pop() (T, bool) { + s.lock.Lock() + defer s.lock.Unlock() + + length := len(s.items) + + if len(s.items) == 0 { + var zero T + return zero, false + } + + lastItemIndex := length - 1 + item := s.items[lastItemIndex] + s.items = s.items[:lastItemIndex] + return item, true +} + +func (s *stack[T]) Peek() (T, bool) { + s.lock.Lock() + defer s.lock.Unlock() + + length := len(s.items) + + if len(s.items) == 0 { + var zero T + return zero, false + } + + lastItemIndex := length - 1 + item := s.items[lastItemIndex] + return item, true +} + +func (s *stack[T]) IsEmpty() bool { + s.lock.Lock() + defer s.lock.Unlock() + + return len(s.items) == 0 +} + +func (s *stack[T]) Size() int { + s.lock.Lock() + defer s.lock.Unlock() + + return len(s.items) +} diff --git a/internal/stack/stack_test.go b/internal/stack/stack_test.go new file mode 100644 index 0000000..33c2615 --- /dev/null +++ b/internal/stack/stack_test.go @@ -0,0 +1 @@ +package stack_test