#AoC 2022 | Day 17 | Solutions & Spoilers
813 messages ยท Page 1 of 1 (latest)
hi
hi
lol
These elephants need to calm down
my part 1 works for example input but not actual input
i think i got it but i actually don't
oh
n_repeats = (1000000000000 - dropped) // d_dropped
dropped += n_repeats * d_dropped
extra_rows += n_repeats * d_max_y
Accidentally divided by d_max_y so it found way too many cycles
Also explains why it didn't work for the test until I changed extra_rows to be += instead of =
I wonder why it works better for the test than for the real input though
ยฏ_(ใ)_/ยฏ
In the part 2 example, no matter how I cut it, I'm consistently getting that every 15 rocks, the height increases by 23, so apart from an offset, shouldn't the answer be a fixed amount off of 1000000000000/15*23? I'm getting 1,533,333,333,338 when the real answer is 1,514,285,714,288... how am I off by billions and billions?
I got every 35 rocks increase by 53 for the example, which gives 1,514,285,714,263 for 1,000,000,000,000//35*53
what are yall's cycle times
i want to die
because for me
i got different
cycle ranges
*change amounts
depending on how many
rows i went down
i just noticed that every time i looped back to the start of the jets
i had the same board loadout & etc
so i just simulated 1000000000000 % rocks_per_cycle -> rocks_per_cycle being how many rocks i get through every time i loop back to 0
and then added 1000000000000 // rocks_per_cycle * height_per_cycle to the resulting height
ah p2 was fun
I started by trying to use regex on the differences in height to try to find repetition but then I just moved to using vscode's repetition highlighting
Here's my p2
https://paste.pythondiscord.com/ehusixofix
mine is 1755
I see you people doing cycle detection in code while I output into a txt file and am did ctrl f
Lol
I feel like a genius right now
2634 is my cycle length lul
jesus christ
so many duplicated code fragments
ah found a way to code it
https://paste.pythondiscord.com/hocawilequ
this one has some fun control flow
Thanks for the tip. I ended up just ditching all the changes I made to go to part 2 and started again with part 1 figuring that must have had the correct cycles for the example, and turns out that was right, so not even sure what mistake I made in that first attempt, but just threw it out.
i don't get why mine won't work
instead of doing |.####..| |....##.| |....##.| |....#..| |..#.#..| |..#.#..| |#####..| |..###..| |...#...| |..####.| +-------+ it does ```
|####...|
|....##.|
|....##.|
|....#..|
|..#.#..|
|..#.#..|
|#####..|
|..###..|
|...#...|
|..####.|
+-------+
I kinda liked these two lines of my code feature 3 enumerates:
directions = iter(itertools.cycle(enumerate(data)))
for rocknum, (rockstyle, rock) in enumerate(itertools.cycle(enumerate(rocks)), 1):```
I can't decide if that is ugly or elegant, but it worked well for me.
If it tries to drop and can't its just done and comes to a rest, it doesn't continue to get pushed left and right, those left and right pushes will be used for the next piece. Make sure your doing left/right push (only if can) followed by drop (stop if can't) then back to the left/right push.
this is confusing
The first one it falls from height 2 to 1, so that is clear. Then it sees if the left/right push works, and that is open. Finally, it tries to fall again, but this time something is blocking it, so it comes to a rest.
anyways here's the nice visualization of that part now working https://gyazo.com/44784bc26797b7a3216233636ca57ed4
not sure if my brain is broken right now, but i can't think of a way to detect a cycle
I would assume when the wind cycle repeats, but I'm not even done with part 1 so ๐คท
Finally done ๐ https://paste.pythondiscord.com/vedoxorike.py
Essentially, a cycle is going to occur when the conditions are the same as they have been in the past. Spoiler: ||you can use a combination of the current position in the cycle of jet directions, the current position in the cycle of rock shapes, and the 'shape' of the top chunk of the tower||
hmm, word, thanks, i'll try that
hi people
i
i've manually checked that my code is correct for all of the sample
but it continues to fail
does anyone want to find the very easy bug
that i probably missed due to sleep deprive
(the result is half the expected)
Hey @fading sinew!
You either uploaded a .txt file or entered a message that was too long. Please use our paste bin instead.
the paste bin says my log file is too long
Hey @pallid epoch!
You either uploaded a .txt file or entered a message that was too long. Please use our paste bin instead.
https://pastebin.com/m7WTg1Vj
Solution for 10**4299 rocks
404 not found
wtf
that's what mine looks like too...
https://paste.pythondiscord.com/
doesn't work for me
The entire page freezes when I save
idk why
i get a "something went wrong"
How big are you pasting? Is there a limit?
I just tested it ok: https://paste.pythondiscord.com/efonegiboh
i mean when i try to paste the log of all the starting block positions
its like 18 million lines or so idk
nice
nah, it's tiny
i meant my log file
340 ms for 10**4299
170 ms for the actual solution both parts
Finally done with part 1
what was the issue, out of curiosity
I was replacing existing rock with air
I forgot you could slide rocks underneath other rocks
Smh haven't played tetirs for too long
Elephant + 1000000000000 rocks = A heck of a lot of dead elephants ๐
150ms for p1
80 ms for p1 alone
This was fun. I anticipated part two, spend way too much time on part one, and then had part two almost immediately.
Question:
why is
x=None
if x is None
like a billion times faster than
why is
x=1
if x is 1
Is it that much faster?
!e
Ignoring the syntax warning:
import timeit
def check_none():
a = None
if a is None:
return 2
else:
return 3
def check_one():
a = 1
if a is 1:
return 2
else:
return 3
print(timeit.timeit(check_none))
print(timeit.timeit(check_one))
@toxic geyser :white_check_mark: Your 3.11 eval job has completed with return code 0.
001 | <string>:14: SyntaxWarning: "is" with a literal. Did you mean "=="?
002 | 0.08325721399160102
003 | 0.08740069600753486
They are the same when I test that too.
maybe some impl about doing an actual identity check on 1
so 5 hours after this puzzle released i still don't know why my code is failing
no overlappings are occuring anymore
nvm i found the problem
welp, wtf. my code just continues to run to with no end in sight with 1 but finishes in 170 ms if None
fucking weird
Sounds more like a logic error 
sounds about in the range of the p1 answers
i'm testing the test example
it's 3068
ah ok
misssing a condition check perhaps?
5 lines to be uncommented for visual debugging and 3 lines are currently uncommented to see if overlaps occured
you code like you're golfing ๐ญ
wherre are the lch & fall fncs?
and rch
def fall(m,mx,my,x,y):
if y==0:return 0
for n in range(my):
ty=n-1
for i in range(mx):
if m[n,i] and (n==0 or not m[n-1,i]) and arr[y+ty,i+x]:return 0
return 1
def lch(m,my,x,y):
if x==0:return 0
x-=1
for n in range(my):
if m[n,0]and arr[y+n,x]:return 0
return 1
def rch(m,mx,my,x,y):
if (x:=x+mx)==7:return 0
for n in range(my):
if m[n,-1]and arr[y+n,x]:return 0
return 1
Erm, mine is 10092 characters.
thanks, golfing rn lol
i gave up and wtf
3127 was the right answer to my input in part 1
the wrong answer to the example input was the right answer to my input
LMAO ๐คฃ
sucks to be you, i guess ๐ญ
1/range(min(answer_list),max(answer_list))
i wanna figure out why i had the wrong answer to the example input which was the right answer to my input
probably by chance I would imagine
it doesn't happen on the puzzle input either 
Come to golf now. Done a first level golfing
fine
tfw
better than what happened to @hot mantle ngl kek
i blame pycharm
i selected the console and then when i cmd c it highlights the code itself
for some odd reason
Sublime >>> Pycharm
atleast for smaller codes. Pycharm over sublime for projects
is cycle detection with just the top five rows fine or do i need more rows
i think i might have a slight error in my code
LMAO what on earth did you do
import antigravity
does a cycle always have to cycle back to the start here?
No
DW I made that wrong assumption too lol
hmm
Actually you could probably make it work like that
i've managed to detect a cycle
Might be shorter for the golf as well
well
if i knew it cycled at the start i could just divide stuff by two
but i know it doesn't
so there's an offset
which is super annoying
actually no i can't even do that
bruh
Completely hacked together my solution for today with cycles using just my height after each rock. Time to get back to yesterday's problem
i still don't know how my part2 solution works tbh
I thought the cycle length must just be lcm(len(movements), 5)
There's no way that should work
but no, it's weirdly different; I ended up just writing a search checking different cycle lengths. for my real input, the lcm was ~50k and the actual cycle length was ~40k
Works for both test and puzzle data
ML be like
I said 'should'
are you guys like shifting the board down or anything
Going to slightly clean up my code then I'll share it
Does your input end on a piece lock-in?
That's the only way I see that approach working
No, just skipping some drops in the middle
What do you mean by piece lock-in?
I just gave my program 5k rows to drop in lol
When moving down fails so the piece locks in/stops falling/whatever
For part 2 I switched to a defaultdict lol
Not sure what you mean, every piece locks in then?
(technically I used aoc_helper.SparseGrid but I didn't actually use any of its methods lol)
hmm
You might have a piece in the middle of a drop at the end of your input
E.g. <><>
I think you might misunderstand what I'm doing. Will share code in a moment
<>< locks in the first piece, then > moves a second piece without locking it in
some people said that their data cycled after just one cycle of the moves
so
hmm
so if i know i have a cycle
then i can use the turtle bunny algorithm thing
to find the cycle length
i think
Here's my hacked together solution. I'm hoping it'll work with any input and I wasn't just lucky with my input.
def part_two():
rock_count = 10000
cave_height = rock_count // 5 * 13
cave = np.full((cave_height, 7), 0)
height = 0
heights = []
idx = 0
for i in range(rock_count):
rock = rocks[i % 5]
y = cave_height - height - rock.shape[0] - 3
x = 2
while (
np.all(cave[y : y + rock.shape[0], x : x + rock.shape[1]] + rock <= 1)
and y < cave_height
):
match gas[idx]:
case "<":
x = max(0, x - 1)
if np.any(
cave[y : y + rock.shape[0], x : x + rock.shape[1]] + rock > 1
):
x += 1
case ">":
x = min(7 - rock.shape[1], x + 1)
if np.any(
cave[y : y + rock.shape[0], x : x + rock.shape[1]] + rock > 1
):
x -= 1
idx = (idx + 1) % len(gas)
y += 1
y -= 1
height = max(height, (cave_height - y))
heights.append(height)
cave[y : y + rock.shape[0], x : x + rock.shape[1]] += rock
n = 1
repeat = 0
repeat_value = 0
for i in range(1, 2000):
if (
heights[(n + 1) * i] - heights[(n) * i]
== heights[(n + 2) * i] - heights[(n + 1) * i]
== heights[(n + 3) * i] - heights[(n + 2) * i]
):
repeat = i
repeat_value = heights[(n + 1) * i] - heights[(n) * i]
break
offset = 1000000000000 % repeat
return 1000000000000 // repeat * repeat_value + heights[offset - 1]
It was possible for this to be wrong, if, for example, every other time or every third that combination occurred it fell differently because of the state of the board, but such a state would be hard to propagate through so many pieces .
right as i manage to figure it out for the test data
i try to use the real data
and i get 54
wtf
how does it work so nicely for the example
and then break so horribly in the actual data
hard to play tetris with these pieces
no t spins sadly
how do terminal animations work
does it like print things so fast
it seems like it's animating
you find the parts of the terminal you need to change, and just update those parts by moving the cursor to a specific location and printing the character you need
how do you move the cursor?
with ansi escapes
yep
so you can print("hello world") 10 times. and then move the cursor up 5 rows and chnage only one of those already printed hello world strings to asdfdasdfasdfa ?
i alwys thought cursor location can only be changed in the last line printed ๐
Won't work in conhost (CMD) or idle though
whats conhost
oh
Use Windows Terminal
powershell?
i dont know the difference between command prompt and terminal tbh
when i enter terminal it only shows me the cmd.exe
Windows Terminal is ANSI-compliant though
You download Terminal from the windows store, it can run any of cmd, powershell, wsl
cmd is a shell, winterm is a good terminal emulator. when you open cmd.exe, however, it defaults to conhost, a really bad emulator.
To conhost's credit it's more lightweight than Terminal
If I want to do something quick and dirty conhost is usually good enough
ye it goes "up" 4 rows
but it will erase the lines below it
well ye i thought thats the whole point if one wants to change only a thing in one of the already printed lines
need to print over the existing one i thought
Yes but it doesn't erase the other lines unless you print over those too
FYI if you want to do cursor position stuff I've always used colorama
Includes a conhost compatibility thing too
for i in range(20):
print("Hello world",i)
print("\x1b[15Aasdfasfasdfasd")
it does for me somehow
Might be your shell, try printing 10 newlines
for i in range(20):
print("Hello world",i)
print("\x1b[15Aasdfasfasdfasd")
print('\n' * 10)
If that doesn't work then I don't know
It should work
!e I'm curious, does it work in the bot?
for i in range(20):
print("Hello world",i)
print("\x1b[15Aasdfasfasdfasd")
print('\n' * 10)
@peak lance :white_check_mark: Your 3.11 eval job has completed with return code 0.
001 | Hello world 0
002 | Hello world 1
003 | Hello world 2
004 | Hello world 3
005 | Hello world 4
006 | Hello world 5
007 | Hello world 6
008 | Hello world 7
009 | Hello world 8
010 | Hello world 9
011 | Hello world 10
... (truncated - too many lines)
Full output: https://paste.pythondiscord.com/orokuvapih.txt?noredirect
Hello world 0
Hello world 1
Hello world 2
Hello world 3
Hello world 4
Hello world 5
Hello world 6
Hello world 7
Hello world 8
Hello world 9
Hello world 10
Hello world 11
Hello world 12
Hello world 13
Hello world 14
Hello world 15
Hello world 16
Hello world 17
Hello world 18
Hello world 19
[43m15Aasdfasfasdfasd
I don't know if that got corrupted or just didn't work lol
Ok no it just didn't work
Wait ANSI doesn't render properly on mobile at all lol
me last night
i was so braindead
haha
i've been going to bed super early last few days, i'm not willing to give up more sleep for aoc
I was doing that for most days and today I just fell asleep in my chair in the middle of AoC at midnight.
Probably a good thing I did though. I've been stuck in cycle detection for P2 for god knows how long now. It's right on the test input but wrong on my actual input. Only guess I have is it might have to do with overhangs. I'm looking for same piece type, same heights of each column, and same index on the jet pattern. If anyone has any ideas, I'm all ears at this point
https://paste.pythondiscord.com/cirubifume
finally found why my p1 is wrong
at some step the list just starts eating itself and i have no idea why
looks like a standard memory corruption but this shouldnโt happen in python?
like it seems like the list pointer is getting overwritten
if you want a "cheaty" solution try to store the max heights and the current piece every time your jets wrap back around to 0 for like 10 times and observe the difference in the max heights
(might just be my own input but it might work for you idk)
yeah, idk if i wanna give up my sleep anymore either
i also have a shuttle and flight to catch early tomorrow morning, so it's time to drop down the leaderboard tonight ๐
Rip
i have this function that is supposed to shift the falling rock. it works fine for left and right, but gets kinda weird when moving down :/
def shift_block(grid:list[str], dir: tuple[int,int]):
dx, dy = dir
positions = []
for i in range(len(grid)):
for j in range(len(grid[0])):
if grid[i][j] == '@':
positions.append((i, j))
step = 1 if dx < 0 else -1
for i, j in positions[::step]:
grid[i+dy][j+dx] = '@'
grid[i][j] = '.'
did you try using a debugger or anything of the sort
bro i'm so sorry
the step was to figure out in which order to remove and add the part of the blocks, but it only relevant if its an horizontal move
so in this case it's 1?
yeah
def shift_block(grid:list[str], dir: tuple[int,int]):
dx, dy = dir
positions = []
for y in range(len(grid)):
for x in range(len(grid[0])):
if grid[y][x] == '@':
positions.append((x, y))
new_positions = [(x, y+1) for x, y in positions]
for x, y in positions: # remove block
grid[y][x] = '.'
for x, y in new_positions: # build shifted block
grid[y][x] = '@'
...
shift_block(grid, (0,1))
...
this is what really blows my mind, how does this happen in one instruction grid[y][x] = '@'
it just added three @ out of nowhere
the length of the new_positions is 5, yet it places 7 @
how is that possible
def shift_block(grid: list[str], dir: tuple[int,int]):
dx, dy = dir
positions = []
for y in range(len(grid)):
for x in range(len(grid[0])):
if grid[y][x] == '@':
positions.append((x, y))
new_positions = [(x, y+1) for x, y in positions]
for x, y in positions: # remove block
grid[y][x] = '.'
for x, y in new_positions: # build shifted block
grid[y][x] = '@'
grid = [
list('...@...'),
list('..@@@..'),
list('...@...'),
list('.......'),
list('.......'),
list('.......')
]
shift_block(grid, (0, 1))
for i in grid:
print(''.join(i))
this seems to work fine for me?
what
[['.', '.', '.', '@', '.', '.', '.'], ['.', '.', '@', '@', '@', '.', '.'], ['.', '.', '.', '@', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.'], ['.', '.', '.', '.', '.', '.', '.']]
wait a sec
['.', '.', '.', '@', '.', '.', '.']
['.', '.', '@', '@', '@', '.', '.']
['.', '.', '.', '@', '.', '.', '.']
['.', '.', '.', '.', '.', '.', '.']
['.', '.', '.', '.', '.', '.', '.']
['.', '.', '.', '.', '.', '.', '.']
thing?
the grid you're testing w/
remove the shift_block
just wanna make sure it's due to the shift_block and not bc of anything else
try printing out new_positions?
try printing the grid after the loop with grid[y][x] = '.'
and after the
for x, y in new_positions: # build shifted block
grid[y][x] = '@'
```it's weird?
print out y, x in each iteration
(gonna be honest i'm just throwing darts here, i've no clue what's wrong either)
yeah ive been like that for like two hours
and print out the grid after each individual iteration
1 3
-------
.......
...@...
.......
.......
.......
.......
2 2
-------
.......
...@...
..@....
.......
.......
.......
2 3
-------
.......
...@...
..@@...
.......
.......
.......
2 4
-------
.......
...@...
..@@@..
.......
.......
.......
3 3
-------
.......
...@...
..@@@..
...@...
...@...
...@...
-------
how??
what????
surprised pikachu face:
same
ok uh
just try setting
grid[3][3] = '@'
see what that does
OH WAIT
I THINK I KNOW
the last two elements
they're linked to the index 3 row
like yknow how
x = [0, 1, 2, 3]
y = x
x[0] = 1234
print(y) # [1234, 1, 2, 3]
huh?
i think that's the bug
i didnt get it
something similar to that is happening in your grid
because y didn't copy x
it's a reference to x
so if you change x, y will change along w/ it
but where is the bug
that's the bug
each row isn't independent
you might've screwed up grid initialization
okay, ill look into it, thanks a lot ^^
so the bugs here ๐ง
def insert_rock(rock, grid, width):
for i, line in enumerate(rock):
grid.insert(i, ['.'] * 2 + list(line) + ["."] * (width - len(line) - 2))
# resize_grid(grid, rock)
rock_height = len(rock)
space_between = ['.'] * width
for _ in range(3):
grid.insert(rock_height,space_between)
return grid
maybe?
oh
i see it
space_between = ['.'] * width
for _ in range(3):
grid.insert(rock_height,space_between)
the space between is the same in the last 3 rows
np!
This is driving me round the bend
For part 2, I've got a solution that works perfectly for the test case, agrees for every rock num I could be bothered to simulate, and yet doesn't give the correct answer for the main thing
I managed to complete part 1 with straightforward method which take roughly 20 seconds to execute. I see there is a repeating pattern on rock agencement but I'm too lazy to code this out...
test case works perfectly fine up to the point that i can track, but completely off on the result
def play(inputs, count):
types = [[(0, 0), (1, 0), (2, 0), (3, 0)],
[(1, 0), (0, 1), (1, 1), (2, 1), (1, 2)],
[(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)],
[(0, 0), (0, 1), (0, 2), (0, 3)],
[(0, 0), (1, 0), (0, 1), (1, 1)]]
field = []
bottom = -1
i = 0
for r in range(count):
while len(field) < bottom+8: # field padding for next rock
field.append(['.']*7)
rock = types[r % 5]
x, y = 2, bottom+4
dropped = False
while not dropped:
m = '< >'.index(inputs[i])-1
# left/right
if all(0 <= x+a+m < 7 and field[y+b][x+a+m] == '.'
for a, b in rock):
x = x+m
i = (i+1) % len(inputs)
# down
if y > 0 and all(field[y+b-1][x+a] == '.' for a, b in rock):
y -= 1
else: # block done
for a, b in rock:
field[y+b][x+a] = '#'
dropped = True
bottom = max(y + b for _, b in rock)
while field[-1] == ['.']*7: # cut off empty rows
field.pop()
return field
print(len(play('>>><<><>><<<>><>>><<<>>><<<><<<>><>><<>>', 2022)))``` any help appreciated, like I said, seems to emulate the test case perfectly fine but the endresult is off by a massive 500
1'000'000'000'000
that's a lot of fckin rocks
it takes me a bit less than a second to process 100'000 rocks
looks like I have the wrong approach here
possibly pattern repetition?
maybe
probably after len(input) * 5
there's a few leads to improve perf further but the order of magnitude isn't good enough
which should only be 50k
you've done part 2 already ?
no i'm still failing the test case lmao
but after len(input)*5 you should start a new cycle since that's where input index 0 and rock index 0 will overlap again
just have to be careful about how the cycles connect
well that's my guess anyways
you're right for the example
welp I won't finish yesterday's puzzle tonight after all ๐ข
yea i'm also 2 behind now since i'll have to go to bed
rip
oh
but
you actually don't have to handle the cycle connection do you
you can do 2 full circles and match the second with all of the other
yea that works as well
@hot mantle what was your off by 500 error, I might be stuck at the same problem
wdym?
โซthe final revision of the code i wrote was off by 59
here you said 499
oh that
that had overlaps
so it went down where it shouldn't?
it went left or right where it shouldn't
@lucid gulch this?
ahhh so it doesn't eat inputs if it won't move
ok thank you! that will hopefully be it
maybe not hmm
well if anyone sees a problem on first glance lmk please
congratz T.T
oh i got pretty close
14 off
68 off on actual input
anyways here's a visualization
i don't get the issue with my solution
welp
might be screwed today
i changed like nothing and now it works
that's pretty cool
wait what. today was pretty simple. if i had started earlier i would have earned points. ๐ณ
๐ณ
place 100 for part 2 is 40:48
that's lots of time.
not exactly sure when i started. 
is todays second part another one of those
where a bad implementation iin part 1
leads to absurd amount of time necessary for computation?
i don't think so.
i think part 2 should be easy independent of the specific part 1 implementation. 
okay, i just checked. apparently i spent about 45 minutes on part 1. ๐
lots of small errors here and there.
my part 2 was part 1 but extended
with cycle detection
but than that the code was basically the same
I rewrote my entire code for part 2
oh really? mine was adding a few variables and if statements here and there and it worked out
also i did some funny cursed shit with my Rock class
class Rock(namedtuple("Rock", "shape left_x bottom_y")):
def __lshift__(self, value):
return Rock(self.shape, self.left_x - value, self.bottom_y)
def __rshift__(self, value):
return Rock(self.shape, self.left_x + value, self.bottom_y)
def __sub__(self, value):
return Rock(self.shape, self.left_x, self.bottom_y - value)
def __add__(self, value):
return Rock(self.shape, self.left_x, self.bottom_y + value)
@property
def positions(self):
return [
(self.left_x + dx, self.bottom_y + dy)
for dy, row in enumerate(self.shape)
for dx, filled in enumerate(row)
if filled
]
def validate_move(self, grid):
return all(0 <= pos[0] < 7 and pos[1] >= 0 and pos not in grid for pos in self.positions)
so then i could do
match jet:
case ">": rock >>= 1
case "<": rock <<= 1
rock -= 1
I had a really bad hashing method for the board state
so I just changed my board to a 200k tall numpy array
huh really? interesting
an I piece slid down like 40 rows in my input
oh i saved state for the entire grid in a set
that contained positions that were filled
and then hashed the last 10 rows
if i'm understanding what you're saying
yeah
I had to hash the last 50 rows
last 25 rows wasn't enough
because the I piece would fall down 40 rows and I'd get a cycle of length 1
rock += ord(jet) - 61
trust
rock+=1|-(jet<'>')
trust
okay this is pretty smart
and i would say this is smart but why
wtf does |- do
honestly today was kinda sus
because it relied on an i piece
not falling all the way to the bottom
unless someone can prove
that there's an upper bound on the maximum number of spots
a piece can fall down
I think someone on reddit said something about keeping track of the maximum number of drops of a piece
also all my days are kinda sus
well not all
a lot of my days
honestly i'm still not really sure how to not "cheese" today's solution
not sure what to actually detect cycles in
i should think before posting, i forgot the thing was 7 wide
I mean you thought for quite a while before that already
i am bad at aoc, unfortunately
so what
3 digit ranked wym
pretty much everyone here is 2 or 3 digit
leaderboard
pydis lb or aoc lb
actual
i can semi-consistently hit 3 digits for problems
sub 1k generally isn't hard
but that doesn't mean much
sub 100 though ๐
it's not hard to get sub 1k if you start when it releases
^
from my perspective you guys are like really good at aoc
this is my first year, i'm pretty inexperienced
I've been taking like 6 hours to solve these last few days
my second year after not doing it in 2021 ๐คทโโ๏ธ
just remember this isn't like usaco
ye lol
and the solution is what counts - not how you did it
sometimes i just waste 10 mins trying to optimize
only to realize that i can't
and it really was braindead simulation
i.e. my today's solution looked like this
did you not need to simulate after all the cycles?
wym
like
C=D=E=a=0;Z=x=1j
K=input()
F,G={0},{}
L=lambda x:all([(p:=B+x+A).imag>0<=p.real<7,0][p in F]for A in I)
while 1:
A=max(A.imag for A in F);Z=B=2+4j+A*x;I=((0,1,x,1+x),range(4),(1,x,2+x,1+2j),(0,1,2,2+x,2+2j),(0,x,2j,3j))[C:=-~C%5];E==2022>(a:=A)
if(C,D)in G:N,O=G[C,D];P,Q=divmod(1e12-E,N-E);Q>=0<a>print(a,A+(O-A)*P)
while Z:G[C,D]=E,A;B+=L(M:=ord(K[D])-61)*M;Z=L(-x);B-=x*Z;D=-~D%10091
F|={B+A for A in I};E+=1```
๐
I had
(height before first cycle) + (cycle length)*(number of cycles) + (height of remaining partial cycle)
do you not need the remaining partial cycle?
i basically noticed that every time jet loops around, a constant height is added, ergo that previous cycles don't matter
so i just simulated how much i needed to simulate (like 1000000000000 % rocks_per_cycle idk) how many rocks
and just added the height i get per cycle
very fun
hacky solution
i did every 5 times the jet looped
wym
it isn't-i made sure to check
lmao
wym
if you wanted code that works for any input you probably should do that
๐คทโโ๏ธ
who the heck pinged
so after sleeping on it and watching the "game" run for a while i figured out my problem python bottom = max(y + b for _, b in rock)after setting the stone i used this to change the spawn point but didn't account for the fact that pieces could slot in below the current max
am I right in the assumption that patterns only happen on a cycle basis i.e. len(input)*len(rock_shapes)?
my solution for p1 takes a couple minutes, pretty sure it would be an eternity to use it for part 2...
i have no idea how to optimize it
why does it take a couple minutes for your p1?
cause its not a very efficient solution, but its what i could get
Hey @plain gyro!
You either uploaded a .txt file or entered a message that was too long. Please use our paste bin instead.
now it takes about 18 seconds
because i was using deepcopy and it is very slow
but still, its not good enough
try profiling to find the bad bits
you aren't meant to brute force 3 trillion rocks
so i should approach the problem in a smarter way?
and not place and move every rock?
no
find a way to reduce the number of rocks you need to place by taking advantage of repeating stuff
I'm loosing my mind over part 2
Part 2 was weird for me in that I found it conceptually easy but really irritating to code
mathematical as in trying to find the height per repeating group of rocks?
here's where I'm at
heigh grow as a repetitive sequence
for the example, the sequences are 7 cycles long, the first 2 are useless, and then it repeats every 35 cycles, which is 5 sequeences of 7 cycles
one cycle being one rock
and rn I'm trying to figure out where those magic numbers come from
my pattern recognition works for the test case but doesn't produce anything on input
sad stuff
how did you figure out the magic number in the first place?
right
so what you need to do is programatically derive the 35 cycle number
and I agree, it's much more annoying to do programmatically than just looking at the output
but it's not always 35 isn't it ?
what I'm thinking of
it looks like the number of flow mov / the numbers of shapes
that's what I'm working out rn
@past karma is it related to len(input)*5 ?
thought that should obviously be it but so far only produced results for the test for me
not sure I'm following what you mean there
well my thought was that obviously things should repeat an some multiple of len(input)*5 because thats where both stones and the input will repeat, starting a new cycle
which works for the test case
but i've realized the problem with that logic, still thought it might be related nonetheless
oh right. yeah that was my initial thought as well
seems not to be the case
but there is a repeating group
so just brute force pattern recognition?
there's repeating groups before nb_flow * nb_shapes
i essentially did this
alright, i'm gonna do it as well then
if you do it as dumbly as I did then it takes 2 mins to run
i kinda don't wanna mess with my play algorithm but doing it afterwards requires knowledge of how long to run
@past karma so people have been doing pattern recognition ?
"pattern recognition" seems rather fancy for what I did but I guess so
I was ooking for a pure mathematical solution this whole time
if find_pattern:
if pattern is None:
for k in range(highest-49):
if grid[highest-49:highest+1] == grid[k:k+50]:
pattern = grid[k:k+50]
print(f'repeat at r: {r} height: {highest} against {k+50}')
else:
if grid[k:k+50] == pattern:
print(f'repeat at r: {r} height: {highest} against {k+50}')```
i get one immediate result but can't find anthing after that
not sure I follow what you did there, but I did a very dumb way of doing it
i'm checking if the highest 50 lines can be found somewhere else in the grid, if so try to find it again after doing some more cycles so i know the pattern length
but trying to find it again doesn't produce anything
all I did was, after each rock fall, try and see if the grid could be split in 2 and have two halves exactly the same - but do that a number of times whilst progressively removing the bottom rows from the grid
you're a genius
yeah well my genius solution takes two minutes to run lol
def check_for_repeating_groups(self, group_no):
for start, _ in enumerate(self._rocks_positional):
sublist = self._rocks_positional[start:]
if len(sublist) < 10:
# we are looking for large repeating groups
# use as quick and dirty way of ditching tiny repeating groups
return False, None, None
if len(sublist) % group_no != 0:
# looking for exact repeating groups so need a sublist divisible into equal parts
continue
groups = more_itertools.divide(group_no, sublist)
groups = iter(groups)
first = list(next(groups))
if all(first == list(x) for x in groups):
# found
group_len = len(first)
offset_len = len(self._rocks_positional) - group_len * group_no
return True, group_len, offset_len
return False, None, None
"rocks_positional" is a list[set[int]] where each set represents a row in the chamber, and each int in a set is the horizontal position of a rock in that row
I actually check to see if the grid can be split in 2, then again in 3 to work out the number of rocks for a repeating group
no problem! what was the solution runtime?
yh
I figured something : if you take the diff (between current and last elevation), the current flow, and the current shape
you could use that as an id to match when the first repeating line was happening
sounds like a good optimisation :)
just took me 2 days lol
and there's still a magic number : at one place I had to add a "-1)
but it's working I'll never modify it again
long long part2(std::vector<Shape> const& shapes, std::string const& gas_flow)
{
Tetris tetris(shapes, gas_flow);
std::vector<long long> cumulated;
std::unordered_map<RockInfos, long long, DfHash> rock_last_index;
int time_to_stabilize = 14;
int repeat_pattern_size = 35;
int i = 0;
while (true)
{
tetris.tick();
cumulated.push_back(tetris.max_y);
RockInfos rock{
tetris.max_y - cumulated.back(),
std::distance(tetris.gas_flow.begin(), tetris.current_flow_it),
std::distance(tetris.shapes.begin(), tetris.current_shape_it)
};
auto [l_it, l_inserted] = rock_last_index.insert({ rock, i});
if (!l_inserted)
{
repeat_pattern_size = i - l_it->second;
time_to_stabilize = l_it->second - 1;
break;
}
i++;
}
long long target = 1'000'000'000'000;
long long repeat = target / repeat_pattern_size;
long long remaining = target % repeat_pattern_size - time_to_stabilize;
long long result = cumulated[time_to_stabilize]
+ (cumulated[repeat_pattern_size + time_to_stabilize] - cumulated[time_to_stabilize]) * repeat
+ cumulated[time_to_stabilize + remaining - 1] - cumulated[time_to_stabilize];
return result;
}
for the curious
sorry it's in cpp
I'm pulling my hair out here. Part one works for the example, but it says the number is too low when using the puzzle input. I've gone over my code a bunch of times and even tried printing out the board after each step, but I can't find anything wrong.
want to post your code and see if we can figure out any issues?
^
Hey @silver light!
It looks like you tried to attach a Python file - please use a code-pasting service such as https://paste.pythondiscord.com
Sorry for the delay - I had to help my kid fill out an application for school.
ah no worries :)
Just a hint please if you have one.
your code is quite far off. For my main output, it's off by a factor of 50% larger that it should be
Yeah, but it gets the example right.
yes. same here. Very puzzling.
I thought the logic was good...
still looking into it
Thank you very much.
this is awkward lol - I ran the code on a different input which is why it crapped out
like, a different day's input
and running it on my actual day 17 input, it does give the correct answer
maybe redownload it
Just did that.
Trying now.
It's still giving me 3202, which is too low.
And never mind... It accepted the answer. I must have fat-fingered the entry...
I'm very sorry to have waited your time.
nah don't worry about it :)
Thank you very much!
Holy Carp! 1000000000000 rocks? I'm going to need some optimizations...
I hope they don't forget what I'm doing for them here.
So I'm not seeing any loops in my input, so should I look for relationships at step intervals of the length of the input?
I've looked the bottom 10m records for any match to the first 10 and I haven't found a single match yet... does that sound right?
now i'm thinking that I can't use the bottom few rows.
yea i think if you're gonna get a match it's gonna be xyzabcabcabc
i think i'm on to something finally with my algorithm
alright, thank you @past karma for the pointers you gave, i basically did what you said after converting my game to be set based instead of grid based python def find_pattern(grid, pattern=None): if pattern is None: for k in range(20, len(grid)//2): if grid[-k:] == grid[-k*2:-k]: pattern = grid[-k:] return pattern else: if grid[-len(pattern):] == pattern: return pattern and
if count > 1000000:
found = find_pattern(grid, repetition['pattern'])
if found:
if repetition['pattern'] is None:
repetition = {'pattern': found, 'cycle': r, 'height': len(grid)}
else:
cycles_rem, remainder = divmod(count - repetition['cycle'], r-repetition['cycle'])
count = r + remainder
magic = (cycles_rem-1)*(len(grid)-repetition['height'])``` magic gets initialized to 0 and will always be added to the final return, count is the number of cycles to be run
ah and you can speed up your solution to basically instant by setting the min length of blocks to 2000+ since that seems to be in the ballpark for input solutions
Good to know!
Finally got it. I feel kind of bad tho. I searched for the repeated blocks manually so it only works with my puzzle input.
Not sure if I'm being incredibly dumb, but I cannot find the cycle in the test input. I've completed Part 1 (which fully simulates tetris the rocks falling), so I assume my code is doing the right thing. But even visually inspecting the board, I can't find a pattern.
Am I missing something obvious or have I somehow cheesed Part 1 :/
there is a pattern, but it doesn't start right at the bottom
there is an "offset", if you like, before the pattern starts
So, for the test input, the cycle only begins after you get to 25 height. And then the cycle is a repeating group of 53 height.
that corresponds to 15 rocks for the offset, and 35 rocks per repeating group.
hopefully that helps :)
try to see if you can do a general solution :) IMO for me the minimum bar is a solution that can work for both test and real input.
I will eventually do it, but I'm a little burnt out on that one. It took me hours to figure it out.
Thanks for the tip. Stupidly I had changed how many rocks the simulation would run and didn't notice. So I never had quite enough of the chamber to find the pattern.
Annoyingly the maths I used to get the height for part 1 test using the repeating cycle doesn't work for part 2 test or actual input. As above, I've been at this on and off for days now. Gonna park it as I'm getting sick of rocks ๐
A trick I used was not to store states at all, I just stored the heights so I didn't have to change my part1 solution much. Using the heights I calculated the deltas each time, and used a sliding window. So I iterated thru the windows (of arbitrary size like 150) until I found one that occurred later on. If a series of 150 deltas gets repeated you can be pretty sure it's a cycle
Idk if this helps 
oooooh, rocks cycle 5 and the jets cycle N, that would be slick
10,091 :/ hrmmm
<3% done on brute. force
so it took ~10 minutes to do ~3% by brute force, and an hour to program something to skip to the end, if my computer didn't hang on the brute force one it would have taken ~5 hours
time saved!
Does anyone have the final state of the test input for part 1? I'm getting the wrong answer, but I can't find a flaw in my simulation logic either :/
how exactly do you want the final state? I can provide an ascii printout if you desire?
Yeah, like what's in the problem but for the whole stack. I want to try to find where it's going wrong
ok. I'll try a printout for you
perfect, thanks!
!paste
Pasting large amounts of code
If your code is too long to fit in a codeblock in Discord, you can paste your code here:
https://paste.pythondiscord.com/
After pasting your code, save it by clicking the floppy disk icon in the top right, or by typing ctrl + S. After doing that, the URL should change. Copy the URL and post it here so others can see it.
Perfect! thank you!
And that's what I needed to find the bug! thanks, onto part 2 ๐
what was the bug btw?
The bit of code i has that checks for collisions between rocks was only looking at the bottom line of the falling rock for collisions, but for all lines of the rock. So the + ones would kind of get stuck on their corner. so instead of getting:
| + |
| +++|
| ####+ |
i'd get
| + |
| +++|
| + |
| #### |
does that make sense?
ah interesting
classic 'forgot to increase an iterator' bug :p But it didn't actually crop up until the 22nd rock, so it still seemed to be working for the test input and the simulation shown in the problem
yeah I just looked at the description example now - it's in none of the simulations
Hmm so now I'm thinking about part 2, seeing all the talk here about finding a repeating cycle. That seems pretty tough, but I have an idea to make it easier: instead of saving the lines as strings with one character per slot, i'll save them as integers, encoded as a bit mask (so the first line of the example would be 0011110. Then I could generate integer representations of a few hundred lines, and then just find the repeating cycle in that sequence of integers.
FYI I'm not saving the lines as string - internally they are saved in a structure similar to that (I use idx values from 0 - 6 but the concept is the same). I just print out when I need to (e.g. for easy unit testing)
Oh yeah I was only saving them as a string for easy printing out. But bit masks were the key for my day 16 solution so im looking for more ways to use that :p
Hmm i'm realizing that really only helps me to find the cycle in the output, but that's not enough to actually answer. I need to find the cycles in where the rocks end up
not sure I follow the difference between the two there
well if I only look at the individual lines of the output, I can't easily distinguish between the individual rocks.
I also am assuming that the final cycle will be truncated, and I'll have to reconstruct that one to get the true final height
My final cycle was truncated yes. Of course the example input was not though ๐
naturally
Ah yes I gotcha. This is reliving PTSD for me ๐
Well, thank you for sharing your experience! saving me some stress for sure ๐
@past karma I finally got around to implementing a solution based on the cycles stuff, and it worked on the first try ๐
Ah awesome! Congrats :)
