#AoC 2023 | Day 14 | Solutions & Spoilers

1250 messages · Page 2 of 2 (latest)

hazy iris
#

yeah, I'm guilty of skipping the lore a lot as well. or reusing variables from previous days. I end up with boards and mazes all over.

lament depot
#

Yep just like in math. Unit Circle, trigonometric functions... complex numbers are extremely useful

sly warren
#

How many rotations does the sample input take to repeat? 10 right?

#

Calculating 69 not 64 for the 10.. ugh

pine pollen
#

I got 7

#

are your rotations the right way around

#

it's north west south east

sly warren
#

No, but I think I know my error

#

oooh, I misinterpreted: ||it repeats on the 10th rotation, but the loop length is 7||

noble island
#

Interesting, for me it was way more

#

That means that fair benchmark comparisons will be difficult

#

For me it was like 90 or so

sly warren
#

I mean the sample data. I think

pine pollen
#

mine was 103 + 13n

noble island
#

Ah okay

#

My bad

#

I have to find a better way to hash the grid state

pine pollen
#

for me storing the entire grid in the cache was better than hashing it

#

because at the end I didn't have to recompute anything

#

tho maybe storing intermediate load values would be better

sly warren
#

Yah, and here's the moment where I realize my solution is... well... slow 😠

noble island
#

But yeah really what I mean is that I need a better representation for the board state

pine pollen
#

yeah true

#

meant that storing the entire object instead of just the hash was better

#

I struggled with picking a board state for this problem

#

sparse grid was easier to hash but 2d array would've been easier to simulate imo

#

tho I guess just converting 2d array -> tuple of tuples and then hashing would work but AyameShrug

raven heath
#

numpy has tobytes luckily

marsh axle
#

my god

#

I spent honestly an hour debugging my code

#

because I didn't read carefully and thought they meant I had to do 1000000000 rolls (so 1000000000/4 cycles)

raven heath
#

that sucks'

raven heath
#

though there's a good chance you still get the right answer

marsh axle
#

I feel like my reading ability is my own biggest hurdle

#

nope, I didn't

raven heath
#

rip

marsh axle
#

not even for the sample

#

oh well, at least my actual code was right, just had to not undercount

raven heath
#

my biggest issue this year is i keep hardcoding values that should be variables

#

there's a bug in my implementation

marsh axle
#

pretty happy with how I did the different rolls actually, I implemented the thing for P1, and then just added a function to transform the coords right before I change the grid based on the direction

#
def roll_grid(grid: &Grid, dir: Direction) {
    assert grid.width == grid.height, `Grid must be square`
    let N = grid.width

    let total = 0u64
    for let col = 0; col < N; col++ {
        let pos = 0
        for let row = 0; row < N; row++ {
            // Part 1: let p = Point(col, row)
            let p = get_point(grid, col, row, dir)
            let c = grid.at_point(p)

            match c {
                '#' => pos = row + 1
                '.' => {}
                'O' => {
                    grid.set_point(p, '.')
                    // Part 1: let new_point = Point(col, pos)
                    let new_point = get_point(grid, col, pos, dir)
                    grid.set_point(new_point, 'O')
                    pos++
                }
                else => assert false, `Unknown character {c}`
            }
        }   
    }
}
raven heath
#

i did a bunch of GRID.T, GRID[::-1] or whatever

marsh axle
#

I wanted to but then I'd have to implement actually rotating the grid and that sounded more annoying

#

this was just minimal change in my actual code

#

and threw in:

def get_point(grid: &Grid, _x: u32, _y: u32, dir: Direction): Point {
    let N = (grid.width - 1) as u64
    let x = _x as u64
    let y = _y as u64
    return match dir {
        N => Point(x, y),
        W => Point(y, N - x),
        S => Point(N - x, N - y),
        E => Point(N - y, x),
    }
}
raven heath
#

i starts to do something like

def roll(dy, dx):
  ...

but ended up just using different views

