2021 Day 19 Part 1
This commit is contained in:
parent
e77a316ee6
commit
7b389121b0
2 changed files with 251 additions and 0 deletions
|
|
@ -2,6 +2,7 @@ package aoc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -103,3 +104,9 @@ func Ints(text string) (ns []int) {
|
||||||
func ExLines(text string) []string {
|
func ExLines(text string) []string {
|
||||||
return strings.Split(strings.TrimSpace(text), "\n")
|
return strings.Split(strings.TrimSpace(text), "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MustScan(text, format string, dests ...interface{}) {
|
||||||
|
if _, err := fmt.Sscanf(text, format, dests...); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
244
2021/day19/main.go
Normal file
244
2021/day19/main.go
Normal file
|
|
@ -0,0 +1,244 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/jdkaplan/advent-of-code/aoc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
log.SetFlags(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
text := aoc.Input().ReadFile("day19.txt")
|
||||||
|
fmt.Println(part1(text))
|
||||||
|
}
|
||||||
|
|
||||||
|
func part1(text string) int {
|
||||||
|
scanners := parseInput(text)
|
||||||
|
|
||||||
|
offsets := make(map[int]Offset)
|
||||||
|
offsets[0] = Offset{tx: V{0, 0, 0}, rot: noRot}
|
||||||
|
|
||||||
|
solve:
|
||||||
|
for len(offsets) < len(scanners) {
|
||||||
|
log.Println("Solved", len(offsets), "/", len(scanners))
|
||||||
|
for _, s2 := range scanners {
|
||||||
|
if _, ok := offsets[s2.id]; ok {
|
||||||
|
// Already solved
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for id, o1 := range offsets {
|
||||||
|
s1 := scanners[id]
|
||||||
|
tx2, r2, ok := match(s1, s2, 12)
|
||||||
|
if ok {
|
||||||
|
offsets[s2.id] = Offset{
|
||||||
|
tx: tx2.rot(o1.rot).plus(o1.tx),
|
||||||
|
rot: rrot(o1.rot, r2),
|
||||||
|
}
|
||||||
|
continue solve
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return uniqPoints(scanners, offsets)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Offset struct {
|
||||||
|
tx V
|
||||||
|
rot R
|
||||||
|
}
|
||||||
|
|
||||||
|
func uniqPoints(scanners map[int]Scanner, offsets map[int]Offset) int {
|
||||||
|
uniq := make(map[V]struct{})
|
||||||
|
for _, s := range scanners {
|
||||||
|
o := offsets[s.id]
|
||||||
|
for p := range s.readings {
|
||||||
|
u := p.rot(o.rot).plus(o.tx)
|
||||||
|
uniq[u] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var ps []V
|
||||||
|
for p := range uniq {
|
||||||
|
ps = append(ps, p)
|
||||||
|
}
|
||||||
|
sort.Slice(ps, func(i, j int) bool {
|
||||||
|
pi, pj := ps[i], ps[j]
|
||||||
|
if pi.x != pj.x {
|
||||||
|
return pi.x < pj.x
|
||||||
|
}
|
||||||
|
if pi.y != pj.y {
|
||||||
|
return pi.y < pj.y
|
||||||
|
}
|
||||||
|
if pi.z != pj.z {
|
||||||
|
return pi.z < pj.z
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
return len(uniq)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseInput(text string) (scanners map[int]Scanner) {
|
||||||
|
scanners = make(map[int]Scanner)
|
||||||
|
for _, block := range strings.Split(text, "\n\n") {
|
||||||
|
lines := strings.Split(block, "\n")
|
||||||
|
|
||||||
|
var id int
|
||||||
|
aoc.MustScan(lines[0], "--- scanner %d ---", &id)
|
||||||
|
|
||||||
|
readings := make(map[V]struct{})
|
||||||
|
for _, line := range lines[1:] {
|
||||||
|
nums := strings.Split(line, ",")
|
||||||
|
x := aoc.MustInt(nums[0])
|
||||||
|
y := aoc.MustInt(nums[1])
|
||||||
|
z := aoc.MustInt(nums[2])
|
||||||
|
readings[V{x, y, z}] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
scanners[id] = Scanner{id, readings}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func match(s1, s2 Scanner, req int) (translation V, rotation R, ok bool) {
|
||||||
|
for a1 := range s1.readings {
|
||||||
|
for a2 := range s2.readings {
|
||||||
|
for _, rot := range uniqRot {
|
||||||
|
tx := a1.minus(a2.rot(rot))
|
||||||
|
|
||||||
|
var matches int
|
||||||
|
for p2 := range s2.readings {
|
||||||
|
p1 := p2.rot(rot).plus(tx)
|
||||||
|
if _, ok := s1.readings[p1]; ok {
|
||||||
|
matches++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if matches >= req {
|
||||||
|
return tx, rot, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return V{}, R{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
type Scanner struct {
|
||||||
|
id int
|
||||||
|
readings map[V]struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type V struct{ x, y, z int }
|
||||||
|
|
||||||
|
func (v V) plus(u V) V {
|
||||||
|
return V{v.x + u.x, v.y + u.y, v.z + u.z}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v V) minus(u V) V {
|
||||||
|
return V{v.x - u.x, v.y - u.y, v.z - u.z}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v V) dot(u V) int {
|
||||||
|
return v.x*u.x + v.y*u.y + v.z*u.z
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v V) rot(r R) V {
|
||||||
|
return rot(r, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
type R struct {
|
||||||
|
xx, xy, xz int
|
||||||
|
yx, yy, yz int
|
||||||
|
zx, zy, zz int
|
||||||
|
}
|
||||||
|
|
||||||
|
func rot(r R, v V) V {
|
||||||
|
x := r.xx*v.x + r.xy*v.y + r.xz*v.z
|
||||||
|
y := r.yx*v.x + r.yy*v.y + r.yz*v.z
|
||||||
|
z := r.zx*v.x + r.zy*v.y + r.zz*v.z
|
||||||
|
return V{x, y, z}
|
||||||
|
}
|
||||||
|
|
||||||
|
func rrot(r1, r2 R) R {
|
||||||
|
rx := V{r1.xx, r1.xy, r1.xz}
|
||||||
|
ry := V{r1.yx, r1.yy, r1.yz}
|
||||||
|
rz := V{r1.zx, r1.zy, r1.zz}
|
||||||
|
|
||||||
|
cx := V{r2.xx, r2.yx, r2.zx}
|
||||||
|
cy := V{r2.xy, r2.yy, r2.zy}
|
||||||
|
cz := V{r2.xz, r2.yz, r2.zz}
|
||||||
|
|
||||||
|
return R{
|
||||||
|
rx.dot(cx), rx.dot(cy), rx.dot(cz),
|
||||||
|
ry.dot(cx), ry.dot(cy), ry.dot(cz),
|
||||||
|
rz.dot(cx), rz.dot(cy), rz.dot(cz),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var uniqRot []R = genUniqRot()
|
||||||
|
|
||||||
|
var noRot = R{
|
||||||
|
1, 0, 0,
|
||||||
|
0, 1, 0,
|
||||||
|
0, 0, 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
func genUniqRot() []R {
|
||||||
|
id := R{
|
||||||
|
1, 0, 0,
|
||||||
|
0, 1, 0,
|
||||||
|
0, 0, 1,
|
||||||
|
}
|
||||||
|
rx := R{
|
||||||
|
1, 0, 0,
|
||||||
|
0, 0, -1,
|
||||||
|
0, 1, 0,
|
||||||
|
}
|
||||||
|
ry := R{
|
||||||
|
0, 0, 1,
|
||||||
|
0, 1, 0,
|
||||||
|
-1, 0, 0,
|
||||||
|
}
|
||||||
|
rz := R{
|
||||||
|
0, -1, 0,
|
||||||
|
1, 0, 0,
|
||||||
|
0, 0, 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
uniq := make(map[R]struct{})
|
||||||
|
|
||||||
|
push := func(ops ...R) {
|
||||||
|
r := id
|
||||||
|
for _, op := range ops {
|
||||||
|
r = rrot(op, r)
|
||||||
|
}
|
||||||
|
uniq[r] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
rotations := []R{rx, ry, rz}
|
||||||
|
|
||||||
|
push()
|
||||||
|
for _, r1 := range rotations {
|
||||||
|
push(r1)
|
||||||
|
for _, r2 := range rotations {
|
||||||
|
push(r1, r2)
|
||||||
|
for _, r3 := range rotations {
|
||||||
|
push(r1, r2, r3)
|
||||||
|
for _, r4 := range rotations {
|
||||||
|
push(r1, r2, r3, r4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var rs []R
|
||||||
|
for r := range uniq {
|
||||||
|
rs = append(rs, r)
|
||||||
|
}
|
||||||
|
return rs
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue