Part 1 and first pass at Part 2
This commit is contained in:
215
fifteen/path.go
Normal file
215
fifteen/path.go
Normal file
@@ -0,0 +1,215 @@
|
||||
package fifteen
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type path struct {
|
||||
riskLevel [][]int
|
||||
width int
|
||||
height int
|
||||
}
|
||||
|
||||
type point struct {
|
||||
x int
|
||||
y int
|
||||
}
|
||||
|
||||
func (p *path) load(filename string) error {
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
scanner := bufio.NewScanner(file)
|
||||
|
||||
p.riskLevel = [][]int{}
|
||||
for scanner.Scan() {
|
||||
row := []int{}
|
||||
for _, n := range strings.Split(scanner.Text(), "") {
|
||||
v, err := strconv.Atoi(string(n))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
row = append(row, v)
|
||||
}
|
||||
p.riskLevel = append(p.riskLevel, row)
|
||||
}
|
||||
|
||||
p.height = len(p.riskLevel)
|
||||
p.width = len(p.riskLevel[0])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *path) scale(sizeX int, sizeY int) *path {
|
||||
scaledPath := &path{
|
||||
width: p.width * sizeX,
|
||||
height: p.height * sizeY,
|
||||
}
|
||||
|
||||
scaledPath.riskLevel = make([][]int, scaledPath.height)
|
||||
for i := 0; i < scaledPath.height; i++ {
|
||||
scaledPath.riskLevel[i] = make([]int, scaledPath.width)
|
||||
}
|
||||
|
||||
for i := 0; i < sizeX; i++ {
|
||||
for j := 0; j < sizeY; j++ {
|
||||
for y := 0; y < p.height; y++ {
|
||||
for x := 0; x < p.width; x++ {
|
||||
risk := p.riskLevel[y][x] + i + j
|
||||
for risk > 9 {
|
||||
risk -= 9
|
||||
}
|
||||
|
||||
scaledPath.riskLevel[(j*p.height)+y][(i*p.width)+x] = risk
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return scaledPath
|
||||
}
|
||||
|
||||
func (p *path) print() {
|
||||
for y := 0; y < p.height; y++ {
|
||||
row := ""
|
||||
for x := 0; x < p.width; x++ {
|
||||
row += fmt.Sprintf("%d", p.riskLevel[y][x])
|
||||
}
|
||||
fmt.Println(row)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *path) totalRisk() int {
|
||||
start := point{
|
||||
x: 0,
|
||||
y: 0,
|
||||
}
|
||||
end := point{
|
||||
x: p.width - 1,
|
||||
y: p.height - 1,
|
||||
}
|
||||
|
||||
path := p.path(start, end)
|
||||
total := 0
|
||||
|
||||
for i, point := range path {
|
||||
if i == 0 {
|
||||
continue
|
||||
}
|
||||
total += p.riskLevel[point.y][point.x]
|
||||
}
|
||||
|
||||
return total
|
||||
}
|
||||
|
||||
func minValue(distance map[point]int, nodes []point) (point, []point) {
|
||||
var min *point
|
||||
remaining := []point{}
|
||||
|
||||
for i := range nodes {
|
||||
if min == nil {
|
||||
min = &nodes[i]
|
||||
continue
|
||||
}
|
||||
|
||||
if distance[nodes[i]] < distance[*min] {
|
||||
remaining = append(remaining, *min)
|
||||
min = &nodes[i]
|
||||
} else {
|
||||
remaining = append(remaining, nodes[i])
|
||||
}
|
||||
}
|
||||
|
||||
return *min, remaining
|
||||
}
|
||||
|
||||
func contains(nodes []point, x int, y int) bool {
|
||||
for _, n := range nodes {
|
||||
if n.x == x && n.y == y {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *path) path(start point, end point) []point {
|
||||
dist := map[point]int{}
|
||||
prev := map[point]point{}
|
||||
nodes := []point{}
|
||||
|
||||
for y := 0; y < p.height; y++ {
|
||||
for x := 0; x < p.width; x++ {
|
||||
point := point{x: x, y: y}
|
||||
// Virtually infinite for worst case
|
||||
dist[point] = 10 * (p.width + p.height)
|
||||
nodes = append(nodes, point)
|
||||
}
|
||||
}
|
||||
dist[start] = 0
|
||||
|
||||
for len(nodes) > 0 {
|
||||
current, newNodes := minValue(dist, nodes)
|
||||
|
||||
// Check neigbours
|
||||
neighbours := []point{}
|
||||
// left
|
||||
if current.x > 0 && contains(newNodes, current.x-1, current.y) {
|
||||
left := point{
|
||||
x: current.x - 1,
|
||||
y: current.y,
|
||||
}
|
||||
neighbours = append(neighbours, left)
|
||||
}
|
||||
// up
|
||||
if current.y > 0 && contains(newNodes, current.x, current.y-1) {
|
||||
up := point{
|
||||
x: current.x,
|
||||
y: current.y - 1,
|
||||
}
|
||||
neighbours = append(neighbours, up)
|
||||
}
|
||||
// right
|
||||
if current.x < p.width-1 && contains(newNodes, current.x+1, current.y) {
|
||||
right := point{
|
||||
x: current.x + 1,
|
||||
y: current.y,
|
||||
}
|
||||
neighbours = append(neighbours, right)
|
||||
}
|
||||
// down
|
||||
if current.y < p.height-1 && contains(newNodes, current.x, current.y+1) {
|
||||
down := point{
|
||||
x: current.x,
|
||||
y: current.y + 1,
|
||||
}
|
||||
neighbours = append(neighbours, down)
|
||||
}
|
||||
|
||||
for _, n := range neighbours {
|
||||
cost := dist[current] + p.riskLevel[n.y][n.x]
|
||||
if cost < dist[n] {
|
||||
dist[n] = cost
|
||||
prev[n] = current
|
||||
}
|
||||
}
|
||||
|
||||
nodes = newNodes
|
||||
}
|
||||
|
||||
path := []point{end}
|
||||
index := end
|
||||
for index != start {
|
||||
prefix := []point{prev[index]}
|
||||
path = append(prefix, path...)
|
||||
index = prev[index]
|
||||
}
|
||||
|
||||
return path
|
||||
}
|
Reference in New Issue
Block a user