marsh axle
#

honeslty would have done the same if I had python

raven heath
#

does your language use a different keyword for constants

#

or variables and constants both use let i guess?

marsh axle
#

i don't have runtime constants

raven heath
#

ok

#

is that like an extra compiler check or something

#

to make something a constant

#

i don't anything about compilers

marsh axle
#

basically

#

just didn't do it because currently for stuff like

a.x = 5

I just handle the left and right sides completely independently, and I'd have to go around and refactor all that to now have to care about is something being assigned to or not

raven heath
#

would be fun to do this in my own language one year

#

i would like an array-based, white-space significant language

marsh axle
#

it is pretty fun actually, even though I miss being speed

#

writing speed, not execution speed

#

So far at 0.2s for all 14 days so not too bad

raven heath
#

yeah, debugging seems tough too depending on how good your error messages are

marsh axle
#

I transpile to C, so can just jump into GDB

raven heath
#

that's not too bad

marsh axle
#

I have like SDL bindings too so can do some basic graphics, I did my da 10 visualization thing in my own lang too, that was fun

pine pollen
lament depot
#

There's one consistent leaderboard getter who does aoc in their own language

#

They wrote it specifically for the kind of speed solving for aoc type problems

raven heath
#

i could see it, if you made a lang specifically for competition

marsh axle
#

yeah if I wrote an interpreted dynamic language I'd probably be just as fast as python (assuming I implemented enough utils), but mne's like low-level systems language, basically at the level of C

raven heath
#

good collections and string manipulation

lament depot
marsh axle
#

honestly I'm even just happy that I made it to <500 on leaderboards a couple times while essentially doing it in C

lament depot
raven heath
#

i feel like if i was better at nim, it could be decently competitive

#

it's sometimes less verbose than python

noble island
#

but since the board is something like 20% round sotnes

#

it's not very beneficial

pine pollen
#

yeah ignore that I was wrong

arctic bear
#
with open("input.txt") as f:
    lines = tuple(f.read().splitlines())


def rotate(lines):
    for _ in range(4):
        lines = map("".join, zip(*lines))
        lines = (
            "#".join("".join(sorted(group, reverse=True)) for group in line.split("#"))
            for line in lines
        )
        lines = tuple(line[::-1] for line in lines)

    return lines


history = [lines]

for _ in range(1_000_000_000):
    lines = rotate(lines)
    if lines in history:
        break
    history.append(lines)

offset = history.index(lines)
print(
    sum(
        row.count("O") * (len(lines) - i)
        for i, row in enumerate(
            history[(1_000_000_000 - offset) % (len(history) - offset) + offset]
        )
    )
)

``` can someone look at this and tell me how to optimize it?
#

i thought about storing history in a set cause its faster to check if its in it but i also need full history so i would need two variables

lament depot
#

Since you're using a tuple, you could use a dictionary

#

That'll give you O(1) lookup for lines in history

arctic bear
#

what would be key and value?

marsh axle
#
    let seen = Map<SV, u64>::new()
    let count = 0u64
    while count < 1000000000u64 {
        let s = grid_to_string(&grid)
        let prev = seen.get_item(s)
        if prev? {
            diff = count - prev.value
            break
        }
        seen.insert(s, count)

        cycle(&grid)
        count += 1
    }
arctic bear
#
history = {lines: 0}

for i in range(1_000_000_000):
    lines = rotate(lines)
    if lines in history:
        break
    history[lines] = i

offset = history[lines]
```?
marsh axle
#

pretty much yeah

arctic bear
#

i get keyerror tho

#
Traceback (most recent call last):
  File "/Users/down/Desktop/advent-of-code/2023/14-2.py", line 30, in <module>
    history[(1_000_000_000 - offset) % (len(history) - offset) + offset]
    ~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
KeyError: 190
arctic bear
marsh axle
#

well yeah you've changed int->lines mapping to lines->int by changing from array to map

#

so old index won't work

#

for that part

#

honestly, I just calculated the remainder and cycled it that many times lol

#

you could do something like:

    history[lines] = i
    history[i] = lines
#

so you can index the other way too

arctic bear
#

everything cool until i have to get index of lines (offset)

marsh axle
#

doesn't what I said above do the trick for that?

arctic bear
#

almost works

#

i changed it to this ```py
history = {0: lines}

for i in range(1_000_000_000):
lines = rotate(lines)
if lines in history:
break
history[lines] = i
history[i] = lines

offset = history[lines] + 1

and then `len(lines)` has to be divided by 2
#

but it gives output ||103876|| while correct one is ||103861||

marsh axle
#

Not sure what math you're doing tbh

#

I did mine differently so my brain is confused

arctic bear
#

(1_000_000_000 - offset) % (len(history) // 2 - offset) + offset this you mean?

marsh axle
#

I did something akin to:

cycle_size = i - history[lines]
remaining = (1000000000 - i) % cycle_size
history[history[lines] + remaining]
#

maybe equivalent

arctic bear
#

looks like same thing written diffriently

marsh axle
#

yeah you never use i though, but I guess that doesn't matter

arctic bear
#

isnt your i mine offset

marsh axle
#

i is the iteration in which you found the cycle

arctic bear
#

soo if i understood correctly it is mine offset

marsh axle
#

your offset is the start of the cycle, my i is the end of the cycle

arctic bear
#

ah end of it

#

well no idea why its like that

marsh axle
#

My logic was kinda, say I need to do 10 iterations, but I find iteration 5 is same as iteration 1. I stop at iteration 5 (same as you, break from loop) and compute:

cycle_size = 5 - 1 = 4
remaining_iterations = 10 - 5 = 5
# but every cycle_size iterations it's the same, so: 
remaining_iterations = (10 - 5) % 4 = 2

need to be 2 iterations from start of cycle, so can look at iter 1+2
arctic bear
#

for me ```py

loop start is

offset

loop end is

len(history) // 2 - offset

#

let me try that

marsh axle
#

why not just use your i variable from the for loop?

#

instead of len(history)//2

arctic bear
#

hmm not so clean to do so but let me try

marsh axle
#

you can just use it, it stays around outside the loop

arctic bear
#

i know it does

#

the last value of it in the loop

tired mist
#
with open("day14.txt", "r") as file:
    data = tuple(map(tuple, file.read().splitlines()))
    reverse = tuple(map(tuple, zip(*data)))
    w = {i : [e for e, y in enumerate(x) if y == "#"] for i, x in enumerate(data)}
    h = {i : [e for e, y in enumerate(x) if y == "#"] for i, x in enumerate(reverse)}
    seen, c, p1 = [], 0, 0
    get_current = lambda x: ((reverse, h), (data, w))[x % 2]
    while (nxt := tuple(map(tuple, zip(*reverse)))) not in seen:
        seen.append(nxt)
        for direction in range(4):
            current, blocks =  get_current(direction)
            new_r = []
            for e, row in enumerate(current):
                new, prev, static = [], 0, blocks[e]
                for i in static:
                    new += sorted(row[prev : i], key = lambda x: x != ["O", "."][direction in [2, 3]]) + ["#"]
                    prev = i + 1
                new_r.append(new + sorted(row[prev : len(row)], key = lambda x: x != ["O", "."][direction in [2, 3]]))
            if current == reverse:
                data = tuple(map(tuple, zip(*new_r)))
            else:
                reverse = tuple(map(tuple, zip(*new_r)))
            if not p1 and not direction:
                p1 = sum([data[u].count("O") * -u for u in range(-len(data), 0)])
        c += 1
    nxt = seen[(s := seen.index(nxt)) + (1000000000 - s) % (c - s)]
    print(p1, sum([nxt[e].count("O") * -e for e in range(-len(nxt), 0)]))```
arctic bear
#

for the default input ```py
O....#....
O.OO#....#
.....##...
OO.#O....O
.O.....O#.
O.#..O.#.#
..O..#O..O
.......O..
#....###..
#OO..#....


i get `i=9 history[lines]=2`
#

and it outputs 65 instead of 64

#

history = {0: lines, lines: 0}

for i in range(1_000_000_000):
    lines = rotate(lines)
    if lines in history:
        break
    history[lines] = i
    history[i] = lines

cycle_size = i - history[lines]
remaining = (1_000_000_000 - i) % cycle_size

print(i, history[lines])
print(
    sum(
        row.count("O") * (len(lines) - i)
        for i, row in enumerate(
            history[remaining + history[lines]]
        )
    )
)
``` with code like this
marsh axle
#

i got 3->10

#

maybe off-by-one?

arctic bear
#

hm let me try

#

works now hm

marsh axle
#

i always add initial state at 0, that's why

arctic bear
#

let me clean it up and see with actual input

#

something odd and happens i dont know why

#

basically this works py cycle_size = i + 1 - history[lines] + 1 remaining = (1_000_000_000 - i + 1) % cycle_size but this doesnt py cycle_size = i + 1 - (history[lines] + 1) remaining = (1_000_000_000 - i + 1) % cycle_size i mean this is correct in math but then i cant really calculate offset like history[lines] + 1 cause it would be as i put it in brackets

marsh axle
#

not sure what you mean?

#

I'd just do this tbh:

for i in range(1_000_000_000):
    if lines in history:
        break
    history[lines] = i
    history[i] = lines
    lines = rotate(lines) # Move to end
arctic bear
#

so it was this simple this whole time

marsh axle
#

that way you're not doing the whole off-by-one for indices-vs-counting

arctic bear
#

🎉

#

thanks

#

i just merged your code to one line of calculation

#

and is literally the same as mine but you subtract length from 1bil i did 1bil - offset and funny enough both work i dont think i wanna know why but yea

#

so
history[(1_000_000_000 - length) % (length - offset) + offset] and
history[(1_000_000_000 - offset) % (length - offset) + offset] both work

marsh axle
#

yeah that makes sense

#

(X) % Y is the same as (X + Y) % Y

arctic bear
#

oh right

shell nebula
#

because i'm gross here is p1 golf'd

sum([ c*(i+1) for i,c in enumerate((np.apply_along_axis(lambda row: tuple('#'.join([ ''.join(sorted(x, reverse=True)) for x in ''.join(row).split('#') ])), 0, np.array([ list(x) for x in data.splitlines() ])) == 'O').sum(axis=1)[::-1]) ])
gloomy lion
#

Slowly I'm catching up

#

Will be with the cool kids soon

rain lotus
#

And day 5 p2 but i dont plan on working on that pain till im done

gloomy lion
#

Day 12 is what killed my process

#

Day 5 P2 is also difficult

rain lotus
#

Thats the damaged/undamaged part right

gloomy lion
#

Yes

#

Hot springs

rain lotus
gloomy lion
#

Oh it absolutely was not easy for me, but it was just about doable

#

I think finding that one difficult is very justified

rain lotus
#

I just started day 12 and i have no clue what to do

gloomy lion
#

P1 or P2?

#

Oh right, you just started it

#

Want to chat in the Day 12 chat about it?

rain lotus
limber hamlet
#

but i decided to do current days first bc then i can follow the newest conversations lol

gloomy lion
#

Fair enough :)

#

Well now it’s the weekend so you can catch up :)

limber hamlet
gloomy lion
#

Day 12 is definitely the one to go for if you want a challenge

limber hamlet
#

day 12 is evocative of ||combinatorics and thus DP|| ... but i can't be sure of anything rn xD

#

too tired and time to wind down with movie :D

tidal cairn
#

Hi, can I ask a question?
It says "Tilt the platform so that the rounded rocks all roll north."
Does this mean I have to shift all round rocks just up until I hit a '#' ?
I know I can move in all 4 directions, but the example does not say this

tidal cairn
#

So the answer is yes for part 1.
My rolling algo is a bit stupid, but what I noticed: when I replace the string values for numbers, I can actually exploit some calculations. But I have to go still through the matrix multiple times. Hmpf
Is there any way to avoid this going through the matrix multiple times? And I have to track all rocks individually, don't I?

tidal cairn
#

Hi, anybody around to discuss part 2? I understand , I have to rotate anti-clockwise. A cycle is 4 rotations, and after each rotation I have to shift the rocks in the direction and maximum.
We start at north. Then I tilt/move, and I rotate again.
Did somebody use by any chance np.rot90 for this? I am able to get the value for the first part, but I cannot re-create the examples. I assume if there is written 1 cycle, this means the first full rotation NWSE. Right?
Why is the logic no tilt/shift, rotate, tilt/tshift etc

def four_cycles(test):
    for rotation in range(0,4):
        test=tilt(test)
        print('Result after tilt function: ')
        print(convert_num2str_matrix(test))
        #now we rotate
        print('Now we rotate ', rotation)
        test=np.rot90(test, k=1, axes=(0, 1))

    return test
#

For unknown reason to me, this does not seem to work.

real marsh
#

@tidal cairn updated my answer, hope it makes sense

tidal cairn
# real marsh the point is that whenever you tilt, the rocks fall in the direction of the tilt...

Thank you for your reply. Yes, my tilt moves them also up already. That is why I understood now, that I have to rotate with np.rot90(matrix, k=-1), i.e. clockwise. This ensures I can always apply the sample shift of the rocks, i.e. my tilt function.
My code is, however very slow, because I thought I have to save each state, overwrite it. @real marsh You describe it as rotate, move up, rotate, move up etc.
But I found in the solutions one example, where the person rotates and does only the tilt. Maybe this is a side effect of the np.rot90 function that provides always a view.
How did you solve this @real marsh , please?

real marsh
# tidal cairn Thank you for your reply. Yes, my tilt moves them also up already. That is why I...

Not sure what "slow" means to you, my code takes 38ms for part1 but 10seconds for part2.

def roll_rocks(data: np.ndarray) -> None:
    #rotate the array such that moving the rocks up will move them in the 
    # north, west, south, east direction on the original array
    N = data
    W = np.rot90(data, -1) #returns a view
    S = np.rot90(data, -2)
    E = np.rot90(data, -3)
    for rolled in (N, W, S, E):
        shift_rocks(rolled)

def shift_rocks(data: np.ndarray) -> None:
    #shifts rocks up until they hit a boundary
    coords = np.argwhere(data == 'O')
    for coord in coords:
        #remove the rock from its original position
        #if for loop breaks immediately, rock is placed in its original position
        data[*coord] = '.'
        for _ in range(coord[0]):
            coord[0] -= 1
            if data[*coord] != '.':
                #step back to a valid position
                coord[0] += 1
                break
        data[*coord] = 'O'
``` this is how I built my solution. The way I stored the map is by having a numpy array of individual strings, i.e.
```py
[['.' '.' '.' ... '.' '.' '#']
 ['O' '.' 'O' ... '.' 'O' '#']
 ['.' '#' '.' ... '.' '.' '#']
 ...
 ['.' '.' '.' ... 'O' '.' '.']
 ['O' '.' '.' ... 'O' '#' '.']
 ['.' '.' '.' ... '#' '.' '#']]

roll_rocks() gets the 4 views of the array and in each view moves the rocks up (coord[0] -= 1 since up means in the direction of decreasing row index). Instead of overwriting the view/original array after each step, I basically remove the rock from where it originally was (data[*coord] = '.'), then I scan the elements above it (for _ in range(coord[0]): coord[0] -= 1) and when I hit a rock (if data[*coord] != '.':) I overwrite the element just under it (coord[0] += 1 data[*coord] = 'O'). Also, you need to recognise that given the number of steps we need to take, eventually the board will enter a cycle. Once that happens, you can predict what happens after N cycles.

tidal cairn
#

Thank you @real marsh I have something similar. I will try to clean it later, and show you. My code for part 2 for the example has been running for 700 min, not yet finished.

rain lotus
tidal cairn
tidal cairn
#

My code looks as follows:

#res is the 2d array, 10x10 for the example
# Load the file content into a 1D NumPy array with dtype str
array_1d = np.loadtxt(fname, dtype=str, delimiter='1', comments=None, unpack=True)

# Reshape the 1D array into a 10x10 array
res = np.array([list(row) for row in array_1d])


def start2num_matrix(res):
    arr=np.copy(res)
    #print(test)

    # Replace values based on conditions
    arr[res == '.'] = '1'
    arr[res == 'O'] = '0'
    arr[res == '#'] = '2'

    # Convert the array elements to integers
    arr = test.astype(int)
    return arr

def convert_num2str_matrix(arr):
    """Converts the numerical matrix back to the string matrix
    """
    numeric_array=np.copy(arr)
    # Replace numeric values back to characters
    reversed_array = np.where(numeric_array == 2, '#', np.where(numeric_array == 1, '.', 'O'))
    return reversed_array
    

def move_north(row_idx, arr):
    """
    I use here a trick. First I replace: 
    0 <--> 0
    . <--> 1
    # <--> 2
    
    Then I calculate two adjecent lines: 
    0+0=0 <--> no
    .+.=2 <--> no
    .+0=1 <--> yes, this means it is possible to move
    #+#=4 <--> no 
    """    
    sum_neighboring_lines=arr[row_idx,:]+arr[row_idx+1,:]   
    # as outlined above, rolling occurs only where the value is 1
    pos_arg=np.argwhere(sum_neighboring_lines==1)
    #now we move the rocks (0) one line up and leave an empty place back (1)
    for idx in pos_arg:
        arr[row_idx,idx]=0
        arr[row_idx+1, idx]=1    
    return arr
      
def one_cycle(arr, offset):
    for row_idx in np.arange(arr.shape[0]-offset):
        #print('Row_idx ', row_idx)
        arr=move_north(row_idx, arr)       
    return arr

def tilt(arr):
    for offset in range(1, arr.shape[0]):
        arr=one_cycle(arr, offset)
    return arr
        
def four_cycles(arr):
    for rotation in range(0,4):
        arr=tilt(arr)
        arr=np.rot90(arr, k=-1, axes=(0, 1))
    return arr
#

Part2:

#Check if tilt function works:
arr=start2num_matrix(res)
only_north=tilt(arr)
print('Check only north tilt:')
print(convert_num2str_matrix(only_north))
print('Load on north beam: ', count_load(test))
#136 for the first example

total_num_cycles=1000000000
#total_num_cycles=3
for i in range(0, total_num_cycles):
    arr=four_cycles(arr)

#After 3 cycles I have the correct matrix as in the example
real marsh
tidal cairn
real marsh
#

when once a hash already exists in the dict, I stop

tidal cairn
#

@real marsh
Can I ask also two questions about your coord?
What does something like this do data[*coord] = '.'
I don't understand the * here. Is it some kind of unpack operator?

 for rolled in (N, W, S, E):
        shift_rocks(rolled)

You store the four views in a tuple? Correct? And on each view you apply your shift_rocks function.
Can you explain to me, because I have not understood, why we have to chain it like this: shift_rocks, rotate, shift_rocks?
I have the suspicion, my code is also slow because I do

test=tilt(test)
test=np.rot90(test, k=1, axes=(0, 1))

I overwrite my test-array all the time and keep the update version. In your code I have not seen this.

tidal cairn
#

My problem exactly is how to implement the break condition. I have to think

real marsh
# tidal cairn <@272179773340516352> Can I ask also two questions about your coord? What does ...

coord is a numpy array of (x, y), * is an unpack operator so it's basically data[x, y]
yeah the views are stored in a tuple
the reason for rotating and shifting comes from the assignment Each cycle tilts the platform four times so that the rounded rocks roll north, then west, then south, then east.
I don't overwrite the original array because I alter it by changing the view. If I move rocks up, then do np.rot90(k=-1) and move the rock up again, the original array changes such that the rocks move left. You can try it out.
This is my part2 solution
||```py
def solve2(data: list[str]) -> None:
data = parse_data(data)
hashes = {}
while True:
#use hashes as a representation of the arrays
h = sha256(data).hexdigest()
if h in hashes:
#found loop
break
hashes[h] = len(hashes)
roll_rocks(data)
start = hashes[h]
length = len(hashes) - start #length of the loop
for _ in range((10**9 - start) % length):
roll_rocks(data)
result = get_values(data)
print(sum(result))

tidal cairn
#

@real marsh Ah yes! The assignment says rotate, shift.
But how do we not know that history matters? You implemented in your roll_rocksfunction only four rotations.
When I rotate 20 times, 5x 4, why does the history of those 16 times not matter to the last 4 rotations?
I don't know how I can better describe it.
This is why, I thought it is necessary to implement something like:
arr=four_cycles(arr)
with

def four_cycles(arr):
    for rotation in range(0,4):
        arr=tilt(arr)
        arr=np.rot90(arr, k=-1, axes=(0, 1))

Here I overwrite every single arry and later have a loop around the four_cycles-function.

#

@real marsh
Can you explain to me again what you mean with " If I move rocks up, then do np.rot90(k=-1) and move the rock up again, the original array changes such that the rocks move left." please? Thankyou

#

I understood I have to do apply tilt/roll_rocks, rotate one 90 degree withnp.rot90(k=-1) and repeat this.
I try to implement this break condition

real marsh
# tidal cairn <@272179773340516352> Ah yes! The assignment says rotate, shift. But how do we n...

the history does matter, it makes up the loop which we are trying to find, but I think there is also something where the way you approach the problem in your mind makes you confused but I do not know what exactly that is.

For the rotations: I have a function that changes the x coordinate of a point(x, y) by moving it north. When I do view = np.rot90(arr, k=-1), view is a view of the original arr such that if I move my rock up, it goes left. This means I can apply shift_rocks(view), inside the function I still change the x coordinate of the point, but if I assign a value to a point(x-1, y) on the view, that gets translated into the point(x, y-1) on the original array.

