package main
import (
"bufio"
"fmt"
"os"
"time"
"github.com/fatih/color"
)
func waitEnter() {
fmt.Printf("Press ENTER to continue\n")
bufio.NewReader(os.Stdin).ReadBytes('\n')
}
func sliceEqual(a, b []int) bool {
if len(a) != len(b) {
return false
}
for t := range a {
if a[t] != b[t] {
return false
}
}
return true
}
type Tool int
const (
ToolNeither Tool = iota
ToolTorch
ToolClimbing
)
type Puzzle struct {
gridW, gridH int
gridSize int
grid []int
depth int
tX, tY int
}
func (p *Puzzle) Solve(depth, tX, tY int) {
p.gridW, p.gridH = tX+10, tY+10
p.gridSize = p.gridW * p.gridH
p.grid = make([]int, p.gridSize)
p.depth = depth
p.tX, p.tY = tX, tY
for y := 0; y < p.gridH; y++ {
i := y * p.gridW
for x := 0; x < p.gridW; x++ {
if x == tX && y == tY {
p.grid[i+x] = 0
continue
}
if y == 0 {
p.grid[i+x] = x * 16807
} else if x == 0 {
p.grid[i+x] = y * 48271
} else {
p.grid[i+x] = p.grid[i+x-1] * p.grid[i+x-p.gridW]
}
p.grid[i+x] = (p.grid[i+x] + p.depth) % 20183
}
}
p.grid[0] = 0
}
func pickTool(kind int, tool Tool) (Tool, bool) {
switch kind {
case 0:
return ToolClimbing, (tool == ToolClimbing)
case 1:
return ToolClimbing, (tool == ToolNeither)
case 2:
return ToolTorch, (tool == ToolTorch)
}
return ToolNeither, (tool == ToolNeither)
}
func (p *Puzzle) FindPath(pretty bool) {
grid := make([]int, p.gridSize)
relScores := make([]int, p.gridSize)
for y := 0; y < p.gridH; y++ {
i := y * p.gridW
for x := 0; x < p.gridW; x++ {
grid[i+x] = 0
}
}
currentTool := ToolNeither
current := 0
target := (p.tY * p.gridW) + p.tX
for {
up := current - p.gridW
dn := current + p.gridW
lt := current - 1
rt := current + 1
if up == target || dn == target || lt == target || rt == target {
_, change := pickTool(p.grid[up]%3, currentTool)
score := 1
if change {
score = 7
}
grid[target] = grid[current] + score
relScores[target] = score
grid[current] += 9000
current = target
break
}
if up >= 0 && grid[up] == 0 {
_, change := pickTool(p.grid[up]%3, currentTool)
score := 1
if change {
score = 7
}
relScores[up] = score
grid[up] = grid[current] + score
}
if lt >= 0 && current%p.gridW > 0 && grid[lt] == 0 {
_, change := pickTool(p.grid[lt]%3, currentTool)
score := 1
if change {
score = 7
}
relScores[lt] = score
grid[lt] = grid[current] + score
}
if rt < p.gridSize && current+1%p.gridW != 0 && grid[rt] == 0 {
_, change := pickTool(p.grid[rt]%3, currentTool)
score := 1
if change {
score = 7
}
relScores[rt] = score
grid[rt] = grid[current] + score
}
if dn < p.gridSize && grid[dn] == 0 {
_, change := pickTool(p.grid[dn]%3, currentTool)
score := 1
if change {
score = 7
}
relScores[dn] = score
grid[dn] = grid[current] + score
}
grid[current] += 9000
lowest := 8999
for i, score := range grid {
if score > 0 && score < lowest {
current = i
lowest = score
}
}
if lowest == 8999 {
break
}
currentTool, _ = pickTool(p.grid[current]%3, currentTool)
// Print path
if pretty {
fmt.Printf("\033c")
for y := 0; y < p.gridH; y++ {
i := y * p.gridW
for x := 0; x < p.gridW; x++ {
if x == 0 && y == 0 {
fmt.Printf("S")
continue
}
if x == p.tX && y == p.tY {
fmt.Printf("T")
continue
}
if grid[i+x] >= 9000 {
fmt.Printf("o")
continue
}
if grid[i+x] > 0 {
fmt.Printf("+")
continue
}
fmt.Printf(".")
}
fmt.Printf("\n")
}
time.Sleep(10 * time.Millisecond)
}
}
current = target
for {
lowest := 99999
up := current - p.gridW
dn := current + p.gridW
lt := current - 1
rt := current + 1
if up == 0 || dn == 0 || lt == 0 || rt == 0 {
break
}
if up >= 0 && grid[up] >= 9000 && grid[up] < lowest {
lowest = grid[up]
current = up
}
if lt >= 0 && current%p.gridW > 0 && grid[lt] >= 9000 && grid[lt] < lowest {
lowest = grid[lt]
current = lt
}
if rt < p.gridSize && current+1%p.gridW != 0 && grid[rt] >= 9000 && grid[rt] < lowest {
lowest = grid[rt]
current = rt
}
if dn < p.gridSize && grid[dn] >= 9000 && grid[dn] < lowest {
lowest = grid[rt]
current = rt
}
grid[current] = 100000
// Print path
if pretty {
fmt.Printf("\033c")
for y := 0; y < p.gridH; y++ {
i := y * p.gridW
for x := 0; x < p.gridW; x++ {
if x == 0 && y == 0 {
fmt.Printf("S")
continue
}
if x == p.tX && y == p.tY {
fmt.Printf("T")
continue
}
if grid[i+x] == 100000 {
fmt.Printf("*")
continue
}
if grid[i+x] >= 9000 {
fmt.Printf("o")
continue
}
if grid[i+x] > 0 {
fmt.Printf("+")
continue
}
fmt.Printf(".")
}
fmt.Printf("\n")
}
time.Sleep(10 * time.Millisecond)
}
}
// Print path
score := 0
p.PrintState()
for y := 0; y < p.gridH; y++ {
i := y * p.gridW
for x := 0; x < p.gridW; x++ {
if x == 0 && y == 0 {
fmt.Printf("S")
continue
}
if x == p.tX && y == p.tY {
fmt.Printf("T")
continue
}
if grid[i+x] == 100000 {
fmt.Printf("*")
score += relScores[i+x]
continue
}
if grid[i+x] >= 9000 {
fmt.Printf("o")
continue
}
if grid[i+x] > 0 {
fmt.Printf("+")
continue
}
fmt.Printf(".")
}
fmt.Printf("\n")
}
fmt.Printf("\n")
for y := 0; y < p.gridH; y++ {
i := y * p.gridW
for x := 0; x < p.gridW; x++ {
fmt.Printf("%d", relScores[i+x])
}
fmt.Printf("\n")
}
fmt.Printf("Minutes: %d\n", score)
}
func (p *Puzzle) PrintState() {
fmt.Printf("\033c")
score := 0
rocky := color.New(color.FgHiWhite)
wet := color.New(color.FgHiBlue)
narrow := color.New(color.FgWhite)
for y := 0; y < p.gridH; y++ {
i := y * p.gridW
for x := 0; x < p.gridW; x++ {
if i+x == 0 {
fmt.Printf("%c", 'S')
continue
}
if x == p.tX && y == p.tY {
fmt.Printf("%c", 'T')
continue
}
erosion := p.grid[i+x] % 3
switch erosion {
case 0:
rocky.Printf("%c", '.')
case 1:
score++
wet.Printf("%c", '=')
case 2:
score += 2
narrow.Printf("%c", '|')
default:
fmt.Printf("%c", '?')
}
}
fmt.Printf("\n")
}
fmt.Println("Risk level:", score)
}
func main() {
// Test
t := Puzzle{}
t.Solve(510, 10, 10)
t.FindPath(true)
// Puzzle
p := Puzzle{}
p.Solve(9171, 7, 721)
p.FindPath(false)
}