#AoC 2022 | Day 9 | Solutions & Spoilers

1800 messages · Page 2 of 2 (latest)

grave delta
#

just update the Knot object

lost steppe
#

and access at index and index-1

grave delta
#

whole ass class there, just update the x, y attributes 😄

rugged heron
grave delta
#

wooooo

lost steppe
#

my thing was roughly

last = head
for cur in tail:
  # update cur wrt last
  last = cur
```which was quite convenient
rugged heron
#

hecking finally

#

That hurt my blood pressure

grave delta
#

Which route did you take?

lost steppe
#

me?

grave delta
#

no, doggo 😄

lost steppe
#

oh, which fix

vivid vergeBOT
#

events/advent_of_code/2022/9/python/script_day9.py lines 104 to 109

for index, (previous, knot) in enumerate(pairwise(knots), 1):
    new_knot = move_knot(previous, knot)
    knots[index].x = new_knot.x
    knots[index].y = new_knot.y

visited.add((knots[-1].x, knots[-1].y))```
grave delta
#

heh noice

#

behold the power of mutable objects for getting shit done

frosty orbit
#

functional programmers seething

grave delta
#

mutate all the things!

lost steppe
#

pithink

for index in range(1, len(knots)):
    knots[index] = move_knot(knots[index-1], knots[index])

placid torrent
#
    for direction, distance in data:
        for _ in range(distance):
            head = (head[0] + direction[0], head[1] + direction[1])
            for i, (prev, next) in list([head, *tails]).windowed(2).enumerated():
                if list(zip(prev, next)).mapped(lambda i: abs(i[0] - i[1])).max() > 1:
                    tails[i] = move_towards(prev, next)
            visited[tails[-1]] = True
lost steppe
#

(and is arguably simpler)

rugged heron
#

alright alright alright

lost steppe
placid torrent
lost steppe
#

I still need to either pick a better hash table impl or implement my own thing...

lost steppe
#

my solution is almost >1ms :/

#

too close for comfort

#

and I know the hash table is a big chunk of the runtime

#

like 2/3

#

I suspect my 1ms limit will be impossible soon enough, but not quite yet 😛

novel berry
#

hope they have some cryptography based question this time. I love some of those

grim lagoon
#

So uh is there a better way to do this than brute forcing all 12 directions and moving the tail?

#

For part 1

shy monolith
#

yes

valid ember
#

I just knew part two would be ||a longer rope||, messing up the logic from my solution to part 1.

#

It's not very efficient, but ¯_(ツ)_/¯

lost steppe
#

I was confused about the warning about new types of movements in the p2 description

timber brook
#

I bet tomorrow's will also involve a coordinate plane

lost steppe
#

turns out my logic was fine already

timber brook
#

maybe some matplotlib

lost steppe
timber brook
#

Remember the continuity

#

We're at the bottom of a ravine

spiral storm
#

If I have an issue with my code Im allowed to ask here right?

timber brook
#

It'll probably have something to do with river currents

lost steppe
frosty orbit
#

theres a story !?

timber brook
#

we're elves hiking through a forest.

#

And you're a weird looking elf

lost steppe
#

🧝

cosmic shore
timber brook
frosty orbit
lost steppe
#

||x||elf

timber brook
#

||s||elf

valid ember
# lost steppe turns out my logic was fine already

Ah right yeah. My logic for part 1 was to move the tail to the intersection of two sets: the cardinal neighbours of the head, and the ordinal+cardinal neighbours of the tail. It turns out this doesn't work if the head can move diagonally (the sets don't intersect).

lost steppe
#

ok, time to actually be at my keyboard to improve my solution from this morning

valid ember
#

So for part 2, I found the intersection of the ordinal+cardinal neighbours of both sets, then picked the coordinate out of these that has the minimum Manhattan distance to the head.

#

By head, I mean the previous knot in the rope.

lost steppe
#

why not just: if >1 L∞ norm, then move x and y closer by 1 if you can?

#

i.e. if max(xdist, ydist) > 1, move closer

#

and the move closer part is just x += sign(dx)

#

(same for y)

cosmic shore
valid ember
valid ember
golden socket
#

we're definitely going to have to like, climb out of the ravine somehow

#

def some 3d pathfinding thing

light echo
#

Finally got it

#

wow

#
from collections import namedtuple


def tail_in_range(tail: list[int], head: list[int]) -> bool:
    return (tail[0] - head[0] in [-1, 1, 0]) and (tail[1] - head[1] in [-1, 1, 0])


def position_change(val_tail: int, val_head: int) -> int:
    if val_tail - val_head > 0:
        return -1
    elif val_tail - val_head < 0:
        return 1
    else:
        return 0


def solve(amt: int) -> int:
    Movement: namedtuple = namedtuple("Movement", "d n")
    movements = [Movement(d, int(n)) for d, n in [m.split() for m in open("input.txt").read().strip().splitlines()]]

    seen = list()
    knots: list[list[int]] = [[0, 0]] * amt
    seen.append(knots[0])
    changes: dict[str: list[int]] = {"U": [0, 1], "D": [0, -1], "L": [-1, 0], "R": [1, 0]}

    for move in movements:
        for _ in range(move.n):
            dx, dy = changes[move.d]
            knots[0] = [knots[0][0] + dx, knots[0][1] + dy]

            for i in range(1, amt):
                tail = knots[i]
                head = knots[i - 1]

                if not tail_in_range(tail, head):
                    match move.d:
                        case 'U' | 'D':
                            knots[i] = [tail[0] + position_change(tail[0], head[0]),
                                        tail[1] + position_change(tail[1], head[1])]

                        case 'L' | 'R':

                            knots[i] = [tail[0] + position_change(tail[0], head[0]),
                                        tail[1] + position_change(tail[1], head[1])]
            if knots[-1] not in seen:
                seen.append(knots[-1])

    return len(seen)


