8 Commits 22bede7ba6 ... 89840da636

Author SHA1 Message Date
  Piotr Czajkowski 89840da636 Needs work 1 year ago
  Piotr Czajkowski 2a4945a048 Solved for small 1 year ago
  Piotr Czajkowski 8d3bf55f1f Need something better 1 year ago
  Piotr Czajkowski 84bc64801e Getting cost of travel. What next? 1 year ago
  Piotr Czajkowski 8b81b31ed4 Need vertices 1 year ago
  Piotr Czajkowski d290c093b1 Need cost 1 year ago
  Piotr Czajkowski b02de82823 Able to build graph 1 year ago
  Piotr Czajkowski b886480848 Able to read input 1 year ago
1 changed files with 233 additions and 0 deletions
  1. 233 0
      16/code.go

+ 233 - 0
16/code.go

@@ -0,0 +1,233 @@
+package main
+
+import (
+	"bufio"
+	"fmt"
+	"log"
+	"os"
+	"regexp"
+	"strings"
+)
+
+type valve struct {
+	name        string
+	rate        int
+	open        bool
+	connections []string
+}
+
+type vertex struct {
+	name    string
+	cost    int
+	visited bool
+}
+
+const maxValue int = 100000
+
+func readInput(file *os.File) ([]vertex, map[string]valve) {
+	scanner := bufio.NewScanner(file)
+	valves := make(map[string]valve)
+	var vertices []vertex
+
+	for scanner.Scan() {
+		line := scanner.Text()
+		if line == "" {
+			continue
+		}
+
+		var current valve
+		if strings.Contains(line, "valves") {
+			n, err := fmt.Sscanf(line, "Valve %s has flow rate=%d", &current.name, &current.rate)
+			if n != 2 || err != nil {
+				log.Fatal("Can't parse (valves):", line, err)
+			}
+
+			re := regexp.MustCompile(`valves .*`)
+			parts := re.FindString(line)
+			parts = strings.TrimLeft(parts, "valves ")
+			current.connections = strings.Split(parts, ", ")
+		} else {
+			var connection string
+			n, err := fmt.Sscanf(line, "Valve %s has flow rate=%d; tunnel leads to valve %s", &current.name, &current.rate, &connection)
+			if n != 3 || err != nil {
+				log.Fatal("Can't parse:", line, err)
+			}
+
+			current.connections = append(current.connections, connection)
+		}
+
+		vertices = append(vertices, vertex{current.name, maxValue, false})
+		valves[current.name] = current
+	}
+
+	return vertices, valves
+}
+
+type path struct {
+	from string
+	to   string
+	cost int
+}
+
+func buildGraph(valves map[string]valve) []path {
+	var graph []path
+	for key, value := range valves {
+		for i := range value.connections {
+			graph = append(graph, path{key, value.connections[i], 1})
+		}
+	}
+
+	return graph
+}
+
+func setCost(name string, cost int, vertices []vertex) {
+	for i := range vertices {
+		if vertices[i].name == name {
+			vertices[i].cost = cost
+			break
+		}
+	}
+}
+
+func getCost(name string, vertices []vertex) int {
+	for i := range vertices {
+		if vertices[i].name == name {
+			return vertices[i].cost
+		}
+	}
+
+	return 0
+}
+
+func getNext(vertices []vertex) *vertex {
+	min := maxValue
+	var current *vertex
+	for i := range vertices {
+		if vertices[i].visited {
+			continue
+		}
+
+		if vertices[i].cost <= min {
+			min = vertices[i].cost
+			current = &vertices[i]
+		}
+	}
+
+	return current
+}
+
+func traverse(from vertex, vertices []vertex, graph []path) []vertex {
+	newVertices := make([]vertex, len(vertices))
+	copy(newVertices, vertices)
+	current := &vertex{from.name, 0, false}
+
+	for {
+		for j := range graph {
+			if graph[j].from != current.name {
+				continue
+			}
+
+			var tentativeCost int
+			if current.cost == maxValue {
+				tentativeCost = maxValue
+			} else {
+				tentativeCost = current.cost + 1
+			}
+
+			if tentativeCost < getCost(graph[j].to, newVertices) {
+				setCost(graph[j].to, tentativeCost, newVertices)
+			}
+		}
+
+		current.visited = true
+
+		current = getNext(newVertices)
+		if current == nil {
+			break
+		}
+	}
+
+	return newVertices
+}
+
+func contains(visited []string, name string) bool {
+	for i := range visited {
+		if visited[i] == name {
+			return true
+		}
+	}
+
+	return false
+}
+
+func filtered(vertices []vertex, valves map[string]valve, visited []string) []vertex {
+	var result []vertex
+	for i := range vertices {
+		if contains(visited, vertices[i].name) {
+			continue
+		}
+
+		val, _ := valves[vertices[i].name]
+		if val.rate > 0 {
+			result = append(result, vertices[i])
+		}
+	}
+
+	return result
+}
+
+func calculate(moveTo []vertex, vertices []vertex, graph []path, valves map[string]valve, visited []string, count int, rate int) int {
+	if count >= 30 || len(moveTo) == 0 {
+		return rate
+	}
+
+	max := 0
+	for i := range moveTo {
+		currentCount := count + moveTo[i].cost + 1
+		if currentCount > 30 {
+			continue
+		}
+
+		val, _ := valves[moveTo[i].name]
+		val.open = true
+		valves[moveTo[i].name] = val
+
+		newVisited := make([]string, len(visited))
+		copy(newVisited, visited)
+		newVisited = append(newVisited, moveTo[i].name)
+
+		canGo := traverse(moveTo[i], vertices, graph)
+		toCheck := filtered(canGo, valves, newVisited)
+		result := calculate(toCheck, vertices, graph, valves, newVisited, currentCount, rate+(30-currentCount)*val.rate)
+		if result > max {
+			max = result
+		}
+	}
+
+	return max
+}
+
+func part1(vertices []vertex, graph []path, valves map[string]valve) int {
+	canGo := traverse(vertices[0], vertices, graph)
+	toCheck := filtered(canGo, valves, []string{})
+	result := calculate(toCheck, vertices, graph, valves, []string{}, 0, 0)
+
+	return result
+}
+
+func main() {
+	if len(os.Args) < 2 {
+		log.Fatal("You need to specify a file!")
+	}
+
+	filePath := os.Args[1]
+	file, err := os.Open(filePath)
+	if err != nil {
+		log.Fatalf("Failed to open %s!\n", filePath)
+
+	}
+
+	vertices, valves := readInput(file)
+	graph := buildGraph(valves)
+	fmt.Println("Part1:", part1(vertices, graph, valves))
+}