diff --git a/cmd/01/dial/dial.go b/cmd/01/dial/dial.go new file mode 100644 index 0000000..e75f273 --- /dev/null +++ b/cmd/01/dial/dial.go @@ -0,0 +1,48 @@ +// Package dial provides a model for representing a dial +package dial + +import ( + "github.com/StevanFreeborn/advent-of-code-2025/cmd/01/direction" + "github.com/StevanFreeborn/advent-of-code-2025/cmd/01/instruction" +) + +type Dial interface { + Value() int + Turn(instruction instruction.Instruction) +} + +type dial struct { + value int +} + +func New() Dial { + return &dial{ + value: 50, + } +} + +func (d *dial) Value() int { + return d.value +} + +func (d *dial) Turn(instruction instruction.Instruction) { + if instruction.Direction() == direction.Left { + d.value -= instruction.Distance() + + for d.value < 0 { + d.value += 100 + } + + return + } + + if instruction.Direction() == direction.Right { + d.value += instruction.Distance() + + for d.value > 99 { + d.value -= 100 + } + + return + } +} diff --git a/cmd/01/dial/dial_test.go b/cmd/01/dial/dial_test.go new file mode 100644 index 0000000..37c13cf --- /dev/null +++ b/cmd/01/dial/dial_test.go @@ -0,0 +1,78 @@ +package dial_test + +import ( + "testing" + + "github.com/StevanFreeborn/advent-of-code-2025/cmd/01/dial" + "github.com/StevanFreeborn/advent-of-code-2025/cmd/01/instruction" +) + +func TestNew(t *testing.T) { + expectedStartingValue := 50 + result := dial.New() + + if result.Value() != expectedStartingValue { + t.Errorf("got unexpected beginning value. expected %d but got %d", expectedStartingValue, result.Value()) + } +} + +type TurnTestCase struct { + Instruction instruction.Instruction + ExpectedDialValue int +} + +func TestTurn(t *testing.T) { + testCases := []TurnTestCase{ + { + Instruction: instruction.FromParts("L", 68), + ExpectedDialValue: 82, + }, + { + Instruction: instruction.FromParts("L", 30), + ExpectedDialValue: 52, + }, + { + Instruction: instruction.FromParts("R", 48), + ExpectedDialValue: 0, + }, + { + Instruction: instruction.FromParts("L", 5), + ExpectedDialValue: 95, + }, + { + Instruction: instruction.FromParts("R", 60), + ExpectedDialValue: 55, + }, + { + Instruction: instruction.FromParts("L", 55), + ExpectedDialValue: 0, + }, + { + Instruction: instruction.FromParts("L", 1), + ExpectedDialValue: 99, + }, + { + Instruction: instruction.FromParts("L", 99), + ExpectedDialValue: 0, + }, + { + Instruction: instruction.FromParts("R", 14), + ExpectedDialValue: 14, + }, + { + Instruction: instruction.FromParts("L", 82), + ExpectedDialValue: 32, + }, + } + + dial := dial.New() + + for i, testCase := range testCases { + + dial.Turn(testCase.Instruction) + + if dial.Value() != testCase.ExpectedDialValue { + t.Errorf("%d: got dial value %d, but expected dial value %d", i, dial.Value(), testCase.ExpectedDialValue) + } + } +} diff --git a/cmd/01/direction/direction.go b/cmd/01/direction/direction.go new file mode 100644 index 0000000..acdacdb --- /dev/null +++ b/cmd/01/direction/direction.go @@ -0,0 +1,5 @@ +// Package direction provides constants for left and right directions. +package direction + +const Left = "L" +const Right = "R" diff --git a/cmd/01/instruction/instruction.go b/cmd/01/instruction/instruction.go new file mode 100644 index 0000000..6537294 --- /dev/null +++ b/cmd/01/instruction/instruction.go @@ -0,0 +1,40 @@ +// Package instruction provides a model for representing dial instructions +package instruction + +import "strconv" + +type Instruction interface { + Direction() string + Distance() int +} + +type instruction struct { + direction string + distance int +} + +func FromLine(line string) Instruction { + dir := string(line[0]) + dist := line[1:] + distAsInt, _ := strconv.Atoi(dist) + + return instruction{ + direction: dir, + distance: distAsInt, + } +} + +func FromParts(direction string, distance int) Instruction { + return instruction{ + direction: direction, + distance: distance, + } +} + +func (i instruction) Direction() string { + return i.direction +} + +func (i instruction) Distance() int { + return i.distance +} diff --git a/cmd/01/instruction/instruction_test.go b/cmd/01/instruction/instruction_test.go new file mode 100644 index 0000000..6c3b1f3 --- /dev/null +++ b/cmd/01/instruction/instruction_test.go @@ -0,0 +1,39 @@ +package instruction_test + +import ( + "fmt" + "testing" + + "github.com/StevanFreeborn/advent-of-code-2025/cmd/01/instruction" +) + +func TestFromLine(t *testing.T) { + expectedDirection := "L" + expectedDistance := 7 + line := fmt.Sprintf("%s%d", expectedDirection, expectedDistance) + + result := instruction.FromLine(line) + + if result.Direction() != expectedDirection { + t.Errorf("got direction %s but expected direction %s", result.Direction(), expectedDirection) + } + + if result.Distance() != expectedDistance { + t.Errorf("got distance %d but expected distance %d", result.Distance(), expectedDistance) + } +} + +func TestFromParts(t *testing.T) { + expectedDirection := "L" + expectedDistance := 7 + + result := instruction.FromParts(expectedDirection, expectedDistance) + + if result.Direction() != expectedDirection { + t.Errorf("got direction %s but expected direction %s", result.Direction(), expectedDirection) + } + + if result.Distance() != expectedDistance { + t.Errorf("got distance %d but expected distance %d", result.Distance(), expectedDistance) + } +} diff --git a/cmd/01/main.go b/cmd/01/main.go index 5547ccb..2247a83 100644 --- a/cmd/01/main.go +++ b/cmd/01/main.go @@ -1,5 +1,28 @@ package main +import ( + "github.com/StevanFreeborn/advent-of-code-2025/cmd/01/dial" + "github.com/StevanFreeborn/advent-of-code-2025/cmd/01/instruction" + "github.com/StevanFreeborn/advent-of-code-2025/internal/file" +) + func SolvePartOne(filePath string) int { - return 0 + lines := file.ReadLines(filePath) + dial := dial.New() + total := 0 + + for _, line := range lines { + if line == "" { + continue + } + + instruction := instruction.FromLine(line) + dial.Turn(instruction) + + if dial.Value() == 0 { + total++ + } + } + + return total } diff --git a/cmd/01/main_test.go b/cmd/01/main_test.go index eabc50e..c69c8f5 100644 --- a/cmd/01/main_test.go +++ b/cmd/01/main_test.go @@ -7,11 +7,21 @@ import ( ) func TestSolvePartOneWithExampleInput(t *testing.T) { - expected := -1 + expected := 3 result := solution.SolvePartOne("EXAMPLE.txt") if result != expected { - t.Errorf("SolvePartOne() = %d; want %d", result, expected) + t.Errorf(`SolvePartOne("EXAMPLE.txt") = %d; want %d`, result, expected) + } +} + +func TestSolvePartOneWithInput(t *testing.T) { + expected := 1011 + + result := solution.SolvePartOne("INPUT.txt") + + if result != expected { + t.Errorf(`SolvePartOne("INPUT.txt") = %d; want %d`, result, expected) } } diff --git a/internal/file/file.go b/internal/file/file.go new file mode 100644 index 0000000..ce90c26 --- /dev/null +++ b/internal/file/file.go @@ -0,0 +1,28 @@ +// Package file provides useful methods for interacting with files +package file + +import ( + "bufio" + "fmt" + "os" +) + +func ReadLines(filePath string) []string { + lines := []string{} + + file, openErr := os.Open(filePath) + + if openErr != nil { + return lines + } + + scanner := bufio.NewScanner(file) + + for hasLine := scanner.Scan(); hasLine; hasLine = scanner.Scan() { + line := scanner.Text() + fmt.Println(line) + lines = append(lines, line) + } + + return lines +} diff --git a/internal/file/file_test.go b/internal/file/file_test.go new file mode 100644 index 0000000..3c1bd1b --- /dev/null +++ b/internal/file/file_test.go @@ -0,0 +1,21 @@ +package file_test + +import ( + "slices" + "testing" + + "github.com/StevanFreeborn/advent-of-code-2025/internal/file" +) + +func TestReadLines(t *testing.T) { + expectedLines := []string{ + "hello", + "world", + } + + result := file.ReadLines("test.txt") + + if slices.Equal(result, expectedLines) == false { + t.Errorf("got %s but expected %s", result, expectedLines) + } +} diff --git a/internal/file/test.txt b/internal/file/test.txt new file mode 100644 index 0000000..94954ab --- /dev/null +++ b/internal/file/test.txt @@ -0,0 +1,2 @@ +hello +world