print(f"Part 1: {solve(2)}")
print(f"Part 2: {solve(10)}")```
#

it used to be way longer

#

then decided to start from scratch

#

Thoughts/improvements?

fossil hemlock
#

part 1 :

from dataclasses import dataclass

def read_file(filepath):
    with open(filepath) as file:
        return file.read()

@dataclass
class Coord():
    x: int
    y: int
    def __hash__(self):
        return hash((self.x, self.y))

head_movements = {
    "R": lambda coord: Coord(coord.x + 1, coord.y),
    "L": lambda coord: Coord(coord.x - 1, coord.y),
    "U": lambda coord: Coord(coord.x, coord.y + 1),
    "D": lambda coord: Coord(coord.x, coord.y - 1),
}

def dist(first, second):
    return Coord(x=first.x-second.x, y=first.y-second.y)

def norm(coord):
    n = Coord(x=0, y=0)
    if coord.y != 0:
        n.y=coord.y//abs(coord.y)
    if coord.x != 0:
        n.x=coord.x//abs(coord.x)
    return n

def tail_movements(tail_coord, head_coord):    
    d = dist(head_coord, tail_coord)
    if abs(d.x) <= 1 and abs(d.y) <= 1:
        return tail_coord

    n = norm(d)   
    return Coord(x=tail_coord.x+n.x, y=tail_coord.y+n.y)

def part1(content):
    head_position = tail_position = Coord(x=0, y=0)
    visited = {head_position}

    for instr in [l.split(" ") for l in content.strip().split("\n")]:
        for i in range(int(instr[1])):
            head_position = head_movements[instr[0]](head_position)
            tail_position = tail_movements(tail_position, head_position)
            if tail_position not in visited:
                visited.add(tail_position)

    return len(visited)

input_data = read_file("../input.txt")
print(part1(input_data))
misty cradle
#

part2 is hard

golden socket
#

it's just like part 1

misty cradle
#

idk how to do p2

#

trying to figure out how to do the diagonal stuff

fossil hemlock
#

and part2 with the same design

def part2(content):
    nodes = [Coord(x=0, y=0)] * 10
    visited = {Coord(x=0, y=0)}

    for instr in [l.split(" ") for l in content.strip().split("\n")]:
        for i in range(int(instr[1])):
            nodes[0] = head_movements[instr[0]](nodes[0])
            for i, n in enumerate(nodes):
                if i == 0:
                    continue
                coord = tail_changes(n, nodes[i-1])
                if coord == n:
                    break
                nodes[i] = coord
            if nodes[-1] not in visited:
                visited.add(nodes[-1])

    return len(visited)
#

took me 1h TT__TT

#

there's more than 3 indentations levels too, which is unacceptable

idle flame
#

it accounts for CRLF and LF at the same time

timber brook
idle flame
#

for real?

timber brook
#

!pep 278

vivid vergeBOT
#
**PEP 278 - Universal Newline Support**
Status

Final

Python-Version

2.3

Created

14-Jan-2002

Type

Standards Track

idle flame
#

oh shoot

#

nice

timber brook
#

it's the default in python 3

devout locust
#

what am i misunderstanding about part2?
this step doesnt make sense to me

timber brook
#

when the head starts going up, each knot needs to go 1 in the direction of the next knot

#

(if the distance is > 1)

devout locust
#

so why isnt that panel like ⬇️

timber brook
#

after 2 moves, 3 is not directly to the left of 3. It's slightly down as well.

heady pike
#
def move_rope(data, length):
    visited = set((0, 0))
    rope = [np.array([0, 0]) for _ in range(length)]

    for row in data.splitlines():
        dr, x = row.split()
        for _ in range(int(x)):
            rope[0] += {'R': (1, 0), 'L': (-1, 0), 'U': (0, -1), 'D': (0, 1)}[dr]
            for i in range(1, len(rope)):
                dir_vector = rope[i-1]-rope[i]
                if abs(dir_vector).max() > 1:
                    rope[i] += dir_vector.clip(-1, 1)
                    if i == len(rope)-1:
                        visited.add(tuple(rope[i]))
    return len(visited)```
1st day after "learning" numpy. Working with vectors is a lot nicer than using tuples.
timber brook
#

You know you can use * on the list?

#

numpy will unpack all the list instances to its own values

#

np.array([[0, 0]] * length)

heady pike
#

ah thank you, thought it was gonna do the usual thing

timber brook
#

You could also do ```py

np.array([0] * 10 * 2).reshape([10, 2])
array([[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0],
[0, 0]])

devout locust
timber brook
#

all move (possibly diagonally) to stay in range of the next knot

devout locust
#

but theyre in range if only 1 moves diagonally and everything shifts to the right? no?

willow cedar
#

you're moving every time instead of moving when the one in front of you is over 1 away.

heady pike
#

each part moves relative to the previous one

#

not relative to the head

timber brook
#

so you should use pairwise

heady pike
#

any other things that could've been done smarter with numpy?

willow cedar
heady pike
#

well just for ease of use operations wise

willow cedar
heady pike
#

oh you used complex numbers that solves any need to use arrays

devout locust
#

whats the middle state then

willow cedar
#

start:

......
......
......
....H.
4321.. 

h moves 1 up:

......
......
....H.
......
4321.. 

1 follows:

......
......
....H.
....1.
432... 

2 follows:

......
......
....H.
...21.
43.... 

etc....

......
......
....H.
.4321.
5.....
devout locust
#

after step "1 follows:" knot 2 can move to where 1 was and be in range

#

why would it move diagonally up to the right and not just right

willow cedar
#

if they're not on the same row/col

timber brook
#

What I did was put the distance into a numpy array and used the sign function

#

np.sign(head - tail)

#

Ironically, I think I did part 1 wrong, but still ended up with the right answer.

willow cedar
devout locust
#

i am actually blind

#

lmao i did part1 without taking this into account, false positive i guess

heady pike
#

i think the easiest solution for part1 makes any consideration of that unnecessary

willow cedar
#

You got the right count, but the wrong places. That is lucky I'm surprised the input didn't account for that.

heady pike
#

if abs(rope[i+1]-rope[i])>=2: how does this work with c numbers?

willow cedar
timber brook
#

negatives are important for direction.

heady pike
#

ah, math has been too long

grave delta
fossil hemlock
#

^ I used litteraly the same movement algo

lost steppe
#

eww, using magnitude >=2 is just...disturbing

#

it's not the right metric

heady pike
#

you can just do: move tail to last head position

#

which doesnt work in part2

lost steppe
willow cedar
heady pike
#

you'd be screwed if it was something other than 2 right?

fossil hemlock
#

if the distance <= 1; do nothing

#

less indentation

#

indentations are evil

willow cedar
#

well it was better than my original method, which calculated all the neighbors and then checked for membership.

near = lambda x: {x+complex(dx,dy) for dx in (-1,0,1) for dy in (-1,0,1)}
if rope[i+1] not in near(rope[i]):
lost steppe
#

0 is ok, 1 is not, 2 is ok, 3 and above I think is not ok

heady pike
#

i guess still a neat trick if it works

#

i guess you could also just do max(abs(x.real),abs(x.imag)) > 1

lost steppe
heady pike
#

haha

fossil hemlock
#
def next_tail_pos(tail_coord, head_coord):    
    d = dist(head_coord, tail_coord)
    if abs(d.x) <= 1 and abs(d.y) <= 1:
        return tail_coord

    n = norm(d)   
    return Coord(x=tail_coord.x+n.x, y=tail_coord.y+n.y)

this is how I did it

timber brook
#

is norm() like the sign() function?

lost steppe
#

normalize would be the better term

fossil hemlock
#

idk what's the sign function ?

timber brook
#

!d numpy.sign

vivid vergeBOT
#

numpy.sign(x, /, out=None, *, where=True, casting='same_kind', order='K', dtype=None, subok=True[, signature, extobj]) = <ufunc 'sign'>```
Returns an element-wise indication of the sign of a number.

The [`sign`](https://numpy.org/devdocs/reference/generated/numpy.sign.html#numpy.sign "numpy.sign") function returns `-1 if x < 0, 0 if x==0, 1 if x > 0`. nan is returned for nan inputs.

For complex inputs, the [`sign`](https://numpy.org/devdocs/reference/generated/numpy.sign.html#numpy.sign "numpy.sign") function returns `sign(x.real) + 0j if x.real != 0 else sign(x.imag) + 0j`.

complex(nan, 0) is returned for complex nan inputs.
timber brook
#

give it a number and it will return -1 if the number is negative, +1 if it's positive, and 0 if it's 0

fossil hemlock
#

yes

heady pike
#

i used clip

#

also did the trick

lost steppe
#

is normalization even meaningful in L^∞ norm?

timber brook
#

To me, normalization is clamping a number between -1,1

fossil hemlock
#

it gives you the direction to go to get closer to the head

heady pike
#

yea but normalization wouldn't work for (2,1)

lost steppe
fossil hemlock
#

and since the max movement distance is limited to [-1, 1], the direction and the "offset" for the next move are the same thing

lost steppe
runic mirage
lost steppe
#

you scale to normalize

#

clamping is...clamping

fossil hemlock
heady pike
#

can't you just divide x/abs(x)

#

if not 0 obviously

fossil hemlock
lost steppe
#

x/||x|| is normalizing in general

#

for some norm ||.||

fossil hemlock
lost steppe
#

only the unit circle in our L^∞ norm is a square 😛

lost steppe
fossil hemlock
#

I'm just interested

#

I hate my implementation if the 2 ifs to check if the x/y are 0 or not

grave delta
fossil hemlock
#

it's ugly af

lost steppe
fossil hemlock
#

which is ?

#

nvm

#

min(max) is prob 2 ifs anyway, instead of one

lost steppe
runic mirage
lost steppe
#

imo

runic mirage
#

or even better

fossil hemlock
#

it's prettier, it performs slower tho

#

y.y

runic mirage
#
x/abs(x or 1)```
fossil hemlock
#

I love you

#

I'm stealing this

lost steppe
#

err, didn't mean to reply to that

runic mirage
#

yup

willow cedar
#

ok I think this looks a little messier, but no abs(complex) for you puritans.

if not(-1<=(a := rope[i].real - rope[i+1].real)<=1):
    rope[i+1] += a//abs(a)
if not(-1<=(b := rope[i].imag - rope[i+1].imag)<=1):
    rope[i+1] += b//abs(b)*1j
fossil hemlock
#

*1j ?

willow cedar
fossil hemlock
#

witchcraft

grave delta
#

can do anything you want if you can just make up numbers

lost steppe
#

my impl went like

runic mirage
lost steppe
#

signum is what you want here

fossil hemlock
lost steppe
#

math good :V

runic mirage
#

This is my implementation for the tail following

lost steppe
runic mirage
#

I suppose I could use list with enumerate instead of a dict

fossil hemlock
#

I tried to enumerate a slice of items except the first one and I got so confused : the index python gives you is the index in the slice, not the index in the list

lost steppe
#

separating the head and the tail makes the code a bit neater

willow cedar
# fossil hemlock oh no I just hate math in general. j or i it's not really the problem lol

so complex numbers are also represented as tuples of 2 numbers, which is what we have here.
the nice thing about complex numbers though, unlike tuples of numbers, is that you can add/subtract them.
for instance here was how I handled moving the head:

path = {'L':-1,'R':1,'U':1j,'D':-1j}
rope[0] += path[line[0]]

whatever the direction is, I just add that to the head.

fossil hemlock
#

vade retro

runic mirage
fossil hemlock
#

you couldn't be bothered to write your own abstraction so you used one that did pretty much what you wanted

#

I wrote my own abstraction, I feel dumb now

lost steppe
willow cedar
#

other than anything I accidentally tursly right cause I hate excess code.

lost steppe
#

but this is actually a cute trick 😭

willow cedar
fossil hemlock
#

witchcraft, again

#

burn the witches

lost steppe
#

but yeah, I don't code golf much anymore

#

I had fun code golfing in C a while back

#

it teaches you weird corners of the language...

lost steppe
#

(alcohol pun not intended)

fossil hemlock
#

a shame there's no 3 ways operator in python

lost steppe
#

there was one back in the day

#

__cmp__

fossil hemlock
#

really ? why did it get removed ?

lost steppe
#

idk the rationale

#

stuff like sorted could also accept cmp functions rather than key functions

fossil hemlock
golden socket
novel berry
lost steppe
#

C++ recently got <=>

novel berry
#

else the golf would've been way loweer

lost steppe
#

(spaceship operator)

fossil hemlock
lost steppe
#

python has made you soft 😛

fossil hemlock
#

it's a whole new concept for me

lost steppe
#

I guess coming from something like python it might be a bit much

novel berry
fossil hemlock
#

well it's pretty unique right : you declare a variable and call a function on it; now the variable is destroyed

lost steppe
#

coming from C and C++ it felt not so weird

fossil hemlock
#

if you move everything I guess not

golden socket
#

I'm pretty sure modern C++ uses ownership semantics a lot

fossil hemlock
#

but I haven't given up yet, am doing the advent of code in all 3 languages (at least trying)

lost steppe
#

std::move is only a cast

#

but sure, there is ownership semantics in things like unique_ptr

lost steppe
#

ok cool, moving away from a hash map slashes my time to 1/3

#

<1ms dream lives another day

golden socket
#

how do you maintain the count of what you've visited? BTreeSet?

timber brook
#

hash a struct?

golden socket
#

hm?

cosmic shore
sinful tulip
#
def solve(n: int, data: str) -> int:
    ks = [0] * n
    v = {0}

    def update(h, t):
        d = h - t
        return (
            t
            if abs(d) < 1.5
            else t
            + complex(
                int(d.real / (abs(d.real) - 0.1)), int(d.imag / (abs(d.imag) - 0.1))
            )
        )

    for d, s in get_tuples(data):
        w = {"U": 1j, "D": -1j, "R": 1, "L": -1}[d]
        for _ in range(int(s)):
            ks[0] = ks[0] + w
            for i in range(1, n):
                ks[i] = update(ks[i - 1], ks[i])
            v.add(ks[-1])

    return len(v)
trim garden
cosmic shore
#

thank you, I will double check my follow(self, other) function and make sure im covering all the cases

trim garden
#

because sometimes when i call follow() it doesn't go to any of the if statements

warm spindle
trim garden
#

if i put a else clause at the end the function goes to that a lot

#

which shouldn't be happening

cosmic shore
#

hmmm ok

#

i wonder which cases im not catching

trim garden
#

remember that with the 10 knots there are more possible movement cases

#

as stated on the website

#

then with the 2 knots

cosmic shore
#

i see that it says that, but it also says

Each knot further down the rope follows the knot in front of it using the same rules as before.

so im not sure what these new rules / cases are

trim garden
#

consider this example, what happens if you move up twice?

#

when knot 5 is moved, knot 6 will be 2 down and 2 to the left from it

#

on the 2nd move

cosmic shore
#

ahh, so now it isnt guaranteed that a knot will be at most, 2 units away from another knot

#

now it can be 3?

trim garden
#

by 2 units do you mean 3 Manhattan distance?

cosmic shore
#

yes

trim garden
#

small thing: the way you have your logic structured would be a great use for match

cosmic shore
#

in what way?

trim garden
#

some other cases are also slipping through upon further inspection

#

such as this # other.x = self.x + 1, other.y = self.y - 1

cosmic shore
#

i understand for the move(self, direction) command I can change it to python def move(self, direction): match direction: case ‘R’: self.x += 1

trim garden
#

perhaps an iterative approach would be less error-prone then manually checking all the possible conditions

#

since there are more of them now

warm spindle
cosmic shore
trim garden
#

consider the itertools.permutations function

cosmic shore
#

i will take a look at the documentation for it right now

trim garden
#

also, what's the relationship between other.x - self.x, other.y -self.y and the actual change you want in the position?

#

that would be something to consider

cosmic shore
#

im not sure im totally following, but I will try messing around with it for a bit

trim garden
#

adjacency matrix !! (jok)

lost steppe
#

conceptually a 2d array with all points with -n ≤ x, y ≤ n

#

for some large enough n

golden socket
lost steppe
#

200 is enough I think, I went a bit higher to be safe

#

oh cool, the point counting went from like 2/3 of runtime to <10%

#

I wonder if the simulation of the thing could be made faster, I can see some potential for simd, but that feels like way too much work

golden socket
lost steppe
#

why?

#

I think it's just hashmaps having a really big constant factor

golden socket
#

you have to loop over all points to see which are true right?

lost steppe
#

the complexity should be the same

#

no

#

when I try setting a point, if the current value is false increment a counter as well

golden socket
#

ah

#

theoretically you can change the hash to not use siphash

lost steppe
#

right, it would probably help

#

though the array should still be faster

#

this is basically a hashmap with perfect hashing

golden socket
#

yeah it's direct addressing

lost steppe
#

hmm, I think simd is actually doable

#

but a bit awkward

#

you compute manually until you have a diagonal like this, i.e. the head (1) is 5 along, next tail piece lags one behind, next tail piece one more behind, and so on

....1
...2
..3
.4
5
#

the next element on row 2 depends on 2 and 1

#

the next element on row 3 depends only on 2 and 3

#

and so on

#

so no data dependencies

#

so you can compute the next diagonal in parallel

cosmic shore
lost steppe
#

but it could give a significant speedup

#

if you shove coords in 8 or 16 bits, potentially a really big speedup

#

4x or maybe 8x

cosmic shore
#

im covering the cases where the tail a knot needs to move North, East, South, West, Northeast, Northwest, Southeast, Southwest

#

idk what other possible moves it could even make

lost steppe
#

the tail can only move in those directions

cosmic shore
#

im confused lol my brain is melting

lost steppe
#

how are you covering those cases?

cosmic shore
#

with a very long if/elif chain that im sure can be made better but its all i got at the moment

lost steppe
#

that's not all cases in part 2

#

you can get a case like

..B
...
A..
#

(and yeah, there are better ways to do this that is not hard coding cases)

cosmic shore
#

you can have that case?

#

oh if B is previously northeast of A, and then B gets pulled northeast again due to C moving somewhere

lost steppe
#
...  ..3  ..3
..3  ...  ..2
.2.  .2.  ...
1..  1..  1..
cosmic shore
#

i see

#

hmmmm

lost steppe
#

(I probably reversed the numbering the problem used)

cosmic shore
#

i understand the scenario youre trying to describe

#

i think i know what i need to add to account for that, but still lost on how to use itertools.permutations to make it all very clean and concise

lost steppe
#

you can look at the differences in coordinates

#

that will tell you if you need to move (chrcking distance) and in which direction to move

worthy igloo
#

oh my god

#

i bet someone else did this as well

#

but this working

#

made me so happy

#

LMAO

#

GLORIOUS

golden socket
#

my why didn't you just use a loop

dapper anvil
#

can't have a loop in your rope

wind hawk
#

U 1
L 1
D 1
R 1

#

loop

dapper anvil
#

U 2
D 2
L 1
R 1
L 1
R 1
B A

wind hawk
#

hacks

spare ice
#

can anyone explain to me how to do part 2 of todays challenge? I'm not quite sure on how to do it.

analog bolt
#

basically in part 1 you have a head knot and a tail knot

#

and in part 2 you get a chain of multiple knots

spare ice
#

yup i understand that part

analog bolt
#

I was trying to give a better explanation but came up short sorry XD

spare ice
#

yea no like i understand the problem its asking

#

its just im not quite sure on how to solve it

analog bolt
#

if you have a chain: T-1-2-3-4-H
Then 4 behaves as H's tail, and as 3's head

#

H is the only one that moves and then you have to update all the tails in a descending order, hopefully with the same logic you already have defined for part 1

spare ice
#

well the way i solved part 1 was just to simulate the entire movement and then log the unique positions that the tail visited

#

however the problem i have with part 2 is this (the example)

#

im not sure why the 4,3,2 also moves up with 1

cerulean gorge
#

they move up-right with 1

spare ice
#

but cant they stay below and that would still be okay? as 2 is diagonal to 1?

cerulean gorge
#

2 would not touch 1

spare ice
#
......
......
....H.
....1.
5432.. 

could it not be like this?

#

or am i missing smth

cerulean gorge
#
....H.
....1.
432...

if they didn't move

#

5 is covered

analog bolt
#

did you make a visualisation for this day die?

cerulean gorge
#

yep

analog bolt
#

can I see?

cerulean gorge
spare ice
#

oh damn thats neat

spare ice
cerulean gorge
#

there's a column separating them

rugged heron
cerulean gorge
#

Start:

......
....H.
4321..

Head moves:

....H.
......
4321..

1 Follows:

....H.
....1.
432...

2 Follows:

....H.
...21.
43....

3 Follows:

....H.
..321.
4.....

4 Follows:

....H.
.4321.
......
warm spindle
cerulean gorge
#

!pypi nurses_2

vivid vergeBOT
warm spindle
#

cool

analog bolt
spare ice
cerulean gorge
spare ice
cerulean gorge
spare ice
#

i must have misread that

cerulean gorge
#

illustrated with the example

.....    .....    .....
.....    ..H..    ..H..
..H.. -> ..... -> ..T..
.T...    .T...    .....
.....    .....    .....

.....    .....    .....
.....    .....    .....
..H.. -> ...H. -> ..TH.
.T...    .T...    .....
.....    .....    .....
spare ice
#

when i first did the problem

#

okay yup i understand why now

#

thank you so much

cerulean gorge
#

np

stone vapor
#

is it guaranteed that s is always at the bottom left corner?

lost steppe
#

you know what would be fun? up the ante and make this problem higher dimensional

#

RLUDIO

golden socket
odd star
#

s can be wherever you want it to be since it's technically an infinite board

stone vapor
#

oh wait yeah lmao

odd star
#

I think most people would've had the start be 0,0

stone vapor
#

i was gonna use numpy for this but ngl i might just use a cartesian plane

odd star
#

but you could set it to like 69,420 and the code would still work

lost steppe
dapper anvil
#

so i didn't specify a starting location and only counted the distance.

lost steppe
#

against my better judgement I wrote the setup to be able to have simd...

odd star
lost steppe
#

now I would have to think about how to actually simd this operation

    for j in i..N {
      let cur_x = diag_x[j];
      let head_x = diag_x[j-1];
      let dx = head_x - cur_x;

      let cur_y = diag_y[j];
      let head_y = diag_y[j-1];
      let dy = head_y - cur_y;

      let distx = dx.abs();
      let disty = dy.abs();

      if distx.max(disty) >= 2 {
        diag_out_x[j] = diag_x[j] + dx.signum();
        diag_out_y[j] = diag_y[j] + dy.signum();
      }
    }
#

(and learn how I do such intrinsics in rust)

undone apex
#

hey any1 minds to look into my code i get the example correctly everything moves as it should but i cant get my puzzle input to work i have no clue whats wrong

odd star
#

since at the start the rope should be 0,0 distance from the start

odd star
#

it'd be limited at 4D I think

#

since it'd be annoying to introduce new LR equivalents for each newer dimension

undone apex
#

id be very grateful ;/

#

dm me if any1 can help

golden socket
dapper anvil
#

that way you can use any hashable as a dimension lemon_thinking

#

basically what i had in mind for my infinity tictactoe

#

you could also use lower/upper case, or odd and even numbers.

odd star
#

fair

lost steppe
#
1j 5
limber gazelle
#

am i did something wrong if i used classes?

#

did i miss a easier way?

odd star
#

nothing wrong with using classes

#

you could argue complex numbers are easier but if you're not familiar with them classes work just fine

grim lagoon
#

does this logic make sense for part 1? if we need to move the tail along along a horizontal or vertical axis, the direction we move it will be the same direction we just moved the head, so I can just reuse that command on the tail:

import numpy as np

def need_move(h, t):
    return (max(abs(h[0] - t[0]), abs(h[1] - t[1])) > 1)

with open ("moves.txt", "r") as f:
    moves = f.read().split('\n')

directions = {
    'U': np.array([0,1]),
    'D': np.array([0,-1]),
    'L': np.array([-1,0]),
    'R': np.array([1,0])
    }

head = np.array([0,0])
tail = np.array([0,0])

for m in moves:
    d, dis = m.split()
    for _ in range(int(dis)):
        head = head + directions[d]
        if need_move(head, tail):
            dx = head[0] - tail[0]
            dy = head[1] - tail[1] 

            if (dx and not dy) or (dy and not dx):
                tail + directions[d]
            else:
                #diagonal move
                pass
timber brook
#

that's basically what i did.

grim lagoon
#

now i just need to figure out the diagonal logic

cerulean gorge
#

works for part 1...

grim lagoon
timber brook
#

look at using numpy.sign on the array([dx, dy])

cerulean gorge
#

...

grim lagoon
timber brook
#

also, the if check should compare if abs(dx) > 1 or abs(dy) > 1

#

need_move is basically calculating dx and dy

grim lagoon
#

yeah, but i never return it

#

just using it as a bool to determine if the tail is in range of the head

timber brook
#

Looks good to me. It probably works.

#

except I would add the sign to the tail via +=

grim lagoon
#

oh yeah typo

timber brook
#

tail += np.sign(np.array([dx, dy]))

grim lagoon
cerulean gorge
timber brook
#

I didn't read that function

cerulean gorge
grim lagoon
#

wow, of course theres a function for that

cerulean gorge
#

alternatively, you can vectorize your operations: abs(head - tail).max()

grim lagoon
#

i guess i shouldnt be surprised for using numpy

#

i used wanted easy array addition haha

grim lagoon
cerulean gorge
grim lagoon
#

guess i should have paid attention in algos

cerulean gorge
#

it's more math-y than algo-y, might not be covered

grim lagoon
#

yeah i never took linear algebra

#

highest i ever got was calc 2

#

didnt need it

cerulean gorge
wind hawk
#

(a^n+b^n+...)^1/n

grim lagoon
#

so like

def need_move(h, t):
    return np.linalg.norm(h- t, ord=np.inf) > 1
wind hawk
#

yeah i think that works

odd star
grim lagoon
#

cool

#

so now i just need to figure out how to do diag shit

stone vapor
#

i genuinely cannot read

#

made a whole vis with matplotlib only to realize that it was only asking for the places tail 9 visited, not the entire rope

stone vapor
#

holy shit lmao

odd star
#

an infinitely long rope would've been interesting

cerulean gorge
#

i don't think the tail would move

odd star
#

could've asked how many knots got moved out from the start

#

instead of tail movement

warm spindle
#

If it's infinitely long, it would just be 1

blissful pendant
#
def newposition(ly,lx,fy,fx):
#"l" and "f" used as in leader and follower
    ymoved = False
    xmoved = False
    
    if ly-fy > 1:
        fy+=1
        ymoved = True
    elif ly-fy < -1:
        fy-=1
        ymoved = True
    
    if lx-fx > 1:
        fx+=1
        xmoved = True
    elif lx-fx < -1:
        fx-=1
        xmoved = True
    
    if xmoved and not ymoved:
        fy = ly
    elif ymoved and not xmoved:
        fx = lx
    
    return fy,fx
#

my function for moving the follower

analog bolt
tame lynx
odd star
#

I would probably use like a defaultdict and just move a knot when it's needed

#

depends on if a knot moving back to the start counts tho

warm spindle
grim lagoon
#

or wait that is else

#

lol

cerulean gorge
wind hawk
lost steppe
#

I'm sad, my simd didn't end up much faster 😦

#

like 5% faster at best

#

maybe forcing the problem into a simd-able state makes things slower overall

analog bolt
lost steppe
lost steppe
wind hawk
#

pretty sure how many knots is actually a easier problem than part 1 of the original

lost steppe
#

special instructions that can compute things in parallel

wind hawk
#

because you only need to track the head

grim lagoon
#

would this be considered fine or is it better syntactically to just do 4 if ... and ...s

                if dx > 0:
                    if dy > 0:
                        tail += directions['RU']
                    tail += directions['RD']
                else:
                    if dy > 0:
                        tail += directions['LU']
                    tail += directions['LD']
placid torrent
grim lagoon
#

oh

#

do you wanna see it in the full block

wind hawk
#

sure

grim lagoon
#
import numpy as np

def need_move(h, t):
    #chebyshev distance
    return np.linalg.norm(h - t, ord=np.inf) > 1

with open ("moves.txt", "r") as f:
    moves = f.read().split('\n')

directions = {
    'U': np.array([0,1]),
    'D': np.array([0,-1]),
    'R': np.array([1,0]),
    'L': np.array([-1,0]),
    'RU': np.array([1,1]),
    'RD': np.array([1,-1]),
    'LU': np.array([-1,1]),
    'LD': np.array([-1,-1])
    }

head = np.array([0,0])
tail = np.array([0,0])

for m in moves:
    d, dis = m.split()
    for _ in range(int(dis)):
        head = head + directions[d]
        if need_move(head, tail):
            dx = head[0] - tail[0]
            dy = head[1] - tail[1] 

            if (dx and not dy) or (dy and not dx):
                tail += directions[d]
            else:
                if dx > 0:
                    if dy > 0:
                        tail += directions['RU']
                    tail += directions['RD']
                else:
                    if dy > 0:
                        tail += directions['LU']
                    tail += directions['LD']
wind hawk
#

ah ok

#

why though kek

grim lagoon
#

idk thats how i would move the tail

wind hawk
#

just compose cardinal translations lol

#

no need for the diagonal cases

placid torrent
#

If max(dy, dx) == 1 then you shouldn't move at all fyi

#

*take abs of them

wind hawk
#

oh yeah that too

grim lagoon
#

yeah

#

we talked about that function up before

#

it was @cerulean gorge 's suggestion

grim lagoon
cerulean gorge
blissful pendant
#

only x,y i think

placid torrent
#

Right I missed the need_move bit
Eniraa's suggestion is the right answer then

Composing the translations means check x movement, then check y movement, but they don't need to know about each other

grim lagoon
#

so thats why i have += directions[d] which is what the head just did above

cerulean gorge
odd star
cerulean gorge
placid torrent
grim lagoon
#

Im guessing thats some part 2 chicanery

placid torrent
#

1 goes up-left, 2 goes straight up

cerulean gorge
placid torrent
#

Oh lol

grim lagoon
#

yeah

placid torrent
#

Mb

grim lagoon
#

i already know what part 2 is at this point from watching the visualizations

grim lagoon
cerulean gorge
grim lagoon
#

I feel stupid after writing all that and it’s a function already

cerulean gorge
#

that's numpy -- will continue to do that for you for years

grim lagoon
#

and like i said, i was only using it for "easy array addition"

#

lol

cerulean gorge
#

for nim i had to define all my vector operations 😦

type vec2 = array[2, int]

using a, b: vec2

proc `+=`(a: var vec2, b) =
  a[0] += b[0]
  a[1] += b[1]

proc `-`(a, b): vec2 = [a[0] - b[0], a[1] - b[1]]

proc sign(n: int): int =
  if n > 0: 1 elif n < 0: -1 else: 0

proc sign(a): vec2 = [a[0].sign, a[1].sign]

proc chebyshev(a): int = max(a[0].abs, a[1].abs)

proc simulate_rope(nknots: int): int =
  var
    rope = collect(for _ in 0..<nknots: [0, 0])
    seen = toHashSet [[0, 0]]

  for (dir, n) in commands:
    for _ in 0..<n:
      rope[0] += dir
      for i in 0..<nknots - 1:
        let delta = rope[i] - rope[i + 1]
        if delta.chebyshev > 1: rope[i + 1] += delta.sign
      seen.incl rope[^1]

  seen.len

part 1: simulate_rope 2

lost steppe
#

you can just add random functions to existing types in nim?

cerulean gorge
#

so nim uses uniform function call syntax

lost steppe
#

right, I was about to ask

cerulean gorge
#

so the a.method(...) is the same as method(a, ...)

lost steppe
#

I know some people wanted to get into C++

#

but it never happened

cerulean gorge
#

its really convenient

#

also supports command syntax

lost steppe
#

(these some people included Bjarne Stroustrup)

cerulean gorge
#

which is the my_proc argument no parens

#

if you mark all these procs with {.inline.} pragma, probably this is just as fast as c++

lost steppe
#

I mean, this is basic enough stuff where I hope that would be the case

#

does nim use llvm or do they do something custom?

cerulean gorge
#

can compile to c

lost steppe
cerulean gorge
#

using llvm or whatever

#

or nim -> c -> llvm rather, i guess. i don't know all the logistics here

lost steppe
#

right, that

cerulean gorge
lost steppe
#

llvm is the compiler backend for clang in this case

#

or you could decide to go with gcc at that point, for fun

cerulean gorge
#

but nim can also target js

odd star
#

I search up like every single thing I want it to do

golden socket
#

but it's nice because there's a function for everything you want it to do

limber gazelle
cerulean gorge
limber gazelle
#

async centric means if i dont know async yet i shouldnt touch it?

limber gazelle
cerulean gorge
#

uhh, well there's at least one async function you have to implement, but you don't necessarily need tasks and awaits and everything

limber gazelle
#

may i know all this

#

import App,Button;Slider,Widget

#

is it all done by yourself from scratch

#

or is it something that is from tkinter/pygt etc?

cerulean gorge
#

it's all from scratch

limber gazelle
grim lagoon
#

alright so maybe this is a little cursed, but i append every tail into a list and numpy it so i can apply unique on the axis (cant use set) but it doesnt seem to give the intended result

history = []

for m in moves:
    d, dis = m.split()
    for _ in range(int(dis)):
        head = head + directions[d]
        if need_move(head, tail):
            tail += np.sign(head - tail)
            history.append(tail.copy())

np_history = np.array(history)
print(np_history)
print(np.unique(np_history, axis=0))
[[1 0]
 [2 0]
 [3 0]
 [4 1]
 [4 2]
 [4 3]
 [3 4]
 [2 4]
 [3 3]
 [4 3]
 [3 2]
 [2 2]
 [1 2]]

[[1 0]
 [1 2]
 [2 0]
 [2 2]
 [2 4]
 [3 0]
 [3 2]
 [3 3]
 [3 4]
 [4 1]
 [4 2]
 [4 3]]
odd star
#

wdym?

#

that is what you want isn't it?

#

just take the length of that and you're good

cerulean gorge
#
my_set.add(tuple(tail))
wind hawk
#

it's almost time for yet another choke by me

odd star
#

dw i'm significantly worse

#

i'll see yall in like 2 hours

wind hawk
#

ok

#

i have learned to test against the given input

#

so i don't get 5 min penalties

odd star
#

i was gonna set up my template file with pytest yesterday

#

but i wasn't bothered

analog bolt
#

for this day 9 I did test against the input but that didn't help much lol

odd star
#

so i'm still just commenting out the test file line

grim lagoon
undone apex
#

Is there anything that i may be missing out from part 1 if the example works just fine?

#

The tail moves to all good positions but for my puzzle input i cant get good answer

odd star
#

send your code?

undone apex
#

ye ok just a sec

#
    "R": [0,1],
    "L": [0,-1],
    "U": [1,0],
    "D": [-1,0]
}


# tells if tail has to go diagonal

def go_diagonal(head_pos, tail_pos):    
    if head_pos[0] != tail_pos[0] and head_pos[1] != tail_pos[1]:
        return True
    return False

with open("day 9/data.txt") as file:
    lines = file.readlines()
    head_pos = [0, 0] # x:y position of head atm
    tail_pos = [0, 0] # x:y position of tail atm
    visited = [[0, 0]]
    last_pos = head_pos[:]
    
    for line in lines:
        motion = line.strip().split(" ") # line of code giving instructions
        way = motion[0] # right, left, up, down
        length = int(motion[1]) # how long move
        for moves in range(length): # go step by step based on how big is length of move
            last_pos = head_pos[:]
            head_pos[0] += directions[way][0]
            head_pos[1] += directions[way][1]
            if abs(abs(head_pos[0]) - abs(tail_pos[0])) > 1 or abs(abs(head_pos[1]) - abs(tail_pos[1])) > 1:
                if go_diagonal(head_pos, tail_pos):
                    tail_pos = last_pos[:]
                else:
                    tail_pos[0] += directions[way][0]
                    tail_pos[1] += directions[way][1]
            
                
            if tail_pos not in visited:
                visited.append(tail_pos[:])
    print(len(visited))
odd star
#

you're not keeping track of duplicates

undone apex
#
if tail_pos not in visited:
                visited.append(tail_pos[:])
#

this?

odd star
#

oh wait

#

I guess that does work

undone apex
#

i was debugging this on example from site

#

and tail seemed to move to all correct positions

odd star
#

I think your code might error if the head is on (0,0), tail on (0,-1) and the head moves R

#

because the tail should move R

#

but the way you have the condition set up, ||1|-|-1|| would be 0

#

you should just have abs(head pos - tail pos)

undone apex
#

what do you mean by head_pos - tail_pos

#

i see what you mean what is working bad but dont get what you meant about the condition i should have

#

oh ok i get it

#

thanks a lot 😄

pseudo remnant
#

(n/2, n/2) or (0,n)

lost steppe
#

in my setup 0, 0

pseudo remnant
#

also why did you choose 200 just a guess or

pseudo remnant
lost steppe
#

the class I have shifts the coordinates

#

so 0,0 is at the middle

pseudo remnant
#

whoa

lost steppe
#

but I could have just shifted the start position

pseudo remnant
#

could you share your code?

#

or atleast the class

lost steppe
#

it's not in python pithink

pseudo remnant
#

aww man, sed

lost steppe
#

but it's nothing fancy

pseudo remnant
#

how do you shift?

#

do you dynamically increase the size of the 2d array if it goes out

#

oh wait nno you dont increase you shift my bad

#

hmh

#

what language did you write it in?

lost steppe
#

porting it roughly tp python

class Points:
  def __init__(self, n):
    self.n = n
    self.visited = [[False]*(2*n+1) for _ in range(2*n+1)]
    self.dupes = 0
  def insert(self, x, y):
    x += self.n
    y += self.n
    if not self.visited[x][y]:
      self.dupes += 1
      self.visited[x][y] = True
lost steppe
#

in hindsight just moving the start point would have been sensible

mellow thistle
#

I sat down with a pen and paper and eventually managed to work out what was going on and get a part 1 solution, but now I'm getting brain-ache trying to work out the logic of extending it to part 2

#

def move_parser(move, head_pos, tail_pos, tail_table):
    move_table = {
        '[-1, 2]': [1, -1],
        '[0, 2]': [0, -1],
        '[1, 2]': [-1, -1],
        '[-2, 1]': [1, -1],
        '[2, 1]': [-1, -1],
        '[-2, 0]': [1, 0],
        '[2, 0]': [-1, 0],
        '[-2, -1]': [1, 1],
        '[2, -1]': [-1, 1],
        '[-1, -2]': [1, 1],
        '[0, -2]': [0, 1],
        '[1, -2]': [-1, 1]
    }
    direction, extent = move.split()[0], int(move.split()[1])
    for x in range(extent):
        if direction == 'U':
            head_pos[1] += 1
        elif direction == 'D':
            head_pos[1] -= 1
        elif direction == 'R':
            head_pos[0] += 1
        elif direction == 'L':
            head_pos[0] -= 1
        relative_tail = [tail_pos[0] - head_pos[0], tail_pos[1] - head_pos[1]]
        distance = [abs(tail_pos[0] - head_pos[0]), abs(tail_pos[1] - head_pos[1])]
        if distance[0] > 1 or distance[1] > 1:
            adj = move_table[str(relative_tail)]
            tail_pos = [tail_pos[0] + adj[0], tail_pos[1] + adj[1]]
            tail_table.append(tail_pos)
    return head_pos, tail_pos, tail_table


with open("input_9.txt", "r+")as input_file:
    head_pos = [0, 0]
    tail_pos = [0, 0]
    tail_table = [tail_pos]
    for line in input_file:
        head_pos, tail_pos, tail_table = move_parser(line, head_pos, tail_pos, tail_table)
    print(len(set([str(x) for x in tail_table])))```
mellow thistle
#

aaah, I don't understand this issue at all!

#

I wrote a new function, ran it, it did nothing. I thought it might be an issue iterating over the input so I changed how that works. Now both parts work...but the first part gives the wrong answer

#

any help would be much appreciated!

grim lagoon
#

For part 2 can we just make a bunch of nodes and just have each node check the position of the one in front of it like we did for head tail and just update the history based on the last tail? I dont see how the logic would change

mellow thistle
#

Well, that's what I've done essentially

#

but something is screwy

grim lagoon
#
import numpy as np

def need_move(h, t):
    #chebyshev distance
    return np.linalg.norm(h - t, ord=np.inf) > 1

def part_two():
    with open ("day9/moves.txt", "r") as f:
        moves = f.read().split('\n')

    directions = {
        'U': np.array([0,1]),
        'D': np.array([0,-1]),
        'R': np.array([1,0]),
        'L': np.array([-1,0])
        }

    knots = [[0,0] for _ in range(10)]
    rope = np.array(knots)
    history = set()

    for m in moves:
        d, dis = m.split()
        for _ in range(int(dis)):
            rope[0] += directions[d]
            for knot in range (len(rope)-1):
                if need_move(rope[knot], rope[knot+1]):
                    rope[knot+1] += np.sign(rope[knot] - rope[knot+1])
                    history.add(tuple(rope[-1]))

    print(len(history))
part_two()
#

lets go

#

got it

#

just had to add like one line from my part 1

mellow thistle
#

This is too clever for me

#

Still can't work out what the hell is up with mine

undone apex
vivid vergeBOT
#

Hey @undone apex!

You either uploaded a .txt file or entered a message that was too long. Please use our paste bin instead.

mellow thistle
#

I've sorted out the input thing but the function isn't working and I can't tell why

#

Is this for part 1?

undone apex
#

part 2

mellow thistle
#

sorry can't help 😅

undone apex
#

well i start to lose faith if i can complete it tbh

#

spent way too much time on this

#

and still cant figure this out ;/

mellow thistle
#

I've got like that with previous days, so far I've done them all but it's been hairy

undone apex
#

do you have any tips that may help when it comes to just thinking about this part 2

#

maybe sth that helped you solved it

mellow thistle
#

Well, I'm stuck on part 2 too >_<

#

so...

#

no lol

undone apex
#

oh ;d

#

gl then

mellow thistle
#

what I'm trying to do is take my part 1 logic and apply it to each subsequent knot with a for loop

#

And looking at line by line outputs it seems to be working at first

#

but at some point things move too far apart and it breaks

undone apex
placid torrent
#
        if knot[0] not in visited:
            visited.append(knot[-1][:])

I don't know which way round your rope is supposed to be, and neither do these two lines of code

#

They should both be either [0] or [-1]

undone apex
#

ye i fixed it

#

i noticed it a second ago but it didnt fix much hah

#

the problem is with how all the parts are following each other

#

pasted a changed version

mellow thistle
#

Ah, so it works?

undone apex
#

nah haha

#

just pasted a version without this mistake so people dont notice it as a mistake

heavy night
#

I did part 1 quite easily but I'm struggling with part 2

undone apex
#

same

#

well maybe not quite easily but did it XD

heavy night
#

I am doing the example to get the logic correct but I'm not sure how A) My tail jumps from one place to another and B) the diagram looks nothing like the example's "solution"lol

mellow thistle
#

I just added some extra entries to my moves table and it works fine

#

for some reason in the second day the knots can move away from each other so they're seperated by a single diagonal space, like

...1
....
.2..

and in that case the knot needs to move diagonally like

...1
..2.
....

no idea why this is but when I changed my program to work like this it gave the correct answer immediately

undone apex
#

do you mind sharing your code?

#

i have it in my code

mellow thistle
#

Sure, give me a sec, I'll put it on my github

undone apex
#

i knew that this can happen but well

#

doesnt work for me

mellow thistle
#

I'm not particularly clever at maths so I just used a table to move the knots

unkempt parrot
#

.

#

@limpid wraith

limpid wraith
unkempt parrot
#

huh

#

do you mean the direction var?

#

thats just me simulating the 4 directions

limpid wraith
#

@grim lagoon abs(complex_number - complex_number)

#

it seems like euclidean

unkempt parrot
#

I'm going to sleep now

limpid wraith
unkempt parrot
#

um

#

oh yeah

#

I tototally didnt have day 8 opened

#

yes.

#

(and I did it in a really stupid way please don't point it out)

deep bison
#

I just did

if abs(x-px) > 1 or abs(y-py) > 1:
  ...
unkempt parrot
#

that works

vivid vergeBOT
#

2022/Python/day9.py line 46

if ((prev_knot.real - knot.real)**2 + (prev_knot.imag - knot.imag)**2)**0.5 <= ROOT_2:```
limpid wraith
unkempt parrot
#

yes

#

what you did is equivalent

limpid wraith
unkempt parrot
#

except you used < and I used <=

unkempt parrot
#

^

limpid wraith
unkempt parrot
deep bison
#

right i see what you mean

#

sqrts bad

unkempt parrot
#

I'll leave the left upto you

deep bison
#

just compare the squares

#

sqrt so slow to compute

unkempt parrot
#

ugh do you guys really want me to update it lol

#

done

#

now I could make my code better by using complex as key and not using phase at all, but am I going to? lemon_smug

#

god what was I thinking a few hours ago

#

im off to sleep now

pseudo remnant
#

yo

#
---      -H-
-H-  ->  ---
--T      --T
#

if this was the scenario where would you move the tail

#

directly up or diagonal

limpid wraith
pseudo remnant
#

my answer worked with example and is giving wring result for the actual input i dont even know where to start debugging

pseudo remnant
#

I DID IT I SOLVED PART 1 OF DAY 9

pseudo remnant
#

I DID PART 2 TOOO

#

🥳

signal jacinth
# pseudo remnant I DID PART 2 TOOO

how did you do part 2? i have no clue what to do with the multi-knot logic
i tried iterating through each pair of knots (H&1, 1&2, 2&3, etc.) and applying the logic from part 1, but i cant seem to get the right answer...

pseudo remnant
#
class board():
    def __init__(self, n):
        self.size = n-1
        self.grid = [[0 for i in range(n)] for i in range(n)]
        self.count = 0

    def display_board(self):
        for row in self.grid:
            for cell in row:
                if cell: print('#', end='')
                else:     print('-', end='')
            print('')


class node():
    def __init__( self, x, y ):
        self.x = x
        self.y = y


b = board(500)
rope = []
rl = 10 # length of rope
for i in range(rl):
    rope.append(node(int(b.size/2), int(b.size/2)))

direction_map = {
    'L': (-1,  0),
    'R': ( 1,  0),
    'U': ( 0, -1),
    'D': ( 0,  1),
}


def mark_map(dir, steps):
    for _ in range(steps):

        rope[0].x += direction_map[dir][0]
        rope[0].y += direction_map[dir][1]


        dist_x = rope[0].x-rope[1].x
        dist_y = rope[0].y-rope[1].y

        for i in range(1,rl):
            if not((abs(dist_x) in [0,1]) and (abs(dist_y) in [0,1])):
                # print("not connected")

                if dist_x < 0:     rope[i].x += -1
                elif dist_x > 0: rope[i].x += 1
                
                if dist_y < 0: rope[i].y += -1
                elif dist_y > 0: rope[i].y += 1

            if i != rl-1:
                dist_x = rope[i].x-rope[i+1].x
                dist_y = rope[i].y-rope[i+1].y

        
        if not b.grid[rope[rl-1].y][rope[rl-1].x]:
            b.grid[rope[rl-1].y][rope[rl-1].x] = 1
            b.count += 1


with open('input.txt') as input_:
    for i in input_:
        dir, steps = i.split()
        
        mark_map(dir, int(steps))


b.display_board()
print(b.count)
#

this is my solution

signal jacinth
#

ok thx! lemme read it rq

#

ohh hooray our solutions are going to be fundamentally different lol, i used imaginary numbers

pseudo remnant
#

i created an array of and append n number of nodes

#

the 0th node -> head
the n-1th node -> tail

signal jacinth
#

right, same here, i have a list of essentially x/y values

#

yeah yeah

pseudo remnant
#

i update where the head is supposed to be in the mark_map function

        rope[0].x += direction_map[dir][0]
        rope[0].y += direction_map[dir][1]

and then update where the rest of the nodes are supposed to be except the last one and update the dist varable

#

you know whhat ill

#

share you the code that helped me figure out my solution

#

that was wasyy too clean and readable

signal jacinth
#

and then update where the rest of the nodes are supposed to be
this is the problem, i dont understand the part 2 knot logic

signal jacinth
#

thanks!

sleek wharf
inner mauve
slim jetty
#

There is an among us