nohar icon

AoC 2021 Day 12 in Go

nohar | PRO | 12/12/21 09:35:43 PM UTC | 0 ⭐ | 15309 👁️ | Never ⏰ | []
Go |

2.96 KB

|

None

|

0 👍

/

0 👎

package main
 
import (
    "aoc/utils"
    "fmt"
    "strings"
)
 
func main() {
    g := NewGraph(utils.ReadLines())
    fmt.Println("part1: ", g.Part1())
    fmt.Println("part2: ", g.Part2())
}
 
type Graph struct {
    edges    [][]uint16
    bigCaves uint16
}
 
func (g *Graph) Part1() int {
    initCache()
    return g.countPathsPart1(startCave, 0)
}
 
func (g *Graph) Part2() int {
    initCache()
    return g.countPathsPart2(startCave, 0)
}
 
const (
    startCave = 1
    endCave   = 0
)
 
func caveMask(cave uint16) uint16 {
    return 1 << cave
}
 
func (g *Graph) countPathsPart1(from uint16, visited uint16) int {
    var count int
    if count, ok := getCachedResult(from, visited); ok {
        return count
    }
    for _, to := range g.edges[from] {
        if to == endCave {
            count++
            continue
        }
        toMask := caveMask(to) &^ g.bigCaves
        if visited&toMask == 0 {
            count += g.countPathsPart1(to, visited|toMask)
        }
    }
    putToCache(from, visited, count)
    return count
}
 
const doubleMask = 1 << 15
 
func (g *Graph) countPathsPart2(from uint16, visited uint16) int {
    var count int
    if count, ok := getCachedResult(from, visited); ok {
        return count
    }
    for _, to := range g.edges[from] {
        if to == endCave {
            count++
            continue
        }
        toMask := caveMask(to) &^ g.bigCaves
        if visited&toMask == 0 {
            count += g.countPathsPart2(to, visited|toMask)
        } else if visited&doubleMask == 0 {
            count += g.countPathsPart2(to, visited|doubleMask)
        }
    }
    putToCache(from, visited, count)
    return count
}
 
// Memoïzation
 
type key struct {
    cave    uint16
    visited uint16
}
 
type cache map[key]int
 
var memo cache
 
func initCache() {
    memo = make(cache)
}
 
func getCachedResult(from uint16, visited uint16) (int, bool) {
    count, ok := memo[key{from, visited}]
    return count, ok
}
 
func putToCache(from uint16, visited uint16, count int) {
    memo[key{from, visited}] = count
}
 
// Input parsing
 
func NewGraph(input []string) *Graph {
    g := newBuilder()
    g.parse(input)
    return &g.Graph
}
 
func newBuilder() *graphBuilder {
    return &graphBuilder{
        Graph: Graph{edges: make([][]uint16, 2, 16)},
        caves: map[string]uint16{
            "start": startCave,
            "end":   endCave,
        },
    }
}
 
type graphBuilder struct {
    Graph
    caves map[string]uint16
}
 
func (g *graphBuilder) parse(input []string) {
    for _, line := range input {
        edge := strings.Split(line, "-")
        c1 := g.getCave(edge[0])
        c2 := g.getCave(edge[1])
        g.addEdge(c1, c2)
    }
}
 
func (g *graphBuilder) addEdge(c1, c2 uint16) {
    if c1 != endCave && c2 != startCave {
        g.edges[c1] = append(g.edges[c1], c2)
    }
    if c1 != startCave && c2 != endCave {
        g.edges[c2] = append(g.edges[c2], c1)
    }
}
 
func (g *graphBuilder) getCave(name string) uint16 {
    if cave, ok := g.caves[name]; ok {
        return cave
    }
    cave := uint16(len(g.caves))
    g.caves[name] = cave
    if 'A' <= name[0] && name[0] < 'Z' {
        g.bigCaves |= caveMask(cave)
    }
    g.edges = append(g.edges, nil)
    return cave
}
 

Comments

  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎

    
        
  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