Compare commits
1 Commits
e771ac9d9b
...
1afaff0021
Author | SHA1 | Date |
---|---|---|
|
1afaff0021 |
|
@ -1,4 +1,2 @@
|
||||||
/.direnv/
|
/.direnv/
|
||||||
/.go
|
/.go
|
||||||
example
|
|
||||||
/day19/input
|
|
||||||
|
|
|
@ -43,9 +43,3 @@ day18 result: 952407566854
|
||||||
*** and with separate done channel from the summing goroutine
|
*** and with separate done channel from the summing goroutine
|
||||||
952408144115
|
952408144115
|
||||||
**** YES
|
**** YES
|
||||||
** and
|
|
||||||
2023/12/18 23:35:31 border is 195341588; inside is 148441957805559
|
|
||||||
2023/12/18 23:35:31
|
|
||||||
|
|
||||||
day18 result: 148442153147147
|
|
||||||
* i should have used a formula. maybe then it would taken less than 4 hours
|
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
in{x<4000:R,m<4000:R,A}
|
|
||||||
px{a<4000:R,A}
|
|
||||||
qqz{s>2770:R,m<1801:A,R}
|
|
||||||
|
|
||||||
{x=787,m=2655,a=1222,s=2876}
|
|
|
@ -1,57 +0,0 @@
|
||||||
package day19
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sort"
|
|
||||||
)
|
|
||||||
|
|
||||||
func merge(intervals [][]int) [][]int {
|
|
||||||
const start, end = 0, 1
|
|
||||||
|
|
||||||
var merged [][]int
|
|
||||||
|
|
||||||
if len(intervals) > 1 {
|
|
||||||
sort.Slice(intervals, func(i, j int) bool {
|
|
||||||
return intervals[i][start] < intervals[j][start]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, interval := range intervals {
|
|
||||||
last := len(merged) - 1
|
|
||||||
if last < 0 || interval[start] > merged[last][end] {
|
|
||||||
merged = append(merged,
|
|
||||||
[]int{start: interval[start], end: interval[end]},
|
|
||||||
)
|
|
||||||
} else if interval[end] > merged[last][end] {
|
|
||||||
merged[last][end] = interval[end]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return merged[:len(merged):len(merged)]
|
|
||||||
}
|
|
||||||
|
|
||||||
func applyLessThan(intervals [][]int, n int) [][]int {
|
|
||||||
var lessers [][]int
|
|
||||||
for _, interval := range intervals {
|
|
||||||
from := interval[0]
|
|
||||||
if from >= n {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
lessers = append(lessers, []int{from, n-1})
|
|
||||||
}
|
|
||||||
|
|
||||||
return lessers
|
|
||||||
}
|
|
||||||
|
|
||||||
func applyMoreThan(intervals [][]int, n int) [][]int {
|
|
||||||
var greaters [][]int
|
|
||||||
for _, interval := range intervals {
|
|
||||||
to := interval[1]
|
|
||||||
if to <= n {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
greaters = append(greaters, []int{n+1, to})
|
|
||||||
}
|
|
||||||
// log.Printf(">>>> in applyMoreThan %d to %+v ; result %+v", n, intervals, greaters)
|
|
||||||
|
|
||||||
return greaters
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
#+title: Notes
|
|
||||||
* testing things
|
|
||||||
|
|
||||||
testSorter := day19.ReadSorterLine("qqz{s>2770:qs,m<1801:hdj,R}")
|
|
||||||
log.Printf("my test sorter is %+v", testSorter)
|
|
||||||
|
|
||||||
testOperation := day19.ReadOperationLine("s>2770:qs")
|
|
||||||
log.Println(testOperation)
|
|
||||||
** testing simplification
|
|
||||||
lnx{m>1548:A,A}
|
|
||||||
qqz{s>2770:qs,m<1801:hdj,R}
|
|
||||||
kt{m>2215:R,x>3386:A,x<3107:R,R}
|
|
||||||
|
|
||||||
testSorter := day19.ReadSorterLine("kt{m>2215:R,x>3386:A,x<3107:R,R}")
|
|
||||||
log.Printf("my test sorter is %+v", testSorter)
|
|
||||||
|
|
||||||
simplified := day19.SimplifyOperation(testSorter)
|
|
||||||
log.Printf("> simplivied %+v", simplified)
|
|
||||||
* i probably don't need 'actual actors'
|
|
||||||
just a generic function that takes 'detail' and 'sorterData'
|
|
||||||
then applies sorterData to the detail,
|
|
||||||
and calls itself with new sorter
|
|
||||||
|
|
||||||
with special cases for "R" and "A"
|
|
||||||
|
|
||||||
so. have funciton from OpeartionData & Detail -> true/false
|
|
||||||
if true take the destination, if false, check next
|
|
||||||
* well. only way to do this is with intervals
|
|
||||||
|
|
||||||
so, sorter check takes in interval.
|
|
||||||
|
|
||||||
then for each of the rule,
|
|
||||||
call first rule with full interval,
|
|
||||||
deduct first rule (for those that don't match) and pass to second.
|
|
||||||
deduct second and pass to next
|
|
||||||
|
|
||||||
A will return full
|
|
||||||
R will return empty
|
|
||||||
|
|
||||||
and results from each rule application should be joined
|
|
||||||
|
|
||||||
so. i need interval deduction
|
|
||||||
and i need interval join
|
|
||||||
* found a bug in always using initial intervals to calculate 'failing' after each step
|
|
||||||
2023/12/19 11:45:14 got and checked 167409079868000
|
|
||||||
|
|
||||||
In the above example, there are 167409079868000 distinct combinations of ratings that will be accepted.
|
|
|
@ -1,341 +0,0 @@
|
||||||
package day19
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Run() int {
|
|
||||||
fmt.Println("hello day 19. sorting parts")
|
|
||||||
filename := "day19/input"
|
|
||||||
|
|
||||||
bytes, err := os.ReadFile(filename)
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprint("cannot read file ", filename))
|
|
||||||
}
|
|
||||||
|
|
||||||
text := string(bytes)
|
|
||||||
|
|
||||||
split := strings.Split(text, "\n\n")
|
|
||||||
|
|
||||||
sorters := ReadSorters(split[0])
|
|
||||||
details := ReadDetailsPart(split[1])
|
|
||||||
|
|
||||||
log.Printf("yay, got sorters\n%+v\nand details\n%+v", sorters, details)
|
|
||||||
|
|
||||||
// countApproved := CountApprovedDetails(details, sorters)
|
|
||||||
result := 0
|
|
||||||
|
|
||||||
fullIntervals := AttrIntervals{
|
|
||||||
"x": [][]int{[]int{1, 4000}},
|
|
||||||
"m": [][]int{[]int{1, 4000}},
|
|
||||||
"a": [][]int{[]int{1, 4000}},
|
|
||||||
"s": [][]int{[]int{1, 4000}},
|
|
||||||
}
|
|
||||||
|
|
||||||
andChecked := processInterval(fullIntervals, "in", sorters)
|
|
||||||
log.Print("got and checked ", andChecked)
|
|
||||||
result = andChecked
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func CountApprovedDetails(details []DetailData, sorters map[string]SorterData) int {
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
wg.Add(len(details))
|
|
||||||
|
|
||||||
approvedDetails := make(chan DetailData)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
wg.Wait()
|
|
||||||
close(approvedDetails)
|
|
||||||
}()
|
|
||||||
|
|
||||||
count := 0
|
|
||||||
acceptedScore := 0
|
|
||||||
|
|
||||||
done := make(chan any)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
for detail := range approvedDetails {
|
|
||||||
log.Println("got approved ", detail)
|
|
||||||
count += 1
|
|
||||||
for _, attrValue := range detail.Attrs {
|
|
||||||
acceptedScore += attrValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
close(done)
|
|
||||||
}()
|
|
||||||
|
|
||||||
for _, d := range details {
|
|
||||||
go func(d DetailData) {
|
|
||||||
log.Print("> starting for ", d)
|
|
||||||
isAccepted := ProcessDetail(d, sorters)
|
|
||||||
if isAccepted {
|
|
||||||
log.Println("> accepting ", d)
|
|
||||||
approvedDetails <- d
|
|
||||||
} else {
|
|
||||||
log.Println("> rejecting ", d)
|
|
||||||
}
|
|
||||||
wg.Done()
|
|
||||||
}(d)
|
|
||||||
}
|
|
||||||
|
|
||||||
<-done
|
|
||||||
|
|
||||||
return acceptedScore
|
|
||||||
}
|
|
||||||
|
|
||||||
type Operation rune
|
|
||||||
|
|
||||||
const (
|
|
||||||
LessThan Operation = '<'
|
|
||||||
MoreThan Operation = '>'
|
|
||||||
)
|
|
||||||
func (o Operation)String() string {
|
|
||||||
return string(o)
|
|
||||||
}
|
|
||||||
|
|
||||||
type OperationData struct {
|
|
||||||
AttrName string
|
|
||||||
Operation Operation
|
|
||||||
Num int
|
|
||||||
SentToName string
|
|
||||||
InitialString string
|
|
||||||
}
|
|
||||||
func (od OperationData)String() string {
|
|
||||||
return od.InitialString
|
|
||||||
}
|
|
||||||
|
|
||||||
type SorterData struct {
|
|
||||||
Name string
|
|
||||||
DefaultState string
|
|
||||||
Operations []OperationData
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReadSorters(sortersText string) map[string]SorterData {
|
|
||||||
result := make(map[string]SorterData)
|
|
||||||
sortersText = strings.TrimSpace(sortersText)
|
|
||||||
lines := strings.Split(sortersText, "\n")
|
|
||||||
|
|
||||||
for _, line := range lines {
|
|
||||||
sorter := SimplifyOperation( ReadSorterLine(line) )
|
|
||||||
result[sorter.Name] = sorter
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// qqz{s>2770:qs,m<1801:hdj,R}
|
|
||||||
func ReadSorterLine(line string) (result SorterData) {
|
|
||||||
re1 := regexp.MustCompile(`(?P<NAME>\D+){(?P<OPERATIONS>.+)}`)
|
|
||||||
|
|
||||||
firstSplit := re1.FindStringSubmatch(line)
|
|
||||||
|
|
||||||
result.Name = firstSplit[1]
|
|
||||||
|
|
||||||
operationLines := strings.Split(firstSplit[2], ",")
|
|
||||||
operations := make([]OperationData, len(operationLines)-1)
|
|
||||||
result.Operations = operations
|
|
||||||
|
|
||||||
result.DefaultState = operationLines[len(operationLines)-1]
|
|
||||||
|
|
||||||
for i, line := range operationLines[:len(operationLines)-1] {
|
|
||||||
operations[i] = ReadOperationLine(line)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("mathed %s got %+v; operations : %+v\n", line, firstSplit, operations)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// s>2770:qs
|
|
||||||
func ReadOperationLine(line string) (result OperationData) {
|
|
||||||
result.InitialString = line
|
|
||||||
re := regexp.MustCompile(`(?P<ATTRNAME>\D)(?P<OPERATION>[\>\<])(?P<NUMBER>\d+):(?P<TARGET>\D+)`)
|
|
||||||
split := re.FindStringSubmatch(line)
|
|
||||||
log.Printf("matching operation %s into %+v\n", line, split)
|
|
||||||
result.AttrName = split[1]
|
|
||||||
result.Operation = Operation([]rune(split[2])[0])
|
|
||||||
result.SentToName = split[4]
|
|
||||||
num, err := strconv.Atoi(split[3])
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("error getting number %s in line %s. %s", split[3], line, err))
|
|
||||||
}
|
|
||||||
result.Num = num
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// drop last operations which target same 'next' as default. these check are not necessary
|
|
||||||
func SimplifyOperation(sorter SorterData) SorterData {
|
|
||||||
actualLast := len(sorter.Operations) - 1
|
|
||||||
for i := actualLast; i >= 0; i-- {
|
|
||||||
if sorter.Operations[i].SentToName != sorter.DefaultState {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
actualLast -= 1
|
|
||||||
}
|
|
||||||
|
|
||||||
sorter.Operations = sorter.Operations[:actualLast+1]
|
|
||||||
|
|
||||||
return sorter
|
|
||||||
}
|
|
||||||
|
|
||||||
type DetailData struct {
|
|
||||||
Attrs map[string]int
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReadDetailsPart(text string) (result []DetailData) {
|
|
||||||
text = strings.TrimSpace(text)
|
|
||||||
for _, line := range strings.Split(text, "\n") {
|
|
||||||
result = append(result, ReadDetailLine(line))
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// {x=787,m=2655,a=1222,s=2876}
|
|
||||||
func ReadDetailLine(line string) (result DetailData) {
|
|
||||||
attrs := make(map[string]int)
|
|
||||||
result.Attrs = attrs
|
|
||||||
line = line[1 : len(line)-1]
|
|
||||||
attrsLine := strings.Split(line, ",")
|
|
||||||
re := regexp.MustCompile(`(?P<ATTR>\D)=(?P<NUM>\d+)`)
|
|
||||||
for _, attrLine := range attrsLine {
|
|
||||||
split := re.FindStringSubmatch(attrLine)
|
|
||||||
attrName := split[1]
|
|
||||||
num, err := strconv.Atoi(split[2])
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprint("error parsing detail ", line))
|
|
||||||
}
|
|
||||||
|
|
||||||
attrs[attrName] = num
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func ProcessDetail(d DetailData, sorters map[string]SorterData) (isAccepted bool) {
|
|
||||||
curSorterName := "in"
|
|
||||||
for curSorterName != "A" && curSorterName != "R" {
|
|
||||||
sorter, found := sorters[curSorterName]
|
|
||||||
if !found {
|
|
||||||
panic(fmt.Sprint("error finding soter ", curSorterName))
|
|
||||||
}
|
|
||||||
curSorterName = sorter.NextSorterNameFor(d)
|
|
||||||
}
|
|
||||||
return curSorterName == "A"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s SorterData) NextSorterNameFor(d DetailData) string {
|
|
||||||
for _, operation := range s.Operations {
|
|
||||||
if operation.IsDetailPassing(d) {
|
|
||||||
return operation.SentToName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.DefaultState
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o OperationData) IsDetailPassing(d DetailData) bool {
|
|
||||||
detailValue := d.Attrs[o.AttrName]
|
|
||||||
switch o.Operation {
|
|
||||||
case LessThan:
|
|
||||||
return detailValue < o.Num
|
|
||||||
case MoreThan:
|
|
||||||
return detailValue > o.Num
|
|
||||||
}
|
|
||||||
|
|
||||||
panic(fmt.Sprint("unknown operation. ", o, d))
|
|
||||||
}
|
|
||||||
|
|
||||||
type AttrIntervals map[string][][]int
|
|
||||||
|
|
||||||
func (o OperationData) getPassingIntervals(i AttrIntervals) AttrIntervals {
|
|
||||||
result := make(AttrIntervals, 0)
|
|
||||||
for key, value := range i {
|
|
||||||
result[key] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
operationKey := o.AttrName
|
|
||||||
operatedIntervals := result[operationKey]
|
|
||||||
|
|
||||||
switch o.Operation {
|
|
||||||
case LessThan:
|
|
||||||
result[operationKey] = applyLessThan(operatedIntervals, o.Num)
|
|
||||||
case MoreThan:
|
|
||||||
result[operationKey] = applyMoreThan(operatedIntervals, o.Num)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o OperationData) getFailingIntervals(i AttrIntervals) AttrIntervals {
|
|
||||||
result := make(AttrIntervals, 0)
|
|
||||||
for key, value := range i {
|
|
||||||
result[key] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
operationKey := o.AttrName
|
|
||||||
operatedIntervals := result[operationKey]
|
|
||||||
|
|
||||||
switch o.Operation {
|
|
||||||
case LessThan:
|
|
||||||
result[operationKey] = applyMoreThan(operatedIntervals, o.Num-1)
|
|
||||||
case MoreThan:
|
|
||||||
result[operationKey] = applyLessThan(operatedIntervals, o.Num+1)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func processInterval(i AttrIntervals, sorterName string, sorters map[string]SorterData) (combinationsAccepted int) {
|
|
||||||
if sorterName == "A" {
|
|
||||||
mul := 1
|
|
||||||
for key, attrIntervals := range i {
|
|
||||||
allowedValuesOfAttr := 0
|
|
||||||
for _, interval := range attrIntervals {
|
|
||||||
from := interval[0]
|
|
||||||
to := interval[1]
|
|
||||||
len := to - from + 1
|
|
||||||
allowedValuesOfAttr += len
|
|
||||||
log.Printf("for %s allowed attrs are %d", key, allowedValuesOfAttr)
|
|
||||||
}
|
|
||||||
mul *= allowedValuesOfAttr
|
|
||||||
}
|
|
||||||
log.Printf("exit recursion for %s. Accept interval %+v . result %d. max is %d", sorterName, i, mul, 40000 * 4000 * 4000 * 4000)
|
|
||||||
return mul
|
|
||||||
}
|
|
||||||
if sorterName == "R" {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
s := sorters[sorterName]
|
|
||||||
log.Printf("> starting interval check for %s (%+v) on %+v", sorterName, s, i)
|
|
||||||
intervalsPassingOnThisStep := i
|
|
||||||
|
|
||||||
for _, operation := range s.Operations {
|
|
||||||
intervalsPassing := operation.getPassingIntervals(intervalsPassingOnThisStep)
|
|
||||||
log.Printf(">> %s; in operation %+v. passing are %+v", sorterName, operation, intervalsPassing)
|
|
||||||
ofThoseAreAccepted := processInterval(intervalsPassing, operation.SentToName, sorters)
|
|
||||||
|
|
||||||
combinationsAccepted += ofThoseAreAccepted
|
|
||||||
log.Printf(">> %s; results so far are %d", sorterName, combinationsAccepted)
|
|
||||||
|
|
||||||
intervalsFailingAndPassedToNextCheck := operation.getFailingIntervals(intervalsPassingOnThisStep)
|
|
||||||
log.Printf(">> %s; failing for the next step %+v", sorterName, intervalsFailingAndPassedToNextCheck)
|
|
||||||
intervalsPassingOnThisStep = intervalsFailingAndPassedToNextCheck
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf(">> %s; about to go into DEFAULT", sorterName)
|
|
||||||
intervalsAfterDefault := processInterval(intervalsPassingOnThisStep, s.DefaultState, sorters)
|
|
||||||
log.Printf(">> %s; after defaul. passing are %+v", sorterName, intervalsAfterDefault)
|
|
||||||
combinationsAccepted += intervalsAfterDefault
|
|
||||||
log.Printf(">> %s; results after default %d", sorterName, combinationsAccepted)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
18
main.go
18
main.go
|
@ -3,24 +3,12 @@ package main
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"sunshine.industries/aoc2023/day19"
|
"sunshine.industries/aoc2023/day18"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
log.Print("> starting run:")
|
log.Print("> starting run:")
|
||||||
|
|
||||||
// lnx{m>1548:A,A}
|
result := day18.Run()
|
||||||
// qqz{s>2770:qs,m<1801:hdj,R}
|
log.Printf("\n\nday18 result: %d\n****\n", result)
|
||||||
// kt{m>2215:R,x>3386:A,x<3107:R,R}
|
|
||||||
testSorter := day19.ReadSorterLine("kt{m>2215:R,x>3386:A,x<3107:R,R}")
|
|
||||||
log.Printf("my test sorter is %+v", testSorter)
|
|
||||||
|
|
||||||
simplified := day19.SimplifyOperation(testSorter)
|
|
||||||
log.Printf("> simplivied %+v", simplified)
|
|
||||||
|
|
||||||
detail := day19.ReadDetailLine("{x=787,m=2655,a=1222,s=2876}")
|
|
||||||
log.Printf("> detail %+v", detail)
|
|
||||||
|
|
||||||
result := day19.Run()
|
|
||||||
log.Printf("\n\nday19 result: %d\n****\n", result)
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue