Skip to content

Commit

Permalink
add puzzle status error (#12)
Browse files Browse the repository at this point in the history
* add puzzle status error
Also, added basic test for the Puzzle object while getting unmarshall

Signed-off-by: Rodolfo Sanchez <[email protected]>

* format geometry pkg

---------

Signed-off-by: Rodolfo Sanchez <[email protected]>
  • Loading branch information
dolfolife authored Feb 20, 2024
1 parent c681ad9 commit b7d76b5
Show file tree
Hide file tree
Showing 10 changed files with 189 additions and 70 deletions.
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

0 comments on commit b7d76b5

Please sign in to comment.