tidal cairn
#

@real marsh In your code above, you write:
roll_rocks(data)
This means the function roll_rocks rotates 4 times the data , correct?
But why do you not write data=roll_rocks(data)?
I would then understand that after 4 rotations, data is updated and ready for the next cycle.

real marsh
tidal cairn
#

I still struggle with the break of the loop. Is this in the right direction please? Edit: I need to increase i.

total_num_cycles=1000000000
#total_num_cycles=1000

found=dict()
while i< total_num_cycles:
    check=hash(arr.data.tobytes())
    if check in found:
        break
    else:
        arr=four_cycles(arr)
        check=hash(arr.data.tobytes())
        found[check]=i
    i+=1
tidal cairn
tidal cairn
# real marsh one cycle is 4 rotations

Yes, exactly. But what happens to the grid/data after 4 rotations? Would this not be the starting point of my new data to which I apply 4 new rotations, please?

real marsh
tidal cairn
#

That is why I would have expected in your code at some point: data=roll_rocks(data)

real marsh
tidal cairn
real marsh
# tidal cairn That is why I would have expected in your code at some point: `data=roll_rocks(d...

consider a numpy array like arr = np.array([1,2,3]), if I do arr[1] = 50, I will get np.array([1,50,3]), which means my original array got changed but I do not need to reassign it to a new variable for that change to be reflected in arr

