advent2022

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

main.go (4146B)


      1 package main
      2 
      3 import (
      4 	"bufio"
      5 	"fmt"
      6 	"log"
      7 	"math"
      8 	"os"
      9 )
     10 
     11 func abs(a int) int {
     12 	return int(math.Abs(float64(a)))
     13 }
     14 func copysign(x, s int) int {
     15 	return int(math.Copysign(float64(x), float64(s)))
     16 }
     17 
     18 type Pos struct {
     19 	x, y int
     20 }
     21 
     22 func (p Pos) Diff(o Pos) Pos {
     23 	var diff Pos
     24 	diff.x = p.x - o.x
     25 	diff.y = p.y - o.y
     26 	return diff
     27 }
     28 func (p Pos) Dist(o Pos) int {
     29 	// returns maximum of distances by X or Y axis
     30 	diff := p.Diff(o)
     31 	dx := abs(diff.x)
     32 	dy := abs(diff.y)
     33 	if dx > dy {
     34 		return dx
     35 	} else {
     36 		return dy
     37 	}
     38 }
     39 
     40 type Rope struct {
     41 	id         int
     42 	head, tail Pos
     43 	visited    []Pos
     44 	attach     *Rope
     45 }
     46 
     47 func (r *Rope) AddVisited(p Pos) {
     48 	add := true
     49 	for _, v := range r.visited {
     50 		if v.x == p.x && v.y == p.y {
     51 			add = false
     52 			break
     53 		}
     54 	}
     55 	if add {
     56 		r.visited = append(r.visited, p)
     57 	}
     58 }
     59 func (r *Rope) MoveTail() {
     60 	if r.head.Dist(r.tail) > 1 {
     61 		d := r.head.Diff(r.tail)
     62 		if abs(d.x) > 1 {
     63 			d.x /= 2
     64 		}
     65 		if abs(d.y) > 1 {
     66 			d.y /= 2
     67 		}
     68 		r.tail.x += d.x
     69 		r.tail.y += d.y
     70 
     71 		r.AddVisited(r.tail)
     72 	}
     73 }
     74 func (r *Rope) Step(dx, dy int) {
     75 	r.head.x += dx
     76 	r.head.y += dy
     77 	r.MoveTail()
     78 
     79 	if r.attach != nil {
     80 		d := r.tail.Diff(r.attach.head)
     81 		if d.x != 0 || d.y != 0 {
     82 			r.attach.MoveBy(d)
     83 		}
     84 	}
     85 }
     86 func (r *Rope) MoveBy(p Pos) {
     87 	ax, ay := abs(p.x), abs(p.y)
     88 	if ax != 0 && ay != 0 {
     89 		// diagonal move
     90 		for {
     91 			if ax == 0 || ay == 0 {
     92 				break
     93 			}
     94 			r.Step(copysign(1, p.x), copysign(1, p.y))
     95 			ax--
     96 			ay--
     97 		}
     98 	}
     99 	if ax != 0 {
    100 		for i := 0; i < ax; i++ {
    101 			r.Step(copysign(1, p.x), 0)
    102 		}
    103 	} else if ay != 0 {
    104 		for i := 0; i < ay; i++ {
    105 			r.Step(0, copysign(1, p.y))
    106 		}
    107 	}
    108 }
    109 func (r *Rope) Move(dir string, dist int) {
    110 	switch dir {
    111 	case "L":
    112 		r.MoveBy(Pos{x: -dist})
    113 	case "R":
    114 		r.MoveBy(Pos{x: dist})
    115 	case "U":
    116 		r.MoveBy(Pos{y: dist})
    117 	case "D":
    118 		r.MoveBy(Pos{y: -dist})
    119 	default:
    120 		log.Fatal("Invalid direction", dir)
    121 	}
    122 }
    123 
    124 type Chain struct {
    125 	first *Rope
    126 	last  *Rope
    127 	size  int
    128 }
    129 
    130 func (c *Chain) Move(dir string, dist int) {
    131 	c.first.Move(dir, dist)
    132 	// c.Print()
    133 }
    134 func (c *Chain) Print() {
    135 	var points []Pos
    136 	for rope := c.first; rope != nil; {
    137 		points = append(points, rope.head)
    138 		points = append(points, rope.tail)
    139 		rope = rope.attach
    140 	}
    141 	PrintPoints(points)
    142 }
    143 func makeChain(size int) Chain {
    144 	var c Chain
    145 	c.size = size
    146 	c.first = &Rope{}
    147 	c.first.AddVisited(c.first.tail)
    148 
    149 	prev := c.first
    150 	for i := 1; i < size; i++ {
    151 		r := &Rope{id: i}
    152 		r.AddVisited(r.tail)
    153 		prev.attach = r
    154 		prev = r
    155 	}
    156 	c.last = prev
    157 	return c
    158 }
    159 
    160 func main() {
    161 	if len(os.Args) > 1 {
    162 		day09(os.Args[1])
    163 	} else if len(os.Args) == 1 {
    164 		fmt.Printf("usage: %s inputfile.txt\n", os.Args[0])
    165 	} else {
    166 		fmt.Println("No input data")
    167 	}
    168 }
    169 
    170 func day09(input_file string) {
    171 	fmt.Printf("day 09 input filename: %s\n", input_file)
    172 	input, err := os.Open(input_file)
    173 	if err != nil {
    174 		log.Fatal(err)
    175 	}
    176 	defer input.Close()
    177 	scanner := bufio.NewScanner(input)
    178 	var rope Rope
    179 	rope.AddVisited(rope.tail)
    180 	chain := makeChain(9)
    181 	for scanner.Scan() {
    182 		in := scanner.Text()
    183 		var dir string
    184 		var dist int
    185 		found, err := fmt.Sscanf(in, "%s %d", &dir, &dist)
    186 		if err != nil || found != 2 {
    187 			log.Fatal("Invalid input string", in, err)
    188 		}
    189 		rope.Move(dir, dist)
    190 		chain.Move(dir, dist)
    191 	}
    192 	if err = scanner.Err(); err != nil {
    193 		log.Fatal(err)
    194 	}
    195 
    196 	fmt.Println("Part 1:", len(rope.visited))
    197 	fmt.Println("Part 2:", len(chain.last.visited))
    198 
    199 	// PrintPoints(chain.last.visited)
    200 }
    201 
    202 func PrintPoints(points []Pos) {
    203 	var min, max Pos
    204 	for _, p := range points {
    205 		if p.x < min.x {
    206 			min.x = p.x
    207 		}
    208 		if p.y < min.y {
    209 			min.y = p.y
    210 		}
    211 		if p.x > max.x {
    212 			max.x = p.x
    213 		}
    214 		if p.y > max.y {
    215 			max.y = p.y
    216 		}
    217 	}
    218 	if min.x > 0 {
    219 		min.x = 0
    220 	}
    221 	if min.y > 0 {
    222 		min.y = 0
    223 	}
    224 	dx, dy := -min.x, -min.y
    225 	mx, my := max.x+dx+3, max.y+dy+3
    226 	grid := make([][]bool, my)
    227 	for i := 0; i < my; i++ {
    228 		grid[i] = make([]bool, mx)
    229 	}
    230 	for _, p := range points {
    231 		p.x += dx
    232 		p.y += dy
    233 		grid[p.y][p.x] = true
    234 	}
    235 	for y := my - 1; y >= 0; y-- {
    236 		for x := 0; x < mx; x++ {
    237 			if grid[y][x] {
    238 				fmt.Print("#")
    239 			} else if x == dx && y == dy {
    240 				fmt.Print("X")
    241 			} else {
    242 				fmt.Print(".")
    243 			}
    244 		}
    245 		fmt.Print("\n")
    246 	}
    247 }