advent2022

Advent of Code 2022 Solutions
git clone git://bsandro.tech/advent2022
Log | Files | Refs | README | LICENSE

main.go (5994B)


      1 package main
      2 
      3 import (
      4 	"bufio"
      5 	"fmt"
      6 	"log"
      7 	"os"
      8 	"sort"
      9 	"strings"
     10 )
     11 
     12 type Valve struct {
     13 	name        string
     14 	rate        int
     15 	open        bool
     16 	connections []string
     17 }
     18 
     19 func (v Valve) String() string {
     20 	return fmt.Sprintf("%s,%d,[%s]", v.name, v.rate,
     21 		strings.Join(v.connections, ","))
     22 }
     23 
     24 type Step struct {
     25 	name, parent string
     26 }
     27 type Path struct {
     28 	name    string
     29 	valve   *Valve
     30 	path    Strings
     31 	nested  Paths
     32 	sumRate int // valve.rate + max(nested rates)
     33 }
     34 
     35 func (p Path) Score() int {
     36 	return (10 + p.valve.rate) / len(p.path)
     37 }
     38 func (p Path) String() string {
     39 	s := fmt.Sprintf("(%s:%d:%d:%d sum:%d){", p.name,
     40 		p.valve.rate, len(p.path), p.Score(), p.sumRate)
     41 	s += strings.Join(p.path, "->")
     42 	s += "}"
     43 	if len(p.nested) > 0 {
     44 		for _, n := range p.nested {
     45 			s += "~>"
     46 			s += n.String()
     47 		}
     48 	}
     49 	return s
     50 }
     51 
     52 type Paths []Path
     53 
     54 func (ps Paths) String() string {
     55 	var s string
     56 	for _, p := range ps {
     57 		s += p.String() +
     58 			"\n"
     59 	}
     60 	return s
     61 }
     62 func (ps Paths) Len() int { return len(ps) }
     63 func (ps Paths) Swap(i, j int) {
     64 	ps[i], ps[j] = ps[j], ps[i]
     65 }
     66 func (ps Paths) Less(i, j int) bool {
     67 	//return ps[i].valve.rate > ps[j].valve.rate
     68 	/*if ps[i].sumRate == ps[j].sumRate { return len(ps[i].path) < len(ps[j].path)
     69 	}*/
     70 	return ps[i].sumRate > ps[j].sumRate
     71 }
     72 func (ps Paths) Find(name string) *Path {
     73 	for _, p := range ps {
     74 		if p.name == name {
     75 			return &p
     76 		}
     77 	}
     78 	return nil
     79 }
     80 
     81 type Valves map[string]*Valve
     82 
     83 func (vs Valves) SumRates(names Strings) int {
     84 	sum := 0
     85 	for _, n := range names {
     86 		sum += vs[n].rate
     87 	}
     88 	return sum
     89 }
     90 func (vs Valves) String() string {
     91 	var out string
     92 	for k, v := range vs {
     93 		out += "[" + k + "]:" + v.String() + "\n"
     94 	}
     95 	return out
     96 }
     97 
     98 func (vs Valves) FindRoutes(start string, steps int, visited Strings) Paths {
     99 	steps--
    100 	// opening the valve costs 1
    101 	if steps <= 1 {
    102 		//fmt.Println("route exhausted")
    103 		return Paths{}
    104 	}
    105 	visited = append(visited, start)
    106 	paths := vs.GetPaths(start, visited, steps+1)
    107 	//fmt.Println("FindRoutes", steps, start) fmt.Println(paths)
    108 	for i, p := range paths {
    109 		paths[i].sumRate = paths[i].valve.rate * steps
    110 		paths[i].nested = vs.FindRoutes(p.name, steps-len(p.path), visited)
    111 		if len(paths[i].nested) > 0 {
    112 			sort.Sort(paths[i].nested)
    113 			paths[i].sumRate += paths[i].nested[0].sumRate
    114 		}
    115 	}
    116 
    117 	return paths
    118 }
    119 func (vs Valves) FindPath(start, end string) (Path, bool) {
    120 	var path Path
    121 	path.valve = vs[end]
    122 	path.name = end
    123 	var upcoming []Step
    124 	visited := make(map[string]string) // name:parent
    125 
    126 	upcoming = append(upcoming, Step{name: start, parent: ""})
    127 	for len(upcoming) > 0 {
    128 		cur := upcoming[0].name
    129 		parent := upcoming[0].parent
    130 		upcoming = upcoming[1:]
    131 		if _, exists := visited[cur]; exists {
    132 			continue
    133 		}
    134 		visited[cur] = parent
    135 		for _, s := range vs[cur].connections {
    136 			if _, exists := visited[s]; !exists {
    137 				if s == end {
    138 					path.path = append(path.path, s, cur)
    139 					if parent != "" {
    140 						path.path = append(path.path, parent)
    141 					}
    142 					c := parent
    143 					for c != "" {
    144 						c = visited[c]
    145 						if c != "" {
    146 							path.path = append(path.path, c)
    147 						}
    148 					}
    149 					return path, true
    150 				} else {
    151 					upcoming = append(upcoming, Step{name: s, parent: cur})
    152 				}
    153 			}
    154 		}
    155 	}
    156 
    157 	return path, false
    158 }
    159 
    160 func (vs Valves) GetPaths(start string, excluded Strings, limit int) Paths {
    161 	var paths Paths
    162 	for _, v := range vs {
    163 		if v.name == start || v.open || v.rate == 0 || excluded.Has(v.name) {
    164 			continue
    165 		}
    166 		if p, found := vs.FindPath(start, v.name); found && (len(p.path) <= limit) {
    167 			paths = append(paths, p)
    168 		} else {
    169 			//log.Fatal("no path:", start, v.name)
    170 		}
    171 	}
    172 	sort.Sort(paths)
    173 	return paths
    174 }
    175 
    176 type Strings []string
    177 
    178 func (ss Strings) Has(s string) bool {
    179 	for _, s1 := range ss {
    180 		if s1 == s {
    181 			return true
    182 		}
    183 	}
    184 	return false
    185 }
    186 func main() {
    187 	if len(os.Args) > 1 {
    188 		day16(os.Args[1])
    189 	} else if len(os.Args) == 1 {
    190 		fmt.Printf("usage: %s inputfile.txt\n", os.Args[0])
    191 	} else {
    192 		fmt.Println("No input data")
    193 	}
    194 }
    195 
    196 func day16(input_file string) {
    197 	fmt.Printf("day 16 input filename: %s\n", input_file)
    198 	input, err := os.Open(input_file)
    199 	if err != nil {
    200 		log.Fatal(err)
    201 	}
    202 	defer input.Close()
    203 	valves := make(Valves)
    204 	scanner := bufio.NewScanner(input)
    205 	for scanner.Scan() {
    206 		in := scanner.Text()
    207 		in1, in2, found := strings.Cut(in, "; ")
    208 		if !found {
    209 			log.Fatal("parsing error")
    210 		}
    211 		var vname string
    212 		var vrate int
    213 		cnt, err := fmt.Sscanf(in1, "Valve %s has flow rate=%d", &vname, &vrate)
    214 		if cnt != 2 || err != nil {
    215 			log.Fatal("parsing error:", err)
    216 		}
    217 		in2s := strings.SplitN(in2, " ", 5)
    218 		conns_s := in2s[4]
    219 		conns := strings.Split(conns_s, ", ")
    220 
    221 		valves[vname] = &Valve{name: vname, rate: vrate, connections: conns}
    222 
    223 	}
    224 	if err = scanner.Err(); err != nil {
    225 		log.Fatal(err)
    226 	}
    227 
    228 	//fmt.Print(valves)
    229 
    230 	paths := valves.FindRoutes("AA", 29, Strings{})
    231 	//fmt.Println("FindRoutes path:", paths)
    232 	var pathway Strings
    233 	sort.Sort(paths)
    234 	/*for _, p:=range paths{
    235 		fmt.Println(p.sumRate)
    236 	}*/
    237 	p := &paths[0]
    238 	for {
    239 		pathway = append(pathway, p.name)
    240 		//fmt.Println(p.sumRate)
    241 		if len(p.nested) > 0 {
    242 			sort.Sort(p.nested)
    243 			p = &p.nested[0]
    244 		} else {
    245 			break
    246 		}
    247 	}
    248 	fmt.Println(pathway)
    249 
    250 	rate := 0
    251 	pressure := 0
    252 	v := valves["AA"]
    253 	for i := 1; i <= 30; {
    254 		//paths := valves.GetPaths(v.name, Strings{}) fmt.Println("~~~>", v.name)
    255 		//fmt.Println(paths)
    256 		if len(pathway) > 0 {
    257 			//left := 30 - i
    258 			path, _ := valves.FindPath(v.name, pathway[0])
    259 			plen := len(path.path)
    260 			pathway = pathway[1:]
    261 			fmt.Printf("%s -> %s (%d)\n", v.name, path.name, plen)
    262 			//fmt.Println(path)
    263 			pressure += rate * plen
    264 			i += plen - 1
    265 			v = valves[path.name]
    266 			v.open = true
    267 			rate += v.rate
    268 			fmt.Printf("[%d] rate:%d, pressure:%d\n", i, rate, pressure)
    269 		} else {
    270 			//fmt.Printf("[%d] no more paths\n", i)
    271 			pressure += rate
    272 		}
    273 		i++
    274 		//fmt.Println("===============")
    275 	}
    276 	fmt.Printf("rate:%d, pressure:%d\n", rate, pressure)
    277 }
    278 
    279 //2591 too high
    280 //1634 too low
    281 //2096 too low
    282 //2128 wrong
    283 //2342 wrong
    284 //2297 wrong