Merge pull request #17 from StevanFreeborn/stevanfreeborn/refactor/use-grid-in-day-seven-part-one

refactor: use internal packages
This commit is contained in:
Stevan Freeborn
2025-12-13 07:42:37 -06:00
committed by GitHub
5 changed files with 188 additions and 28 deletions
+27 -28
View File
@@ -2,7 +2,10 @@ package main
import (
"github.com/StevanFreeborn/advent-of-code-2025/internal/file"
"github.com/StevanFreeborn/advent-of-code-2025/internal/grid"
"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"
)
const StartCharacter = "S"
@@ -10,66 +13,62 @@ const SplitterCharacter = "^"
func SolvePartOne(filePath string) int {
input := file.ReadAllLines(filePath)
numOfRows := len(input)
numOfCols := len(input[0])
grid := grid.From(input)
startRow := -1
startCol := -1
var startPosition position.Position
for row := range numOfRows - 1 {
for col := range numOfCols - 1 {
v := string(input[row][col])
for row := range grid.NumberOfRows() - 1 {
for col := range grid.NumberOfColumns() - 1 {
current := position.From(row, col)
v := grid.GetValueAt(current)
if v == StartCharacter {
startRow = row
startCol = col
startPosition = current
break
}
}
}
if startRow < 0 || startCol < 0 {
if startPosition == nil {
panic("unable to find start location")
}
beamStart := position.From(startRow+1, startCol)
beamsQueue := []position.Position{
beamStart,
}
beamStart := startPosition.Move(move.Down)
beamsQueue := queue.New[position.Position]()
beamsQueue.Enqueue(beamStart)
visited := map[position.Position]bool{}
total := 0
for len(beamsQueue) > 0 {
currentBeam := beamsQueue[0]
beamsQueue = beamsQueue[1:]
for beamsQueue.IsEmpty() == false {
currentBeam, _ := beamsQueue.Dequeue()
if visited[currentBeam] {
continue
}
if currentBeam.Row() < 0 || currentBeam.Row() >= numOfRows {
continue
}
if currentBeam.Column() < 0 || currentBeam.Column() >= numOfCols {
if grid.InBounds(currentBeam) == false {
continue
}
visited[currentBeam] = true
value := string(input[currentBeam.Row()][currentBeam.Column()])
value := grid.GetValueAt(currentBeam)
if value == SplitterCharacter {
total++
rightBeam := position.From(currentBeam.Row(), currentBeam.Column()+1)
leftBeam := position.From(currentBeam.Row(), currentBeam.Column()-1)
beamsQueue = append(beamsQueue, rightBeam, leftBeam)
rightBeam := currentBeam.Move(move.Right)
leftBeam := currentBeam.Move(move.Left)
beamsQueue.Enqueue(rightBeam)
beamsQueue.Enqueue(leftBeam)
continue
}
nextBeam := position.From(currentBeam.Row()+1, currentBeam.Column())
beamsQueue = append(beamsQueue, nextBeam)
nextBeam := currentBeam.Move(move.Down)
beamsQueue.Enqueue(nextBeam)
}
return total
+7
View File
@@ -1,9 +1,12 @@
// Package position provides types and functions to represent and manipulate positions and moves on a 2D grid.
package position
import "github.com/StevanFreeborn/advent-of-code-2025/internal/move"
type Position interface {
Row() int
Column() int
Move(move.Move) Position
}
type position struct {
@@ -25,3 +28,7 @@ func (p position) Row() int {
func (p position) Column() int {
return p.column
}
func (p position) Move(move move.Move) Position {
return From(p.row+move.NumberOfRows(), p.column+move.NumberOfColumns())
}
+12
View File
@@ -3,6 +3,7 @@ package position_test
import (
"testing"
"github.com/StevanFreeborn/advent-of-code-2025/internal/move"
"github.com/StevanFreeborn/advent-of-code-2025/internal/position"
)
@@ -20,3 +21,14 @@ func TestFrom(t *testing.T) {
t.Errorf("expected column to be %d, got %d", column, pos.Column())
}
}
func TestMove(t *testing.T) {
expectedPos := position.From(3, 4)
startPos := position.From(2, 4)
result := startPos.Move(move.Down)
if result != expectedPos {
t.Errorf("expected position to be %+v, got %+v", expectedPos, result)
}
}
+55
View File
@@ -0,0 +1,55 @@
// Package queue provides a thread-safe FIFO queue implementation.
package queue
import "sync"
type Queue[T any] interface {
Enqueue(item T)
Dequeue() (T, bool)
IsEmpty() bool
Size() int
}
type queue[T any] struct {
items []T
lock sync.Mutex
}
func New[T any]() Queue[T] {
return &queue[T]{items: []T{}}
}
func (q *queue[T]) Enqueue(item T) {
q.lock.Lock()
defer q.lock.Unlock()
q.items = append(q.items, item)
}
func (q *queue[T]) Dequeue() (T, bool) {
q.lock.Lock()
defer q.lock.Unlock()
if len(q.items) == 0 {
var zero T
return zero, false
}
item := q.items[0]
q.items = q.items[1:]
return item, true
}
func (q *queue[T]) IsEmpty() bool {
q.lock.Lock()
defer q.lock.Unlock()
return len(q.items) == 0
}
func (q *queue[T]) Size() int {
q.lock.Lock()
defer q.lock.Unlock()
return len(q.items)
}
+87
View File
@@ -0,0 +1,87 @@
package queue_test
import (
"testing"
"github.com/StevanFreeborn/advent-of-code-2025/internal/queue"
)
func TestQueue_EnqueueDequeue(t *testing.T) {
q := queue.New[int]()
q.Enqueue(1)
q.Enqueue(2)
q.Enqueue(3)
expectedValues := []int{1, 2, 3}
for _, expected := range expectedValues {
value, ok := q.Dequeue()
if !ok {
t.Errorf("expected dequeue to be successful, but it failed")
}
if value != expected {
t.Errorf("expected value to be %d, got %d", expected, value)
}
}
_, ok := q.Dequeue()
if ok {
t.Errorf("expected dequeue to fail on empty queue, but it succeeded")
}
if !q.IsEmpty() {
t.Errorf("expected queue to be empty, but it is not")
}
}
func TestQueue_Size(t *testing.T) {
q := queue.New[string]()
if q.Size() != 0 {
t.Errorf("expected size to be 0, got %d", q.Size())
}
q.Enqueue("a")
q.Enqueue("b")
if q.Size() != 2 {
t.Errorf("expected size to be 2, got %d", q.Size())
}
q.Dequeue()
if q.Size() != 1 {
t.Errorf("expected size to be 1, got %d", q.Size())
}
}
func TestQueue_ConcurrentAccess(t *testing.T) {
q := queue.New[int]()
done := make(chan bool)
numGoroutines := 100
numItemsPerGoroutine := 100
for i := range numGoroutines {
go func(start int) {
for j := range numItemsPerGoroutine {
q.Enqueue(start + j)
}
done <- true
}(i * numItemsPerGoroutine)
}
for range numGoroutines {
<-done
}
expectedSize := numGoroutines * numItemsPerGoroutine
if q.Size() != expectedSize {
t.Errorf("expected size to be %d, got %d", expectedSize, q.Size())
}
}