refactor: extract grid abstraction for reuse
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
package direction_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/StevanFreeborn/advent-of-code-2025/cmd/01/direction"
|
||||
)
|
||||
|
||||
func TestDirectionConstants(t *testing.T) {
|
||||
if direction.Left != "L" {
|
||||
t.Errorf("Expected Left to be 'L', got '%s'", direction.Left)
|
||||
}
|
||||
|
||||
if direction.Right != "R" {
|
||||
t.Errorf("Expected Right to be 'R', got '%s'", direction.Right)
|
||||
}
|
||||
}
|
||||
+14
-54
@@ -1,78 +1,38 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
const PaperRollCharacter = "@"
|
||||
const EmptyCharacter = "."
|
||||
|
||||
type move struct {
|
||||
NumberOfRows int
|
||||
NumberOfColumns int
|
||||
}
|
||||
|
||||
var (
|
||||
up = move{NumberOfRows: -1, NumberOfColumns: 0}
|
||||
down = move{NumberOfRows: 1, NumberOfColumns: 0}
|
||||
right = move{NumberOfRows: 0, NumberOfColumns: 1}
|
||||
left = move{NumberOfRows: 0, NumberOfColumns: -1}
|
||||
upRight = move{NumberOfRows: -1, NumberOfColumns: 1}
|
||||
upLeft = move{NumberOfRows: -1, NumberOfColumns: -1}
|
||||
downRight = move{NumberOfRows: 1, NumberOfColumns: 1}
|
||||
downLeft = move{NumberOfRows: 1, NumberOfColumns: -1}
|
||||
)
|
||||
|
||||
var moves = []move{
|
||||
up,
|
||||
down,
|
||||
right,
|
||||
left,
|
||||
upRight,
|
||||
upLeft,
|
||||
downRight,
|
||||
downLeft,
|
||||
}
|
||||
|
||||
func SolvePartOne(filePath string) int {
|
||||
input := file.ReadAllLines(filePath)
|
||||
numberOfRows := len(input)
|
||||
numberOfColumns := len(input[0])
|
||||
grid := grid.From(input)
|
||||
|
||||
total := 0
|
||||
|
||||
for row := range numberOfRows {
|
||||
for column := range numberOfColumns {
|
||||
value := string(input[row][column])
|
||||
for row := range grid.NumberOfRows() {
|
||||
for column := range grid.NumberOfColumns() {
|
||||
currentPosition := position.From(row, column)
|
||||
value := grid.GetValueAt(currentPosition)
|
||||
|
||||
if value != PaperRollCharacter {
|
||||
continue
|
||||
}
|
||||
|
||||
numberOfPaperRolls := 0
|
||||
sameNeighbors := grid.GetSameNeighborsOf(currentPosition, move.AllDirections)
|
||||
|
||||
for _, move := range moves {
|
||||
neighborRow := row + move.NumberOfRows
|
||||
neighborColumn := column + move.NumberOfColumns
|
||||
|
||||
if neighborRow > numberOfRows-1 || neighborRow < 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if neighborColumn > numberOfColumns-1 || neighborColumn < 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
neighborValue := string(input[neighborRow][neighborColumn])
|
||||
|
||||
if neighborValue != PaperRollCharacter {
|
||||
continue
|
||||
}
|
||||
|
||||
numberOfPaperRolls++
|
||||
if row == 0 && column == 3 {
|
||||
fmt.Println(sameNeighbors)
|
||||
}
|
||||
|
||||
if numberOfPaperRolls < 4 {
|
||||
if len(sameNeighbors) < 4 {
|
||||
total++
|
||||
}
|
||||
}
|
||||
|
||||
+60
-67
@@ -1,104 +1,97 @@
|
||||
package grid
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/StevanFreeborn/advent-of-code-2025/internal/move"
|
||||
"github.com/StevanFreeborn/advent-of-code-2025/internal/position"
|
||||
)
|
||||
|
||||
type Grid interface {
|
||||
Positions() map[Position]string
|
||||
Walk()
|
||||
NumberOfRows() int
|
||||
NumberOfColumns() int
|
||||
InBounds(position.Position) bool
|
||||
GetValueAt(position.Position) string
|
||||
GetSameNeighborsOf(position.Position, []move.Move) []position.Position
|
||||
}
|
||||
|
||||
type grid struct {
|
||||
positions map[Position]string
|
||||
numberOfRows int
|
||||
numberOfColumns int
|
||||
positions map[position.Position]string
|
||||
}
|
||||
|
||||
func NewGrid(input []string) Grid {
|
||||
func From(input []string) Grid {
|
||||
numberOfRows := len(input)
|
||||
numberOfColumns := len(input[0])
|
||||
positions := map[Position]string{}
|
||||
positions := map[position.Position]string{}
|
||||
|
||||
for row := range numberOfRows {
|
||||
for column := range numberOfColumns {
|
||||
value := string(input[row][column])
|
||||
pos := NewPosition(row, column)
|
||||
pos := position.From(row, column)
|
||||
positions[pos] = value
|
||||
}
|
||||
}
|
||||
|
||||
return grid{
|
||||
positions: positions,
|
||||
numberOfRows: numberOfRows,
|
||||
numberOfColumns: numberOfColumns,
|
||||
positions: positions,
|
||||
}
|
||||
}
|
||||
|
||||
func (g grid) Positions() map[Position]string {
|
||||
positions := map[Position]string{}
|
||||
func (g grid) NumberOfRows() int {
|
||||
return g.numberOfRows
|
||||
}
|
||||
|
||||
for key, value := range g.positions {
|
||||
newPosition := NewPosition(key.Row(), key.Column())
|
||||
positions[newPosition] = value
|
||||
func (g grid) NumberOfColumns() int {
|
||||
return g.numberOfColumns
|
||||
}
|
||||
|
||||
func (g grid) InBounds(pos position.Position) bool {
|
||||
if pos.Row() < 0 || pos.Row() >= g.numberOfRows {
|
||||
return false
|
||||
}
|
||||
|
||||
return positions
|
||||
}
|
||||
|
||||
type Position interface {
|
||||
Column() int
|
||||
Row() int
|
||||
}
|
||||
|
||||
type position struct {
|
||||
column int
|
||||
row int
|
||||
}
|
||||
|
||||
func NewPosition(row int, column int) Position {
|
||||
return position{
|
||||
row: row,
|
||||
column: column,
|
||||
if pos.Column() < 0 || pos.Column() >= g.numberOfColumns {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (p position) Row() int {
|
||||
return p.row
|
||||
func (g grid) GetValueAt(pos position.Position) string {
|
||||
return g.positions[pos]
|
||||
}
|
||||
|
||||
func (p position) Column() int {
|
||||
return p.column
|
||||
}
|
||||
func (g grid) GetSameNeighborsOf(pos position.Position, moves []move.Move) []position.Position {
|
||||
similarNeighbors := []position.Position{}
|
||||
originalValue := g.GetValueAt(pos)
|
||||
|
||||
type Move interface {
|
||||
NumberOfRows() int
|
||||
NumberOfColumns() int
|
||||
}
|
||||
fmt.Println("Moves to check:", moves)
|
||||
for _, m := range moves {
|
||||
postionRow := pos.Row()
|
||||
positionColumn := pos.Column()
|
||||
|
||||
type move struct {
|
||||
numberOfRows int
|
||||
numberOfColumns int
|
||||
}
|
||||
fmt.Println("Checking move:", m, "from position:", pos)
|
||||
fmt.Println("Move rows:", m.NumberOfRows(), "columns:", m.NumberOfColumns())
|
||||
neighborRow := postionRow + m.NumberOfRows()
|
||||
neighborColumn := positionColumn + m.NumberOfColumns()
|
||||
neighborPos := position.From(neighborRow, neighborColumn)
|
||||
|
||||
func (m move) NumberOfRows() int {
|
||||
return m.numberOfColumns
|
||||
}
|
||||
if g.InBounds(neighborPos) == false {
|
||||
continue
|
||||
}
|
||||
|
||||
func (m move) NumberOfColumns() int {
|
||||
return m.numberOfColumns
|
||||
}
|
||||
neighborValue := g.GetValueAt(neighborPos)
|
||||
|
||||
var (
|
||||
Up = move{numberOfRows: -1, numberOfColumns: 0}
|
||||
Down = move{numberOfRows: 1, numberOfColumns: 0}
|
||||
Right = move{numberOfRows: 0, numberOfColumns: 1}
|
||||
Left = move{numberOfRows: 0, numberOfColumns: -1}
|
||||
UpRight = move{numberOfRows: -1, numberOfColumns: 1}
|
||||
UpLeft = move{numberOfRows: -1, numberOfColumns: -1}
|
||||
DownRight = move{numberOfRows: 1, numberOfColumns: 1}
|
||||
DownLeft = move{numberOfRows: 1, numberOfColumns: -1}
|
||||
)
|
||||
if neighborValue != originalValue {
|
||||
continue
|
||||
}
|
||||
|
||||
var AllMoves = []Move{
|
||||
Up,
|
||||
Down,
|
||||
Right,
|
||||
Left,
|
||||
UpRight,
|
||||
UpLeft,
|
||||
DownRight,
|
||||
DownLeft,
|
||||
similarNeighbors = append(similarNeighbors, neighborPos)
|
||||
}
|
||||
|
||||
return similarNeighbors
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
..........
|
||||
..........
|
||||
..........
|
||||
..........
|
||||
..........
|
||||
..........
|
||||
..........
|
||||
..........
|
||||
..........
|
||||
..........
|
||||
@@ -1 +1,152 @@
|
||||
package grid_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
func TestFrom(t *testing.T) {
|
||||
expectedNumberOfRows := 3
|
||||
expectedNumberOfColumns := 3
|
||||
expectedPositions := map[position.Position]string{
|
||||
position.From(0, 0): "@",
|
||||
position.From(0, 1): ".",
|
||||
position.From(0, 2): ".",
|
||||
position.From(1, 0): ".",
|
||||
position.From(1, 1): "@",
|
||||
position.From(1, 2): ".",
|
||||
position.From(2, 0): ".",
|
||||
position.From(2, 1): ".",
|
||||
position.From(2, 2): "@",
|
||||
}
|
||||
|
||||
input := []string{
|
||||
"@..",
|
||||
".@.",
|
||||
"..@",
|
||||
}
|
||||
|
||||
g := grid.From(input)
|
||||
|
||||
if g.NumberOfRows() != expectedNumberOfRows {
|
||||
t.Errorf("expected number of rows to be %d, got %d", expectedNumberOfRows, g.NumberOfRows())
|
||||
}
|
||||
|
||||
if g.NumberOfColumns() != expectedNumberOfColumns {
|
||||
t.Errorf("expected number of columns to be %d, got %d", expectedNumberOfColumns, g.NumberOfColumns())
|
||||
}
|
||||
|
||||
for pos, expectedValue := range expectedPositions {
|
||||
value := g.GetValueAt(pos)
|
||||
|
||||
if value != expectedValue {
|
||||
t.Errorf("expected value at position (%d, %d) to be %s, got %s", pos.Row(), pos.Column(), expectedValue, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInBounds(t *testing.T) {
|
||||
tests := []struct {
|
||||
pos position.Position
|
||||
expected bool
|
||||
}{
|
||||
{position.From(0, 0), true},
|
||||
{position.From(1, 1), true},
|
||||
{position.From(2, 2), true},
|
||||
{position.From(-1, 0), false},
|
||||
{position.From(0, -1), false},
|
||||
{position.From(3, 0), false},
|
||||
{position.From(0, 3), false},
|
||||
}
|
||||
|
||||
input := []string{
|
||||
"@..",
|
||||
".@.",
|
||||
"..@",
|
||||
}
|
||||
|
||||
g := grid.From(input)
|
||||
|
||||
for _, test := range tests {
|
||||
result := g.InBounds(test.pos)
|
||||
|
||||
if result != test.expected {
|
||||
t.Errorf("expected InBounds(%v) to be %v, got %v", test.pos, test.expected, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetValueAt(t *testing.T) {
|
||||
tests := []struct {
|
||||
pos position.Position
|
||||
expected string
|
||||
}{
|
||||
{position.From(0, 0), "@"},
|
||||
{position.From(0, 1), "."},
|
||||
{position.From(1, 1), "@"},
|
||||
{position.From(2, 2), "@"},
|
||||
}
|
||||
|
||||
input := []string{
|
||||
"@..",
|
||||
".@.",
|
||||
"..@",
|
||||
}
|
||||
|
||||
g := grid.From(input)
|
||||
|
||||
for _, test := range tests {
|
||||
result := g.GetValueAt(test.pos)
|
||||
|
||||
if result != test.expected {
|
||||
t.Errorf("expected GetValueAt(%v) to be %s, got %s", test.pos, test.expected, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSameNeighborsOf(t *testing.T) {
|
||||
input := []string{
|
||||
"@..",
|
||||
".@.",
|
||||
"..@",
|
||||
}
|
||||
|
||||
g := grid.From(input)
|
||||
|
||||
tests := []struct {
|
||||
pos position.Position
|
||||
expected []position.Position
|
||||
}{
|
||||
{
|
||||
position.From(1, 1),
|
||||
[]position.Position{
|
||||
position.From(0, 0),
|
||||
position.From(2, 2),
|
||||
},
|
||||
},
|
||||
{
|
||||
position.From(0, 0),
|
||||
[]position.Position{
|
||||
position.From(1, 1),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
result := g.GetSameNeighborsOf(test.pos, move.AllDirections)
|
||||
|
||||
if len(result) != len(test.expected) {
|
||||
t.Errorf("expected GetSameNeighborsOf(%v) to return %d neighbors, got %d", test.pos, len(test.expected), len(result))
|
||||
continue
|
||||
}
|
||||
|
||||
for i, expectedPos := range test.expected {
|
||||
if result[i] != expectedPos {
|
||||
t.Errorf("expected neighbor at index %d to be %v, got %v", i, expectedPos, result[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
// Package move provides types and functions to represent and manipulate moves on a 2D grid.
|
||||
package move
|
||||
|
||||
type Move interface {
|
||||
NumberOfRows() int
|
||||
NumberOfColumns() int
|
||||
}
|
||||
|
||||
type move struct {
|
||||
numberOfRows int
|
||||
numberOfColumns int
|
||||
}
|
||||
|
||||
func (m move) NumberOfRows() int {
|
||||
return m.numberOfRows
|
||||
}
|
||||
|
||||
func (m move) NumberOfColumns() int {
|
||||
return m.numberOfColumns
|
||||
}
|
||||
|
||||
var (
|
||||
Up = move{numberOfRows: -1, numberOfColumns: 0}
|
||||
Down = move{numberOfRows: 1, numberOfColumns: 0}
|
||||
Right = move{numberOfRows: 0, numberOfColumns: 1}
|
||||
Left = move{numberOfRows: 0, numberOfColumns: -1}
|
||||
cardinal = []Move{Up, Down, Right, Left}
|
||||
|
||||
UpRight = move{numberOfRows: -1, numberOfColumns: 1}
|
||||
UpLeft = move{numberOfRows: -1, numberOfColumns: -1}
|
||||
DownRight = move{numberOfRows: 1, numberOfColumns: 1}
|
||||
DownLeft = move{numberOfRows: 1, numberOfColumns: -1}
|
||||
ordinal = []Move{UpRight, UpLeft, DownRight, DownLeft}
|
||||
|
||||
AllDirections = append(cardinal, ordinal...)
|
||||
)
|
||||
@@ -0,0 +1,91 @@
|
||||
package move_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/StevanFreeborn/advent-of-code-2025/internal/move"
|
||||
)
|
||||
|
||||
func TestNumberOfRows(t *testing.T) {
|
||||
tests := []struct {
|
||||
move move.Move
|
||||
expected int
|
||||
}{
|
||||
{move.Up, -1},
|
||||
{move.Down, 1},
|
||||
{move.Right, 0},
|
||||
{move.Left, 0},
|
||||
{move.UpRight, -1},
|
||||
{move.UpLeft, -1},
|
||||
{move.DownRight, 1},
|
||||
{move.DownLeft, 1},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
if test.move.NumberOfRows() != test.expected {
|
||||
t.Errorf("expected number of rows for move to be %d, got %d", test.expected, test.move.NumberOfRows())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNumberOfColumns(t *testing.T) {
|
||||
tests := []struct {
|
||||
move move.Move
|
||||
expected int
|
||||
}{
|
||||
{move.Up, 0},
|
||||
{move.Down, 0},
|
||||
{move.Right, 1},
|
||||
{move.Left, -1},
|
||||
{move.UpRight, 1},
|
||||
{move.UpLeft, -1},
|
||||
{move.DownRight, 1},
|
||||
{move.DownLeft, -1},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
if test.move.NumberOfColumns() != test.expected {
|
||||
t.Errorf("expected number of columns for move to be %d, got %d", test.expected, test.move.NumberOfColumns())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllDirectionsLength(t *testing.T) {
|
||||
expectedLength := 8
|
||||
|
||||
if len(move.AllDirections) != expectedLength {
|
||||
t.Errorf("expected AllDirections length to be %d, got %d", expectedLength, len(move.AllDirections))
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefinedMoves(t *testing.T) {
|
||||
definedMoves := []struct {
|
||||
name string
|
||||
move move.Move
|
||||
expectedRows int
|
||||
expectedColumns int
|
||||
}{
|
||||
{"Up", move.Up, -1, 0},
|
||||
{"Down", move.Down, 1, 0},
|
||||
{"Right", move.Right, 0, 1},
|
||||
{"Left", move.Left, 0, -1},
|
||||
{"UpRight", move.UpRight, -1, 1},
|
||||
{"UpLeft", move.UpLeft, -1, -1},
|
||||
{"DownRight", move.DownRight, 1, 1},
|
||||
{"DownLeft", move.DownLeft, 1, -1},
|
||||
}
|
||||
|
||||
for _, dm := range definedMoves {
|
||||
if dm.move == nil {
|
||||
t.Errorf("expected move %s to be defined, got nil", dm.name)
|
||||
}
|
||||
|
||||
if dm.move.NumberOfRows() != dm.expectedRows {
|
||||
t.Errorf("expected move %s to have %d rows, got %d", dm.name, dm.expectedRows, dm.move.NumberOfRows())
|
||||
}
|
||||
|
||||
if dm.move.NumberOfColumns() != dm.expectedColumns {
|
||||
t.Errorf("expected move %s to have %d columns, got %d", dm.name, dm.expectedColumns, dm.move.NumberOfColumns())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// Package position provides types and functions to represent and manipulate positions and moves on a 2D grid.
|
||||
package position
|
||||
|
||||
type Position interface {
|
||||
Row() int
|
||||
Column() int
|
||||
}
|
||||
|
||||
type position struct {
|
||||
row int
|
||||
column int
|
||||
}
|
||||
|
||||
func From(row int, column int) Position {
|
||||
return position{
|
||||
row: row,
|
||||
column: column,
|
||||
}
|
||||
}
|
||||
|
||||
func (p position) Row() int {
|
||||
return p.row
|
||||
}
|
||||
|
||||
func (p position) Column() int {
|
||||
return p.column
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package position_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/StevanFreeborn/advent-of-code-2025/internal/position"
|
||||
)
|
||||
|
||||
func TestFrom(t *testing.T) {
|
||||
row := 3
|
||||
column := 5
|
||||
|
||||
pos := position.From(row, column)
|
||||
|
||||
if pos.Row() != row {
|
||||
t.Errorf("expected row to be %d, got %d", row, pos.Row())
|
||||
}
|
||||
|
||||
if pos.Column() != column {
|
||||
t.Errorf("expected column to be %d, got %d", column, pos.Column())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user