Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add puzzle status error #12

Merged
merged 2 commits into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion examples/golang/2016/01/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package day1
import (
"fmt"
"path/filepath"
"os"

"github.com/dolfolife/aoctl/pkg/puzzle"
"github.com/dolfolife/aoctl/pkg/aoc"
)
Expand All @@ -14,8 +16,13 @@ func Solve() {
file := filepath.Join(aocConfig.ProjectPath, "01/puzzle.yaml")
inputfile1 := filepath.Join(aocConfig.ProjectPath, "01/input.txt")
inputfile2 := filepath.Join(aocConfig.ProjectPath, "01/input.txt")
p := puzzle.NewPuzzleFromCache(file, []string{ inputfile1, inputfile2 })
p, err := puzzle.NewPuzzleFromCache(file, []string{ inputfile1, inputfile2 })

if err != nil {
fmt.Println(err)
os.Exit(1)
}

part1 := Day1Part1Solver{}
part1.Puzzle = p.Puzzles[0]
answer1, err := part1.Solve()
Expand Down
8 changes: 7 additions & 1 deletion examples/golang/2016/02/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package day2

import (
"fmt"
"os"
"path/filepath"

"github.com/dolfolife/aoctl/pkg/puzzle"
"github.com/dolfolife/aoctl/pkg/aoc"
)
Expand All @@ -14,8 +16,12 @@ func Solve() {
file := filepath.Join(aocConfig.ProjectPath, "02/puzzle.yaml")
inputfile1 := filepath.Join(aocConfig.ProjectPath, "02/input.txt")
inputfile2 := filepath.Join(aocConfig.ProjectPath, "02/input.txt")
p := puzzle.NewPuzzleFromCache(file, []string{inputfile1, inputfile2})
p, err := puzzle.NewPuzzleFromCache(file, []string{inputfile1, inputfile2})

if err != nil {
fmt.Println(err)
os.Exit(1)
}
part1 := Day2Part1Solver{}
part1.Puzzle = p.Puzzles[0]
answer1, err := part1.Solve()
Expand Down
10 changes: 9 additions & 1 deletion examples/golang/2016/03/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package day3
import (
"fmt"
"path/filepath"
"os"

"github.com/dolfolife/aoctl/pkg/puzzle"
"github.com/dolfolife/aoctl/pkg/aoc"
)
Expand All @@ -14,7 +16,13 @@ func Solve() {
file := filepath.Join(aocConfig.ProjectPath, "03/puzzle.yaml")
inputfile1 := filepath.Join(aocConfig.ProjectPath, "03/input.txt")
inputfile2 := filepath.Join(aocConfig.ProjectPath, "03/input.txt")
p := puzzle.NewPuzzleFromCache(file, []string{inputfile1, inputfile2})
p, err := puzzle.NewPuzzleFromCache(file, []string{inputfile1, inputfile2})

if err != nil {
fmt.Println(err)
os.Exit(1)
}

part1 := Day3Part1Solver{}
part1.Puzzle = p.Puzzles[0]
answer1, err := part1.Solve()
Expand Down
51 changes: 25 additions & 26 deletions pkg/math/geometry.go
Original file line number Diff line number Diff line change
@@ -1,45 +1,44 @@
package math

import (
"math"
"strconv"
"math"
"strconv"
)

type Point struct {
X, Y float64
X, Y float64
}

type Segment struct {
A, B Point
A, B Point
}

func CalDistancePoints(pos1 Point, pos2 Point) string {
partX := math.Abs(float64(pos1.X) - float64(pos2.X))
partY := math.Abs(float64(pos1.Y) - float64(pos2.Y))
return strconv.Itoa(int(partX + partY))
partX := math.Abs(float64(pos1.X) - float64(pos2.X))
partY := math.Abs(float64(pos1.Y) - float64(pos2.Y))
return strconv.Itoa(int(partX + partY))
}


func CalIntersectionPoint(a, b, c, d Point) (bool, Point) {
cross := (c.Y-d.Y)*(b.X-a.X) - (c.X-d.X)*(b.Y-a.Y)
if cross == 0 {
return false, Point{} // Parallel lines, no intersection
}

t1 := float64((c.Y-d.Y)*(c.X-a.X) + (d.X-c.X)*(c.Y-a.Y)) / float64(cross)
t2 := float64((a.Y-b.Y)*(c.X-a.X) + (b.X-a.X)*(c.Y-a.Y)) / float64(cross)

p1 := Point{b.X - a.X, b.Y - a.Y}
// Check if the intersection point is within the line segments
if t1 >= 0 && t1 <= 1 && t2 >= 0 && t2 <= 1 {
intersectionX := a.X + t1*p1.X
intersectionY := a.Y + t1*p1.Y
return true, Point{intersectionX, intersectionY}
}

return false, Point{} // The intersection point is outside the line segments
cross := (c.Y-d.Y)*(b.X-a.X) - (c.X-d.X)*(b.Y-a.Y)
if cross == 0 {
return false, Point{} // Parallel lines, no intersection
}

t1 := float64((c.Y-d.Y)*(c.X-a.X)+(d.X-c.X)*(c.Y-a.Y)) / float64(cross)
t2 := float64((a.Y-b.Y)*(c.X-a.X)+(b.X-a.X)*(c.Y-a.Y)) / float64(cross)

p1 := Point{b.X - a.X, b.Y - a.Y}
// Check if the intersection point is within the line segments
if t1 >= 0 && t1 <= 1 && t2 >= 0 && t2 <= 1 {
intersectionX := a.X + t1*p1.X
intersectionY := a.Y + t1*p1.Y
return true, Point{intersectionX, intersectionY}
}

return false, Point{} // The intersection point is outside the line segments
}

func IsValidTriangle(a, b, c int) bool {
return a+b > c && a+c > b && b+c > a
return a+b > c && a+c > b && b+c > a
}
48 changes: 24 additions & 24 deletions pkg/math/geometry_test.go
Original file line number Diff line number Diff line change
@@ -1,40 +1,40 @@
package math

import (
"testing"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/assert"
)

func TestCalDistancePoints(t *testing.T) {
assert.Equal(t, "5", CalDistancePoints(Point{X: 0, Y: 0}, Point{X: 5, Y: 0}))
assert.Equal(t, "5", CalDistancePoints(Point{X: 0, Y: 0}, Point{X: 0, Y: 5}))
assert.Equal(t, "5", CalDistancePoints(Point{X: 0, Y: 0}, Point{X: -5, Y: 0}))
assert.Equal(t, "5", CalDistancePoints(Point{X: 0, Y: 0}, Point{X: 0, Y: -5}))
assert.Equal(t, "10", CalDistancePoints(Point{X: 0, Y: 0}, Point{X: 5, Y: 5}))
assert.Equal(t, "10", CalDistancePoints(Point{X: 0, Y: 0}, Point{X: -5, Y: 5}))
assert.Equal(t, "10", CalDistancePoints(Point{X: 0, Y: 0}, Point{X: 5, Y: -5}))
assert.Equal(t, "10", CalDistancePoints(Point{X: 0, Y: 0}, Point{X: -5, Y: -5}))
assert.Equal(t, "5", CalDistancePoints(Point{X: 0, Y: 0}, Point{X: 5, Y: 0}))
assert.Equal(t, "5", CalDistancePoints(Point{X: 0, Y: 0}, Point{X: 0, Y: 5}))
assert.Equal(t, "5", CalDistancePoints(Point{X: 0, Y: 0}, Point{X: -5, Y: 0}))
assert.Equal(t, "5", CalDistancePoints(Point{X: 0, Y: 0}, Point{X: 0, Y: -5}))
assert.Equal(t, "10", CalDistancePoints(Point{X: 0, Y: 0}, Point{X: 5, Y: 5}))
assert.Equal(t, "10", CalDistancePoints(Point{X: 0, Y: 0}, Point{X: -5, Y: 5}))
assert.Equal(t, "10", CalDistancePoints(Point{X: 0, Y: 0}, Point{X: 5, Y: -5}))
assert.Equal(t, "10", CalDistancePoints(Point{X: 0, Y: 0}, Point{X: -5, Y: -5}))
}

func TestCalIntersectionPoint(t *testing.T) {
intersect, value := CalIntersectionPoint(Point{X: 0, Y: 0}, Point{X: 8, Y: 0}, Point{X: 4, Y: -4}, Point{X: 4, Y: 4})
assert.Equal(t, true, intersect)
assert.Equal(t, Point{4,0}, value)

intersect, value = CalIntersectionPoint(Point{X: 4, Y: -4}, Point{X: 4, Y: 4}, Point{X: 0, Y: 0}, Point{X: 8, Y: 0})
assert.Equal(t, true, intersect)
assert.Equal(t, Point{4,0}, value)
intersect, value = CalIntersectionPoint(Point{X: 0, Y: -8}, Point{X: 0, Y: -1}, Point{X: 0, Y: 0}, Point{X: 8, Y: 0})
assert.Equal(t, false, intersect)
intersect, value := CalIntersectionPoint(Point{X: 0, Y: 0}, Point{X: 8, Y: 0}, Point{X: 4, Y: -4}, Point{X: 4, Y: 4})
assert.Equal(t, true, intersect)
assert.Equal(t, Point{4, 0}, value)

intersect, value = CalIntersectionPoint(Point{X: 4, Y: -4}, Point{X: 4, Y: 4}, Point{X: 0, Y: 0}, Point{X: 8, Y: 0})
assert.Equal(t, true, intersect)
assert.Equal(t, Point{4, 0}, value)

intersect, value = CalIntersectionPoint(Point{X: 0, Y: -8}, Point{X: 0, Y: -1}, Point{X: 0, Y: 0}, Point{X: 8, Y: 0})
assert.Equal(t, false, intersect)
}

func TestTriangleMath(t *testing.T) {

assert.Equal(t, true, IsValidTriangle(1, 1, 1))
assert.Equal(t, false, IsValidTriangle(5, 10, 25))
assert.Equal(t, true, IsValidTriangle(10, 1, 10))
assert.Equal(t, true, IsValidTriangle(25, 1, 25))
assert.Equal(t, true, IsValidTriangle(1, 1, 1))
assert.Equal(t, false, IsValidTriangle(5, 10, 25))
assert.Equal(t, true, IsValidTriangle(10, 1, 10))
assert.Equal(t, true, IsValidTriangle(25, 1, 25))

}
56 changes: 42 additions & 14 deletions pkg/puzzle/puzzle.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,23 @@ import(
"gopkg.in/yaml.v3"
"log"
"os"
"errors"
"fmt"
"strings"
)

type PuzzleStatus string

const (
Unsolved PuzzleStatus = "UNSOLVED"
Solved PuzzleStatus = "SOLVED"
Unreachable PuzzleStatus = "UNREACHABLE"
)

type PuzzleMetadata struct {
Day string `yaml:"day,omitempty"`
Title string `yaml:"title,omitempty"`
Year string `yaml:"year,omitempty"`
Day string `yaml:"day"`
Title string `yaml:"title"`
Year string `yaml:"year"`
}

type PuzzlePart struct {
Answer string `yaml:"answer,omitempty"`
Description string `yaml:"description,omitempty"`
Status PuzzleStatus `yaml:"status,omitempty"`
Description string `yaml:"description"`
Status PuzzleStatus `yaml:"status"`

RawInput []byte
}
Expand All @@ -44,27 +41,58 @@ func NewPuzzleFromHTML(day string, year string, htmlString string, input []byte)
}
}

func NewPuzzleFromCache(filepath string, inputFilepath []string) Puzzle {
// The field status is a collection of status and we need to validate that the
// status is in the set of valid statuses
func (p *Puzzle) ParseFields() error {
mapStatus := map[string]PuzzleStatus{
"UNSOLVED": Unsolved,
"SOLVED": Solved,
"UNREACHABLE": Unreachable,
}
for i, puzzle := range p.Puzzles {
status := strings.ToUpper(string(puzzle.Status))
if _, ok := mapStatus[status]; ok {
p.Puzzles[i].Status = mapStatus[status]
} else {
errorMessage := fmt.Sprintf("cannot parse Puzzle Part %d", i)
return NewError(ErrInvalidStatus, errors.New(errorMessage))
}
}
return nil
}

func NewPuzzleFromCache(filepath string, inputFilepath []string) (Puzzle, error) {
var puzzle Puzzle
yamlFile, err := os.ReadFile(filepath)
if err != nil {
log.Printf("Error trying to read the YAML file err = #%v ", err)
return Puzzle{}, err
}
err = yaml.Unmarshal(yamlFile, &puzzle)
if err != nil {
log.Fatalf("Unmarshal: %v", err)
return Puzzle{}, err
}

// yaml.Umarshall does not have validation on sets like the status field
// We need to map the status of the puzzle
err = puzzle.ParseFields()
if err != nil {
log.Fatalf("Error trying to parse the Puzzle err = #%v ", err)
return Puzzle{}, err
}

for i, inputFile := range inputFilepath {
rawInput, err := os.ReadFile(inputFile)
if err != nil {
log.Printf("Error trying to read the input for Puzzle Part %d err #%v ", i, err)
log.Fatalf("Error trying to read the input for Puzzle Part %d err #%v ", i, err)
return Puzzle{}, err
}
// we need to delete the last byte of the input because it is a newline or EOF
rawInput = rawInput[:len(rawInput)-1]
puzzle.Puzzles[i].RawInput = rawInput
}
return puzzle
return puzzle, nil
}

func getTitleFromBody(body string) string {
Expand Down
32 changes: 32 additions & 0 deletions pkg/puzzle/puzzle_errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package puzzle

import (
"errors"
)

const (
Unsolved PuzzleStatus = "UNSOLVED"
Solved PuzzleStatus = "SOLVED"
Unreachable PuzzleStatus = "UNREACHABLE"
)

var (
ErrInvalidStatus = errors.New("invalid status")
)

type InvalidPuzzle struct {
appError error
statusErr error
}

// to implement the error interface we need to implement the Error() method
func (e *InvalidPuzzle) Error() string {
return errors.Join(e.statusErr, e.appError).Error()
}

func NewError(appError error, statusErr error) *InvalidPuzzle {
return &InvalidPuzzle{
appError: appError,
statusErr: statusErr,
}
}
3 changes: 0 additions & 3 deletions pkg/puzzle/puzzle_part.go

This file was deleted.

30 changes: 30 additions & 0 deletions pkg/puzzle/puzzle_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package puzzle

import (
"testing"
"os"
"path/filepath"

"github.com/stretchr/testify/assert"
)

func TestSolve(t *testing.T) {
ROOT_DIR := os.Getenv("PWD")
basicYaml := filepath.Join(ROOT_DIR, "test_data/basic.yaml")
subject, err := NewPuzzleFromCache(basicYaml, []string{})

// Basic Yaml has no errors
assert.Nil(t, err)

// Metada is set correctly
assert.Equal(t, subject.Metadata.Year, "year_basic")
assert.Equal(t, subject.Metadata.Day, "day_basic")
assert.Equal(t, subject.Metadata.Title, "title_basic")

// Puzzles check
assert.Equal(t, len(subject.Puzzles), 2)
assert.Equal(t, subject.Puzzles[0].Description, "description_1_basic")
assert.Equal(t, subject.Puzzles[0].Answer, "answer_1_basic")
assert.Equal(t, subject.Puzzles[0].Status, Solved)
}

12 changes: 12 additions & 0 deletions pkg/puzzle/test_data/basic.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
metadata:
day: day_basic
year: year_basic
title: title_basic
puzzles:
- description: description_1_basic
answer: answer_1_basic
status: solved
- description: description_2_basic
answer: answer_2_basic
status: unsolved
Loading