Skip to content

Commit

Permalink
Add solution for problem 79
Browse files Browse the repository at this point in the history
  • Loading branch information
sdqri committed Oct 14, 2024
1 parent f072f50 commit bf51bdf
Show file tree
Hide file tree
Showing 5 changed files with 268 additions and 1 deletion.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ Solved problems:
- [Problem 67](/python/problem67/problem67.py) - Maximum Path Sum II _(solved using python)_
- [Problem 74](/golang/problem74/main.go) - Digit Factorial Chains _(solved using golang)_
- [Problem 75](/golang/problem75/main.go) - Singular Integer Right Triangles _(solved using golang)_
- [Problem 79](/golang/problem79/main.go) - Passcode Derivation _(solved using golang)_
- [Problem 80](/golang/problem80/main.go) - Square Root Digital Expansion _(solved using golang)_
- [Problem 81](/golang/problem81/main.go) - Path Sum: Two Ways _(solved using golang)_
- [Problem 82](/golang/problem82/main.go) - Path Sum: Three Ways _(solved using golang)_
Expand Down
7 changes: 6 additions & 1 deletion golang/go.mod
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
module project-euler

go 1.22.0
go 1.23.0

require github.com/stretchr/testify v1.8.4

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fogleman/gg v1.3.0 // indirect
github.com/goccy/go-graphviz v0.1.3 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/image v0.14.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
10 changes: 10 additions & 0 deletions golang/go.sum
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/goccy/go-graphviz v0.1.3 h1:Pkt8y4FBnBNI9tfSobpoN5qy1qMNqRXPQYvLhaSUasY=
github.com/goccy/go-graphviz v0.1.3/go.mod h1:pMYpbAqJT10V8dzV1JN/g/wUlG/0imKPzn3ZsrchGCI=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4=
golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
Expand Down
50 changes: 50 additions & 0 deletions golang/problem79/0079_keylog.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
319
680
180
690
129
620
762
689
762
318
368
710
720
710
629
168
160
689
716
731
736
729
316
729
729
710
769
290
719
680
318
389
162
289
162
718
729
319
790
680
890
362
319
760
316
729
380
319
728
716
201 changes: 201 additions & 0 deletions golang/problem79/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
package main

import (
"bufio"
"flag"
"fmt"
"os"
"slices"
"strconv"
"strings"
"time"
)

type Node struct {
ID string
OutEdges []Edge
}

func NewNode(id string) *Node {
return &Node{
ID: id,
OutEdges: make([]Edge, 0),
}
}

func (from *Node) AddEdgeTo(to *Node, weight int64) {
for _, edge := range from.OutEdges {
if edge.To == to {
return
}
}
from.OutEdges = append(from.OutEdges, Edge{from, to, weight})
}

type Edge struct {
From *Node
To *Node
Weight int64
}

func TraverseGraph(start *Node, visited map[string]bool) string {
if visited[start.ID] {
return ""
}
visited[start.ID] = true

var result strings.Builder
for _, edge := range start.OutEdges {
result.WriteString(fmt.Sprintf("\"%s\" -> \"%s\" [label=\"%d\"];\n", edge.From.ID, edge.To.ID, edge.Weight))
result.WriteString(TraverseGraph(edge.To, visited))
}
return result.String()
}

func GenerateGraphViz(root *Node) string {
visited := make(map[string]bool)
var graph strings.Builder
graph.WriteString("digraph G {\n")
graph.WriteString(TraverseGraph(root, visited))
graph.WriteString("}")
return graph.String()
}

func FilterEdges(edges []Edge, memMap map[string]struct{}) []Edge {
filteredEdges := []Edge{}
for _, e := range edges {
if _, ok := memMap[e.To.ID]; !ok {
filteredEdges = append(filteredEdges, e)
}
}

return filteredEdges
}

func GetLongestPath(root *Node, target *Node, edgeMap map[string][]Edge) []*Node {
path := []*Node{}
memMap := map[string]struct{}{}

path = append(path, target)

if root.ID == target.ID {
return path
}

outerLoop:
for {
if path[len(path)-1] == root {
break outerLoop
}

for _, edges := range edgeMap {
filteredEdges := FilterEdges(edges, memMap)
if len(filteredEdges) == 1 {
if filteredEdges[0].To.ID == path[len(path)-1].ID {
memMap[path[len(path)-1].ID] = struct{}{}
path = append(path, filteredEdges[0].From)
}
}
}
}
return path
}

func main() {
start := time.Now()

file, err := os.Open("./0079_keylog.txt")
if err != nil {
panic(err)
}
defer file.Close()

scanner := bufio.NewScanner(file)

var keylog [][]int64

nodes := map[string]*Node{}

for scanner.Scan() {
line := scanner.Text()
values := strings.Split(line, "")
row := []int64{}
for _, value := range values {
num, err := strconv.ParseInt(value, 10, 64)
if err != nil {
panic(err)
}

// Add node
if _, ok := nodes[value]; !ok {
nodes[value] = NewNode(value)
}

row = append(row, num)
}
keylog = append(keylog, row)
}

for _, row := range keylog {
node0 := nodes[fmt.Sprintf("%d", row[0])]
node1 := nodes[fmt.Sprintf("%d", row[1])]
node2 := nodes[fmt.Sprintf("%d", row[2])]
node0.AddEdgeTo(node1, 1)
node1.AddEdgeTo(node2, 1)
}

edgeMap := map[string][]Edge{}
nodesWithInEdge := map[string]struct{}{}
for _, node := range nodes {
edgeMap[node.ID] = node.OutEdges
for _, edge := range node.OutEdges {
nodesWithInEdge[edge.To.ID] = struct{}{}
}
}

var rootNode *Node
var targetNode *Node
for nodeID, node := range nodes {
if _, ok := nodesWithInEdge[nodeID]; !ok {
rootNode = node
}

if len(node.OutEdges) == 0 {
targetNode = node
}
}

path := GetLongestPath(rootNode, targetNode, edgeMap)
slices.Reverse(path)

var passwordBuilder strings.Builder
for _, node := range path {
passwordBuilder.WriteString(node.ID)
}

elapsed := time.Since(start)
fmt.Printf("password = %v (elapsed = %v)\n", passwordBuilder.String(), elapsed)

// Create graphviz dot file
dotFlag := flag.Bool("dot", false, "Create graphviz dot file")
flag.Parse()

if *dotFlag {
dot := GenerateGraphViz(rootNode)
file, err = os.Create("graph.dot")
if err != nil {
fmt.Println("Error creating file:", err)
return
}
defer file.Close()

writer := bufio.NewWriter(file)
_, err = writer.WriteString(dot)
if err != nil {
fmt.Println("Error writing to file:", err)
return
}
writer.Flush()

}
}

0 comments on commit bf51bdf

Please sign in to comment.