-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Move Go solutions and add a new Go framework
- Loading branch information
Showing
10 changed files
with
361 additions
and
207 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package main | ||
|
||
// P201717 solves 2017/17. | ||
type P201717 struct { | ||
steps int | ||
} | ||
|
||
type node struct { | ||
val int | ||
next *node | ||
} | ||
|
||
func partOne(step int) int { | ||
list := &node{0, nil} | ||
list.next = list | ||
|
||
for size := 1; size <= 2017; size++ { | ||
stepsNeeded := step % size | ||
for i := 0; i < stepsNeeded; i++ { | ||
list = list.next | ||
} | ||
newNode := &node{size, list.next} | ||
list.next = newNode | ||
list = list.next | ||
} | ||
|
||
return list.next.val | ||
} | ||
|
||
func partTwo(step int) int { | ||
lastVal := 0 | ||
pos := 0 | ||
|
||
for size := 1; size <= 50000000; size++ { | ||
pos = ((pos + step) % size) + 1 | ||
if pos == 1 { | ||
lastVal = size | ||
} | ||
} | ||
|
||
return lastVal | ||
} | ||
|
||
// New201717 returns a new solver for 2017/17. | ||
func New201717() *P201717 { | ||
return &P201717{} | ||
} | ||
|
||
// SetInput handles input for this solver. | ||
func (p *P201717) SetInput(data string) { | ||
p.steps = Atoi(data) | ||
} | ||
|
||
// Solve returns the solution for one part. | ||
func (p *P201717) Solve(part int) string { | ||
m := []func(int) int{partOne, partTwo}[part] | ||
return Itoa(m(p.steps)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package main | ||
|
||
import ( | ||
"maps" | ||
"slices" | ||
"strings" | ||
) | ||
|
||
type virusState int | ||
|
||
// P201722 solves 2017/22. | ||
type P201722 struct { | ||
nodes map[Location]virusState | ||
center int | ||
} | ||
|
||
const ( | ||
clean virusState = iota | ||
weakened virusState = iota | ||
infected virusState = iota | ||
flagged virusState = iota | ||
) | ||
|
||
var ( | ||
steps = []int{10000, 10000000} | ||
nextState = []map[virusState]virusState{ | ||
{clean: infected, infected: clean}, | ||
{clean: weakened, weakened: infected, infected: flagged, flagged: clean}, | ||
} | ||
rotations = map[virusState]Rotation{clean: RotateLeft, weakened: RotateStraight, infected: RotateRight, flagged: RotateReverse} | ||
) | ||
|
||
type simulation struct { | ||
*Robot | ||
nodes map[Location]virusState | ||
infected int | ||
} | ||
|
||
func (s *simulation) run(steps int, states map[virusState]virusState) { | ||
for range steps { | ||
state, ok := s.nodes[s.Location] | ||
if !ok { | ||
state = clean | ||
} | ||
s.Direction.Rotate(rotations[state]) | ||
s.nodes[s.Location] = states[state] | ||
if s.nodes[s.Location] == infected { | ||
s.infected++ | ||
} | ||
s.Robot.Advance() | ||
} | ||
} | ||
|
||
// New201722 returns a new solver for 2017/22. | ||
func New201722() *P201722 { | ||
return &P201722{} | ||
} | ||
|
||
// SetInput handles input for this solver. | ||
func (p *P201722) SetInput(data string) { | ||
lines := strings.Split(data, "\n") | ||
slices.Reverse(lines) | ||
p.nodes = make(map[Location]virusState) | ||
for y, line := range lines { | ||
for x, char := range line { | ||
if char == '#' { | ||
p.nodes[Location{x, y}] = infected | ||
} | ||
} | ||
} | ||
p.center = (len(lines) - 1) / 2 | ||
} | ||
|
||
// Solve returns the solution for one part. | ||
func (p *P201722) Solve(part int) string { | ||
s := simulation{ | ||
Robot: &Robot{Location{p.center, p.center}, Direction{0, 1}}, | ||
nodes: maps.Clone(p.nodes), | ||
infected: 0, | ||
} | ||
s.run(steps[part], nextState[part]) | ||
return Itoa(s.infected) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package main | ||
|
||
import ( | ||
"strings" | ||
) | ||
|
||
const ( | ||
target = 2020 | ||
) | ||
|
||
func check(err error) { | ||
if err != nil { | ||
panic(err) | ||
} | ||
} | ||
|
||
// P202001 solves 2020/01. | ||
type P202001 struct { | ||
nums map[int]bool | ||
} | ||
|
||
// New202001 returns a new solver for 2020/01. | ||
func New202001() *P202001 { | ||
return &P202001{} | ||
} | ||
|
||
// SetInput handles input for this solver. | ||
func (p *P202001) SetInput(data string) { | ||
p.nums = make(map[int]bool) | ||
for _, s := range strings.Split(string(data), "\n") { | ||
if s == "" { | ||
continue | ||
} | ||
n := Atoi(s) | ||
p.nums[n] = true | ||
} | ||
} | ||
|
||
func (p *P202001) partOne() int { | ||
for n := range p.nums { | ||
if _, has := p.nums[target-n]; has != false { | ||
return n * (target - n) | ||
} | ||
} | ||
panic("no solution found") | ||
} | ||
func (p *P202001) partTwo() int { | ||
for n := range p.nums { | ||
for o := range p.nums { | ||
t := target - n - o | ||
if _, has := p.nums[t]; has != false { | ||
return n * o * t | ||
} | ||
} | ||
} | ||
panic("no solution found") | ||
} | ||
|
||
// Solve returns the solution for one part. | ||
func (p *P202001) Solve(part int) string { | ||
m := []func() int{p.partOne, p.partTwo}[part] | ||
return Itoa(m()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package main | ||
|
||
import "os" | ||
|
||
var puzzles = map[Puzzle]Solver{ | ||
Puzzle{2017, 17}: New201717(), | ||
Puzzle{2017, 22}: New201722(), | ||
Puzzle{2020, 1}: New202001(), | ||
} | ||
|
||
func main() { | ||
if len(os.Args) == 3 { | ||
p := Puzzle{Atoi(os.Args[1]), Atoi(os.Args[2])} | ||
p.Check(puzzles[p]) | ||
} else { | ||
for puzzle, solver := range puzzles { | ||
puzzle.Check(solver) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"io/ioutil" | ||
"strings" | ||
"time" | ||
) | ||
|
||
// Solver is able to solve the puzzle for a day. | ||
type Solver interface { | ||
Solve(int) string | ||
SetInput(string) | ||
} | ||
|
||
// Puzzle is the challenge for a day. | ||
type Puzzle struct { | ||
year, day int | ||
} | ||
|
||
// ReadData returns the puzzle input data. | ||
func (p Puzzle) ReadData() string { | ||
filename := fmt.Sprintf("../inputs/%d.%02d.txt", p.year, p.day) | ||
data, err := ioutil.ReadFile(filename) | ||
if err != nil { | ||
panic("Failed to read file") | ||
} | ||
return strings.TrimRight(string(data), "\n") | ||
} | ||
|
||
// Check checks if a Solver can solve a puzzle. | ||
func (p Puzzle) Check(solver Solver) { | ||
solutions, err := p.Solutions() | ||
if err != nil { | ||
fmt.Println("failed to load solutions: %v", err) | ||
} | ||
solver.SetInput(p.ReadData()) | ||
for i := 0; i < 2; i++ { | ||
start := time.Now() | ||
got := solver.Solve(i) | ||
elapsed := time.Now().Sub(start) | ||
if got == solutions[i] { | ||
fmt.Printf("%d/%02d.%d PASSED! %12s\n", p.year, p.day, i+1, elapsed) | ||
} else { | ||
fmt.Printf("%d/%02d.%d FAILED!\n", p.year, p.day, i+1) | ||
fmt.Printf("want %s but got %s\n", solutions[i], got) | ||
} | ||
} | ||
} | ||
|
||
// Solutions returns the solutions from the solution file. | ||
func (p Puzzle) Solutions() ([]string, error) { | ||
filename := fmt.Sprintf("../solutions/%d.txt", p.year) | ||
day := fmt.Sprintf("%d", p.day) | ||
data, err := ioutil.ReadFile(filename) | ||
if err != nil { | ||
panic("Failed to read file") | ||
} | ||
for _, line := range strings.Split(strings.TrimRight(string(data), "\n"), "\n") { | ||
if line == "" { | ||
continue | ||
} | ||
words := strings.Split(line, " ") | ||
if words[0] == day { | ||
return words[1:], nil | ||
} | ||
} | ||
return nil, fmt.Errorf("Failed to load solutions") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module aoc | ||
|
||
go 1.22 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package main | ||
|
||
import ( | ||
"strconv" | ||
) | ||
|
||
// Rotation encodes a rotation of n * 90 degrees. | ||
type Rotation int | ||
|
||
// Rotations in four directions. | ||
const ( | ||
RotateRight Rotation = iota | ||
RotateLeft Rotation = iota | ||
RotateReverse Rotation = iota | ||
RotateStraight Rotation = iota | ||
) | ||
|
||
// Direction tracks a 2D vector. | ||
type Direction struct { | ||
dx, dy int | ||
} | ||
|
||
// Rotate the direction by n * 90 degrees. | ||
func (d *Direction) Rotate(rotation Rotation) { | ||
switch rotation { | ||
case RotateRight: | ||
d.dx, d.dy = +1*d.dy, -1*d.dx | ||
case RotateLeft: | ||
d.dx, d.dy = -1*d.dy, +1*d.dx | ||
case RotateReverse: | ||
d.dx, d.dy = -1*d.dx, -1*d.dy | ||
case RotateStraight: | ||
} | ||
} | ||
|
||
// Location tracks a 2D Cartesian coordinate. | ||
type Location struct { | ||
x, y int | ||
} | ||
|
||
// Robot is an object with a Cartesian location and direction. It can advance and rotate. | ||
type Robot struct { | ||
Location | ||
Direction | ||
} | ||
|
||
// Advance the robot by the Direction. | ||
func (r *Robot) Advance() { | ||
r.x += r.dx | ||
r.y += r.dy | ||
} | ||
|
||
// Atoi is a convenience wrapper around strconv.Atoi | ||
func Atoi(a string) int{ | ||
i, err := strconv.Atoi(a) | ||
if err != nil { | ||
panic("strconv.Atoi failed") | ||
} | ||
return i | ||
} | ||
|
||
// Itoa is a convenience wrapper around strconv.Itoa | ||
func Itoa(i int) string{ | ||
return strconv.Itoa(i) | ||
} |
Oops, something went wrong.