Merge pull request #10 from StevanFreeborn/stevanfreeborn/refactor/add-grid-package
refactor: add grid package
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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/StevanFreeborn/advent-of-code-2025/internal/file"
|
"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 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 {
|
func SolvePartOne(filePath string) int {
|
||||||
input := file.ReadAllLines(filePath)
|
input := file.ReadAllLines(filePath)
|
||||||
numberOfRows := len(input)
|
grid := grid.From(input)
|
||||||
numberOfColumns := len(input[0])
|
|
||||||
|
|
||||||
total := 0
|
total := 0
|
||||||
|
|
||||||
for row := range numberOfRows {
|
for row := range grid.NumberOfRows() {
|
||||||
for column := range numberOfColumns {
|
for column := range grid.NumberOfColumns() {
|
||||||
value := string(input[row][column])
|
currentPosition := position.From(row, column)
|
||||||
|
value := grid.GetValueAt(currentPosition)
|
||||||
|
|
||||||
if value != PaperRollCharacter {
|
if value != PaperRollCharacter {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
numberOfPaperRolls := 0
|
sameNeighbors := grid.GetSameNeighborsOf(currentPosition, move.AllDirections)
|
||||||
|
|
||||||
for _, move := range moves {
|
if row == 0 && column == 3 {
|
||||||
neighborRow := row + move.NumberOfRows
|
fmt.Println(sameNeighbors)
|
||||||
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 numberOfPaperRolls < 4 {
|
if len(sameNeighbors) < 4 {
|
||||||
total++
|
total++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +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 {
|
||||||
|
NumberOfRows() int
|
||||||
|
NumberOfColumns() int
|
||||||
|
InBounds(position.Position) bool
|
||||||
|
GetValueAt(position.Position) string
|
||||||
|
GetSameNeighborsOf(position.Position, []move.Move) []position.Position
|
||||||
|
}
|
||||||
|
|
||||||
|
type grid struct {
|
||||||
|
numberOfRows int
|
||||||
|
numberOfColumns int
|
||||||
|
positions map[position.Position]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func From(input []string) Grid {
|
||||||
|
numberOfRows := len(input)
|
||||||
|
numberOfColumns := len(input[0])
|
||||||
|
positions := map[position.Position]string{}
|
||||||
|
|
||||||
|
for row := range numberOfRows {
|
||||||
|
for column := range numberOfColumns {
|
||||||
|
value := string(input[row][column])
|
||||||
|
pos := position.From(row, column)
|
||||||
|
positions[pos] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return grid{
|
||||||
|
numberOfRows: numberOfRows,
|
||||||
|
numberOfColumns: numberOfColumns,
|
||||||
|
positions: positions,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g grid) NumberOfRows() int {
|
||||||
|
return g.numberOfRows
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
if pos.Column() < 0 || pos.Column() >= g.numberOfColumns {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g grid) GetValueAt(pos position.Position) string {
|
||||||
|
return g.positions[pos]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g grid) GetSameNeighborsOf(pos position.Position, moves []move.Move) []position.Position {
|
||||||
|
similarNeighbors := []position.Position{}
|
||||||
|
originalValue := g.GetValueAt(pos)
|
||||||
|
|
||||||
|
fmt.Println("Moves to check:", moves)
|
||||||
|
for _, m := range moves {
|
||||||
|
postionRow := pos.Row()
|
||||||
|
positionColumn := pos.Column()
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
if g.InBounds(neighborPos) == false {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
neighborValue := g.GetValueAt(neighborPos)
|
||||||
|
|
||||||
|
if neighborValue != originalValue {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
similarNeighbors = append(similarNeighbors, neighborPos)
|
||||||
|
}
|
||||||
|
|
||||||
|
return similarNeighbors
|
||||||
|
}
|
||||||
@@ -0,0 +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