#AoC 2023 | Day 14 | Solutions & Spoilers
1250 messages · Page 2 of 2 (latest)
Yep just like in math. Unit Circle, trigonometric functions... complex numbers are extremely useful
How many rotations does the sample input take to repeat? 10 right?
Calculating 69 not 64 for the 10.. ugh
No, but I think I know my error
oooh, I misinterpreted: ||it repeats on the 10th rotation, but the loop length is 7||
Interesting, for me it was way more
That means that fair benchmark comparisons will be difficult
For me it was like 90 or so
I mean the sample data. I think
mine was 103 + 13n
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
Yah, and here's the moment where I realize my solution is... well... slow 😠
I mean you still hashed it
But yeah really what I mean is that I need a better representation for the board state
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 
numpy has tobytes luckily
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)
that sucks'
RIP
though there's a good chance you still get the right answer
rip
not even for the sample
oh well, at least my actual code was right, just had to not undercount
my biggest issue this year is i keep hardcoding values that should be variables
there's a bug in my implementation
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}`
}
}
}
}
i did a bunch of GRID.T, GRID[::-1] or whatever
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),
}
}
i starts to do something like
def roll(dy, dx):
...
but ended up just using different views
honeslty would have done the same if I had python
does your language use a different keyword for constants
or variables and constants both use let i guess?
i don't have runtime constants
ok
is that like an extra compiler check or something
to make something a constant
i don't anything about compilers
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
would be fun to do this in my own language one year
i would like an array-based, white-space significant language
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
yeah, debugging seems tough too depending on how good your error messages are
I transpile to C, so can just jump into GDB
that's not too bad
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
oh neat
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
i could see it, if you made a lang specifically for competition
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
good collections and string manipulation
honestly I'm even just happy that I made it to <500 on leaderboards a couple times while essentially doing it in C
This was their 5/5 solution from last year: https://github.com/betaveros/advent-of-code-2022/blob/main/p5.noul
i feel like if i was better at nim, it could be decently competitive
it's sometimes less verbose than python
idk what you mean by easier; I did consider hashing by list of coordinates (which I guess is close to what you mean by sparse grid)
but since the board is something like 20% round sotnes
it's not very beneficial
yeah ignore that I was wrong
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
Since you're using a tuple, you could use a dictionary
That'll give you O(1) lookup for lines in history
what would be key and value?
key would be lines, value would be the index
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
}
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]
```?
pretty much yeah
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
also isnt offset 0 always?
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
everything cool until i have to get index of lines (offset)
doesn't what I said above do the trick for that?
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||
(1_000_000_000 - offset) % (len(history) // 2 - offset) + offset this you mean?
I did something akin to:
cycle_size = i - history[lines]
remaining = (1000000000 - i) % cycle_size
history[history[lines] + remaining]
maybe equivalent
looks like same thing written diffriently
yeah you never use i though, but I guess that doesn't matter
isnt your i mine offset
i is the iteration in which you found the cycle
soo if i understood correctly it is mine offset
your offset is the start of the cycle, my i is the end of the cycle
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
for me ```py
loop start is
offset
loop end is
len(history) // 2 - offset
let me try that
hmm not so clean to do so but let me try
you can just use it, it stays around outside the loop
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)]))```
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
i always add initial state at 0, that's why
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
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
so it was this simple this whole time
that way you're not doing the whole off-by-one for indices-vs-counting
history = {}
for i in range(1_000_000_000):
if lines in history:
break
history[lines] = i
history[i] = lines
lines = rotate(lines)
offset = history[lines]
length = len(history) / 2
print(
sum(
row.count("O") * (len(lines) - i)
for i, row in enumerate(
history[(1_000_000_000 - length) % (length - offset) + offset]
)
)
)
final result
🎉
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
oh right
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]) ])
#1180008645384216606 message (both parts)
Same here im only like 3 days behind now🤣
And day 5 p2 but i dont plan on working on that pain till im done
Thats the damaged/undamaged part right
It might be easy for some but i dont know crap about range intersection
Oh it absolutely was not easy for me, but it was just about doable
I think finding that one difficult is very justified
I just started day 12 and i have no clue what to do
Sorry i had to do something rq but ye sure
i missed 11, 12, and 13 from school 😔
but i decided to do current days first bc then i can follow the newest conversations lol
yeppp!! i haven't seen day 13 yet, but i've read 11 and 12 and the latter looks super intriguing (and defo challenging lol)
Day 12 is definitely the one to go for if you want a challenge
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
||yep||
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
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?
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.
the point is that whenever you tilt, the rocks fall in the direction of the tilt. This means you need to be shifting the rocks up, left, down, right as far as they go, i.e. tilt up, tilt left.... but the details of how you move the rocks is up to you. I used np.rot90() and I would always move my rocks up, so I would rotate, move up, rotate, move up...
@tidal cairn updated my answer, hope it makes sense
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?
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.
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.
Are you brutefocing every cycle?
I calculate every cycle, yes.
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
oh you cannot just simulate the billion cycles, that would take too long. Try to save the configurations you get after each cycle and stop once you find two that look exactly the same as you have just entered a loop. Find the length of the loop and then figure out where in the loop you end up after 1B cycles
Thank you. Not sure how I can do it.
I could check= hash(arr.data.tobytes()), save it in dict
found[check]
But I have not managed to compare tow entries in the keys.
I did found[check] = num_of_steps
when once a hash already exists in the dict, I stop
@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.
Okay, I try to realise this. I need a moment, how to do this
My problem exactly is how to implement the break condition. I have to think
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))
@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
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.
@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.
I didn't write data=roll_rocks(data) because I'm working on the view of the original array. I don't copy the array, but work on the underlying data. This might be a good read to understand what's going on https://numpy.org/doc/stable/user/basics.copies.html
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
Thank you. Yes, that might be one of my problems. But how does your code reflect for example 5x4 = 5 cycles?
The roll_rock function has only 4 rotations, but what happens afterwards, when a new cycle starts?
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?
you are calculating the hash twice for no reason
That is why I would have expected in your code at some point: data=roll_rocks(data)
yes, that's exactly right, that's what I'm doing in my code.
I remove the lower one! Thank you
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
It stopped after 19 repetitions with a load of 69, which is wrong for the example data.
Does it mean when you write roll_rocks(data) , you operate on the data array and this modification of data serves as starting point for the next function call of rolls_rocks, please?
for the test data, the loop begins after 10 cycles with a starting point on the 3rd cycle
yup
Okay, I think I get this part at least.
Okay, I need to implement your second part after the while loop
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
@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
I think I understand this. You operate on the array all the time and it gets updated, every time
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
_ just means we don't care about the number. the stuff in the range tells you the point in the loop where we need to stop (e.g. if the loop is 100 cycles long and they ask us for 1001 cycles in total, once we arrive at the start of the loop (this is where we are since we broke the loop once we found a loop) we just need to do the cycle once to get to the board state at 1001 cycles
I got:
Start 109
Length of found 131
Length 22
Does this mean from 109th cycle onwards the loop with a length of 22 is repeated?
And the for loop calculates the remaining cycles so we did in the end 0..109 cycles, 22 cycles, and the remaining cycles ?
@tidal cairn 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?
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
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
As soon as you find your sequence you can stop calculating yes
So after you did the calculation above you should get a number, then what you want to do is get the cycle with that index. Then calculate that cycle's weight just like in part 1
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
Sorry i thought the website said weight
What i wanted to say was load
So your final answer
@rain lotus My fault. I think we meant the same. Yes, the development of the load on the final parts of the cycle