arr = np.array([1,2,3])
arr[1] = 50
#no arr = arr
print(arr)
>>> np.array([1,50,3])
``` the same principle applies in my code. I am not reassigning the result, I am acting directly on the underlying array
tidal cairn
#

It stopped after 19 repetitions with a load of 69, which is wrong for the example data.

tidal cairn
real marsh
#

for the test data, the loop begins after 10 cycles with a starting point on the 3rd cycle

tidal cairn
tidal cairn
real marsh
#

I am basically doing

arr = np.array([1,2,3])
arr[1] += 5
print(arr)
>>> np.array([1,7,3])
arr[1] += 5
print(arr)
>>> np.array([1,12,3])
#

changing the original array

tidal cairn
#

@real marsh :
I adapted my code with your example:

def part2(arr):
    found=dict()
    i=0
    while i< total_num_cycles:
        check=hash(arr.data.tobytes())
        if check in found:
            print(i)
            break
        else:
            arr=four_cycles(arr)
            found[check]=i
            
        i+=1
        
    start = found[check]
    length = len(found) - start #length of the loop
    for _ in range((10**9 - start) % length):
        four_cycles(arr)
    result = count_load(arr)
    print(result)

part2(arr)  

Now I get as i=10 for the break and the load 64

tidal cairn
#

Is it bad when I wrote in my code basically data=roll_rock(data)? I can see now, that it was not necessary, but I did it anyways.

#

@real marsh
Can you tell me what this does, please? I took it from your code

for _ in range((10**9 - start) % length):
        four_cycles(arr)
    result = count_load(arr)

I understood start is when it is the current key ater the break imho. length determines the length of pattern.
Does the part in the for loop calcualte for one pattern the load, so that we don't have to repeat 10**9 times?
I struggle also with the _ in the foor loop

#

@real marsh How cool. The loop broke after 131 cycles, it took my pc 2min 43s. I guess it reflects how old my pc is

real marsh
tidal cairn
rain lotus
#

@tidal cairn not sure if this helps but this is what got me the answer

tidal cairn
# rain lotus <@815357558386589800> not sure if this helps but this is what got me the answer

Thank you @rain lotus Your example helped me.

string='efgefgefgefgefgefgefg'
print(string[20]) #>>>g
print(len(string))
len_seq=3
character_to_know= 20
print(string[character_to_know % len_seq]) #>>>g

string2='abcdefgefgefgefgefgef'
print(string2[20]) #>>>f
length_of_offset=len('abcd') #>>>4
value=(character_to_know-length_of_offset) % len_seq
print(value)
print(string2[((character_to_know -length_of_offset) % len_seq)+length_of_offset]) #>>f

Here for the task one has to calculate the outcome of sequence abcd plus ef. As it is sequence, I can skip the repeating parts of efg in between. At least this is how I understood it. Would you agree?

rain lotus
#

I believe this is correct yes

#

Only thing left to change is to plug in 1 million in character_to_know and use your cycles instead of string2

#

And calculate the weight of the cycle that gets output by that

tidal cairn
# rain lotus I believe this is correct yes

Thank you. My main statement is that we have to calculate the tilts for the offset part, and then the remaining part within the series. Like this, we reduce the calculations, right?

#

"And calculate the weight of the cycle that gets output by that"
This sentence I have not understood, sorry

rain lotus
rain lotus
tidal cairn
#

With cycles weight you mean how far I need to be in that cycle, right? In your example your cycle has a length of 3. cycle[2]=g

#

Sorry, you mean what is the tilt for this position in the cycle

#

Or the tilt after those many iterations in the cycle

rain lotus
#

Sorry i thought the website said weight

#

What i wanted to say was load

#

So your final answer

tidal cairn
#

@rain lotus My fault. I think we meant the same. Yes, the development of the load on the final parts of the cycle

copper sonnet
#
hashmap_cycle_detection: 22.520291ms
floyd_cycle_detection: 41.192625ms

my disappointment is immeasurable and my day is ruined

#

I thought for sure hashing a large string would be the bottleneck but it's still the cycling