#AoC 2022 | Day 8 | Solutions & Spoilers
1888 messages ยท Page 2 of 2 (latest)
cubic cost ๐
๐ฅน
from PIL import Image
import numpy as np
def count_and_spin(view_list, tree_matrix, tree_counter):
for y, row in enumerate(tree_matrix):
height = -1
for x, tree in enumerate(row):
if tree[1]:
if tree[0] > height:
height = tree[0]
elif not tree[1] and tree[0] > height:
tree_counter += 1
height = tree[0]
tree_matrix[y][x][1] = True
for y2, row2 in enumerate(tree_matrix):
for x2, tree2 in enumerate(row2):
view_count = 1
height2 = tree2[0]
view_line = (j for i, j in enumerate(row2) if i > x2)
for t in view_line:
if t[0] < height2:
view_count += 1
else:
break
view_list.append(view_count)
tree_matrix[y2][x2][2] *= view_count
tree_matrix = [list(r) for r in zip(*tree_matrix[::-1])]
return view_list, tree_matrix, tree_counter
def visualiser(tree_matrix, height_matrix):
new_matrix = [[[0, 255, 0] if y[1] else [255, 0, 0] for y in row] for row in tree_matrix]
image_arr = np.array(new_matrix)
result = Image.fromarray(image_arr.astype(np.uint8))
result.save("visual.png", "png")
with open("input_8.txt", "r+")as input_file:
tree_counter = 0
tree_matrix = [[[int(y), False, 1] for y in line.strip('\n')] for line in input_file]
view_list = []
for i in range(4):
view_list, tree_matrix, tree_counter = count_and_spin(view_list, tree_matrix, tree_counter)
print(tree_counter)
height_matrix = [[x[2] for x in row] for row in tree_matrix]
visualiser(tree_matrix, height_matrix)
print(max([height for row in height_matrix for height in row]))```
It's somethin very inconsequential and silly. I've added that view_list to find what the maximum view along any axis is and it's only 61, and yet the final number is absolutely insane. It shouldn't be larger than eight digits at the absolute outside
you are updating something with [y2][x2] in the y loop (no dependence on y)
which is suspicious
seems you multiply the same thing a bunch of times
it runs 100x faster than the golf we created today so i don't feel so bad lmao
what's your runtime?
day 8 gave me the opportunity to use numpy
hmm
i refused
maybe because i am not versatile with numpy
i would have to sit one day and experiment with every thing it has
like, working through what's happening logically it seems to be right. It sets the view_count to 1 (the minimum), gets the height of the tree that it's looking at, then constructs a generator that contains the sight_line in one direction. Then it iterates through that generator and adds 1 to the view_count for every tree shorter than the tree, breaking when it reaches an equal or taller tree. then it takes that counter and stores the product of it and the current score in the variable each tree has which tracks its scenic score. the maximum value of the counter on any loop is 61.
what should happen is that each tree should come out with a number somewhere between 1 and 61*61*61*61
but somewhere it goes funny
22 ms both parts (my code)
3.2s both parts (golfed code)
did anyone get a super round looking answer. like with 4 trailing zeros
not me, definitely
i thought i got something wrong because it was so round looking so i didn't submit it immediately lol
everyone's puzzle input length was 99 right
yes
hm
you had exactly 4 trailing zeroes ?
ya
are thinking of a closed form solution?
ok yeah, that's definitely the problem. It's adding the view_count in way more times than it should and I am not sure why
nah
for an answer with 4 trailing zeroes there should be 4 factors of 10
we are multiplying 4 scores
is it possible ๐ค
should be , i am just wondering lol
nvm
i figured ot
there surely can be plenty option with that
lol
p1
z=()=>document.body.innerText.split(/\n/).map(r=>[...r]);
w=(m)=>m[0].map((_,i)=>m.map(r=>r[98-i]));s=z();f=z();
for(i=0;i++<4;){
for(y in f)for(x in f[y]) if(f[y].slice(0,x).every(v=>+v<+f[y][x])) s[y][x]='x';
f=w(f);s=w(s);
}(""+s).split('x').length-1;
p2
f=document.body.innerText.split(/\n/).map(r=>[...r]);
w=(m)=>m[0].map((_,i)=>m.map(r=>r[98-i]));s=f.map(r=>r.map(c=>1));
for(i=0;i++<4;){
for(y in f)for(x in f[y])s[y][x]*=(1+f[y].slice(+x+1).findIndex(v=>v>=f[y][x]))||98-x
f=w(f);s=w(s);
}Math.max(...(""+s).split(','));
see if that helps the python golf
Source : reddit
i did
Ah dang. I tried just making them sets and crossing my fingers but obviously that's not going to work
hmmmmmmmmmm
so each of your individual score had a trailing zero
not necessarily
22ms huh, I guess cubic cost isn't bad for such a small input
it could be like, 2, 5, 4, 40 or something
oh and the max is 99. so yeah more had to have trailing zeros
98
i could find the actual ๐ค
2,50,50,8 types
dew it
wait that would leak answer
dont dew it
50, 50, 25, 16 would be possible
there are multiple inputs though
and give 6 zeroes
yea
isn't everyone's puzzle input unique?
no, there are a few for each day

though i assume for something like today it'd be really easy to just generate more
I don't think 7 zeroes is possible 
Yeah I assumed they generated them to a rule
anyways, it's 10 20 48 50
dang
you would need 25, 25, 25, 5 and then multiply something with 2 7 times
which I'm pretty sure would violate the input size we have
Ah, I am frustrate. I'm going to actually take a break today and not get obsessed like I did yesterday
i had something similar
3 trailing
Ugh, today is making a big dent in my goal of doing each day in under 1ms...
Wish I could code that fast
with context to python or other lang
rust ๐
Rust, I don't think I could get that fast without manual bytecode hacking in python
Would Cython count as python? 
just use pypy
Ah no. What went wrong?
it works but
just ugly
i want to rewrite it
Oh right. Yeah it becomes a bit simpler when you use vectors.
I didn't use numpy, but my coordinates are just tuple[int, int] vectors, with an add function for adding two of them together.
complex numbers ๐
So if you're add position and you want to move one place to the right, you do ```py
position = add(position, (0, 1))
what is the purpose of adding two coordinates ๐
Is that what you did? ๐
oh so like math vectors
Yeah
numpy vectorize that shit
just easier, your loop is just
for dx, dy in [(0, 1), ...]:
while x + dx in bounds and y + dy in boundsO
...
ahhhhh well i second guessed myself so hard on the second part
I think it makes it simpler to reason about the problem, as you're thinking "locally" so to speak.
I like APL for vectoring
nah i used numpy. but i considered it
i was like "it can't be that big right?"
yeah it's only 99, so even n^3 is pretty ok
I have seen some pretty lovely APL solutions for today
It didn't even cross my mind tbh
I may write my own
what's APL solutions
this is pretty much the ideal problem for APL
Same yeah 
manipulate a 2d matrix
A Programming Language
i see it makes it a lot more readable
but still you will have like 4 loops for calculating the 4 scores right
why use numpy when you can use 7 loops ๐
nah, just one loop
LMAO
a lot of solutions used it last year for all the conway's GOL stuff
so you will be going in all 4 directions simultaneously or something
Well three: for each coordinate, for each direction, for each coordinate in that direction until you reach an edge or an equal or greater height tree.
09818
anyhow i still don't understand how 9 can see 1 but oh well
Yeah, it's near the top of my solution here @midnight vortex
adding vectors is just adding the components
yeah thats what i thought
That is a good point lol
had to re-read the prompt to realise it's intended
i thought i had a bug or something
walruses not allowed in the square brackets (unless it's a 3.11 thing?)
!e
x = 'abc'[y:=1]
print(x,y)
oh goodness, I'm such a numpty. The whole day 2 for loop was running inside the day 1 one 
ah so it is 3.11
works in 3.10 too?
@eternal spindle :white_check_mark: Your 3.10 eval job has completed with return code 0.
b 1
yeah
wait a sec
nope, ok in 3.10 as well
what version is my python on then
crack
you're probablythinking it does not work in a comprehension. seems fine in an index.
my vscode is showing red squiggles
i have my interpreter set to 3.11
must be a bug
for _ in range(9801) can be changed to for _ in[0]*9801
oh wait nvm you actually use _
there were found lots of false positives for walrus operator in pylance.
That's not the right answer. Curiously, it's the right answer for someone else; you might be logged in to the wrong account or just unlucky. In any case, you need to be using your puzzle input.
=_=
i see
I don't think I've ever made a good way of exploring four cardinal directions cleanly
3.10 as well
ye i know now
ooooh, we each get unique puzzle inputs, huh
pylance initially convinced me otherwise
but apparently running the code actually works
Not everyone's is unique, apparently, but there's a number of different ones
At least I know I must be in the right ballpark.
not a big fan of my scenic score calculator, but would appreciate feedback
def scenic_score_calculator(tree_grid, location):
left, right, up, down = 1,1,1,1
row, col = location[0], location[1]
tree_val = tree_grid[row][col]
while col-left>0:
if tree_grid[row][col-left] >= tree_val:
break
left+=1
while col+right<98:
if tree_grid[row][col+right] >= tree_val:
break
right+=1
while row-up>0:
if tree_grid[row-up][col] >= tree_val:
break
up+=1
while row+down<98:
if tree_grid[row+down][col] >= tree_val:
break
down+=1
if col == 0:
left = 0
if col == 98:
right = 0
if row == 0:
up = 0
if row == 98:
down = 0
return up*down*left*right
just feels very repetitive :/
You could use loops to avoid repitition
here's a hint
point = (x, y)
for direction in ((1,0), (0,1), (-1, 0), (0, -1)):
# Add the direction to the point
point = ...
Now do what you want
vector addition is pretty straightforward with numpy, but what's a one-liner in python?
new_p = [p[0]+d[0], p[1]+d[1]]?
sure that works
or tuple(sum(n) for n in zip(point, direction))
!e
point = (1, 2)
dirn = (3,4)
print(tuple(sum(n) for n in zip(point, dirn)))
@proud fog :white_check_mark: Your 3.11 eval job has completed with return code 0.
(4, 6)
oh thats a great idea
I'm guessing that being unchanged by %98 is a check that would work in all 4 cases
friendship broken with tuples, complex is best friend now
I was thinking that for a bit, but didn't want to re-cast the whole thing onto the complex plane
Woohoo, got it! It wasn't anything wrong with the code after I fixed that for loop, I just had slightly misread the question
# these nested for loops *should* retrieve the answer for part 2
should ๐
whats the image output like for this?
fwiw I have ~0.5ms in rust, and I should be able to get it a bit faster still
green points are?
Yeah my first run was like 600mus
Like, how fast?
Not sure, currently there are a lot of bounds checks I should be able to remove
visible trees
I could make my solution faster by benchmarking on my desktop 
mine runs in 377 ms once I turn the visualiser off, which isn't bad at all as I didn't really optimise it
2022/6
Is this fast enough?
Oh I accidentally made something like that while debugging my code too! just with character output ๐
is that the shell time command?
day 6?
Yep
yeah that's not very accurate
idk, here's mine
WTF
try something like criterion or hyperfine for benchmarking
d6?
Yeah
why you posting this in d8?
Because I'm dumb
so you don't suffer time loss from startup or cold caches or system behavior... 
And it's the only one I've done in Rust
I have ~8us on that
:o exactly the same as mine.
(what even was d6?)
oof
very helpful thank you
Get it so small it's all zeros
oh d6 was the boring moving window one
oof? that doesn't suit you! More like woof! kek

where I got disappointed by rust const generics
why, didn't want to use nightly?
how is yours taking so much time? The logic is pretty simple lol. Just O(n)
arithmetic is not supported, and the compiler screamed at me when I tried enabling it ๐
Just starting the problem today, is it wrong to brute force the perimeter or is there a "smarter" way?
peri_count = len(np_forest[0, 0:]) + len(np_forest[-1, 0:]) + len(np_forest[1:-1, -1]) + len(np_forest[1:-1, 0])
You mean to capture the trees at the edge?
Yeah, since every tree along the perimeter is visible, I was just counting the length of the perimeter
My code works by scanning each row, then rotating the matrix and doing it again (and again and again), and to get the perimeter I just set the initial height it checks against to -1 ๐คทโโ๏ธ
every other way of doing it seemed excessively brain-achey to me
and even enabling it didn't help me so much
I have no idea what that error is saying
you need to constrain N so it doesn't overflow with the addition
the syntax is very funny
yeah, if the type in the where clause is valid then the compiler knows N is not 255
which, lol
no it's worrying about an usize overflowing
when the value is really 4 or 14
it's great
anyways I did 2 solutions for day 6, one with normal window checking and another with a rolling hash
aha
you could save a constant amount of memory by not making the array 256 long
rust seems like a pain to write. i hope transpilers get good enough to just auto convert what I write in python to rust to gimme blazing speeds
you should just pass your code to a GPT and tell it to make it faster
same level of commitment and similar results
rust is fine to write 
(I write solutions on my phone even)
oh no, a few extra bytes of statically allocated memory
for an object that's created exactly two times ๐
back on topic, writing an O(n*m) solution for today was fun
writing transpilers from python to rust would mean giving the compiler enough info to produce rust code that compiles, right
wouldn't that just be like writing rust lol
transpilers from python to a statically typed compiled language is always lossy to some extent, unless maybe you do something like cython
but at that point you've already left python
You could use loops to avoid repetition

?
lol I read it out of context and was like "wut?"
True, true. I probably read it at a funny angle ๐
I wonder if they managed to shorten their code
Used numpy today, but just to do easy vertical slices. Otherwise I just used iteration over the numpy arrays. Knowing little about numpy it feels wrong but idk
Who can see anyway to improve this? I already see a change for given a base value to tlt1
Combine part 1 and 2, they're basically the same
is there any clever trick today or just looping over ?
def p1(day_input):
for i in range(1, r-1):
for j in range(1, c-1):
tree = int(day_input[i][j])
conditions = [
int(max(day_input[i][:j], key=lambda x: int(x))) < tree,
int(max(day_input[i][j+1:], key=lambda x: int(x))) < tree,
]
up = True
for ii in range(0, i):
if int(day_input[ii][j]) >= tree:
up = False
break
down = True
for ii in range(i+1, r):
if int(day_input[ii][j]) >= tree:
down = False
break
conditions.append(up or down)
if any(conditions):
visible_trees +=1
return visible_trees
Looping twice is prolly the most common solution today ๐
why not horizontal slices as well?
well yes but you get that easily from Python anyway :)
I meant the reason I went numpy and not Python list[list[int]] is for that sweet vertical slice
right
@orchid adder you wrote your own benchmarking thing right? how did you get the compiler to not optimize away your function calls?
did you loop over the slices to compare the elements?
pretty much yes
that's the part I feel like there might have been a way in numpy if I knew how
not a huge deal though
๐ but it's the whole point of numpy
i tried, I really did
but the API for numpy is so huge and Googling the precise function you need to do something can be really difficult
so I gave up and did it the scrub way :) will be cool to read better ways to do it after the fact
but I have to refactor my code to add classes, brb :)
ok, I think this'll do for now. ready for the next day.
# create an empty class
class Tree(namedtuple('Tree', 'x y h')): v=0;s=1
#read the data into a dictionary
data = [*map(list,open(filename).read().splitlines())]
forest = {(x,y):Tree(x,y,h) for y,row in enumerate(data) for x,h in enumerate(row)}
H,W = max(forest)
# loop over each tree
for t in forest.values():
# loop over the 4 directions
for z,d in [
(list(zip( range(0,t.x)[::-1], repeat(t.y))), t.x),
(list(zip( range(t.x+1,W+1), repeat(t.y))), W-t.x),
(list(zip( repeat(t.x), range(0,t.y)[::-1])), t.y),
(list(zip( repeat(t.x), range(t.y+1,H+1))), H-t.y)]:
t.v |= all(t.h>forest[x,y].h for x,y in z)
t.s *= next((c for c,e in enumerate(z,1) if t.h<=forest[e].h),d)
print('part1', sum(t.v for t in forest.values()))
print('part2', max(forest.values(),key=lambda t:t.s).s)
Tom! You're here!
You're just adding classes for show!??
exposed
interesting how the height of the trees is distributed. i had expected differently. Yet it makes sense when you think about it, higher trees, are more likely in the middle of the forest.
a white pixel = height 9; black = height 0
Iโm adding classes to create a nice architecture to unify my part 1 and 2 :)
I am indeed. Was out yesterday (had to code day 7 on my phone) but Iโm here now!
Btw Jupyter on iPhone kills battery life like no tomorrow - I have learned
TIL you can get Jupyter on iPhone
Indeed - Carnets is the app
Could even pip install more-itertools and parse into it
Fancy fancy
Can it push to GitHub?
Or do you have to retype everything on the computer?
Looks like it has concentric rings of tall trees 
So far pretty much this
seems a bit like it. i was confused at first, why the visibility map had rings on it. after seeing the hight map, it makes perfect sense
I actually had to type first on my work computer, then again on my phone to actually run against my input
Wait no don't actually....
And now I got to type again onto my actual computer
Just email it to yourself...
Canโt really do that
Donโt want to go into it too much, but work is strict about that stuff
Where there's a will lazy developer, there's a way
aren't we all ? my 1 liner counts as a class!
Oh yah Iโm also adding classes so I can assert every detail about the test input on the description
screenshot and OCR!
I was doing that for a bit for fun, my class used to have 14 variables. ๐
I return a result and ? it
(? is the best verb)
std::intrinsics::black_box is also useful
The score plot is also interesting:
and on a log scale:
What testing code?
pandas it is
df = pd.read_csv("day_8.input", header=None, squeeze=True).apply(list).apply(pd.Series).astype(int)
print(pd.Series([-1]).pipe(lambda s: df.apply(lambda c: s.__setitem__(0, -1) or c.apply(lambda n: s.__setitem__(0, s[0] + 1) or pd.concat([df.iloc[s[0], :c.name], df.iloc[s[0], c.name+1:], df.iloc[:s[0], c.name], df.iloc[s[0]+1:, c.name]], axis=1).fillna(-1).lt(n).all().any()))).sum().sum())
print(pd.Series([-1, lambda s, n: len(s) if not s.ge(n).any() else s[s.ge(n).argmax():].diff().iloc[1:].le(0).argmax() + s.ge(n).argmax() + 1 if s[s.ge(n).argmax():].diff().iloc[1:].le(0).any() else len(s)]).pipe(lambda s: df.apply(lambda c: s.__setitem__(0, -1) or c.apply(lambda n: s.__setitem__(0, s[0] + 1) or pd.Series([s[1](df.iloc[s[0], :c.name][::-1], n), s[1](df.iloc[s[0], c.name+1:], n), s[1](df.iloc[:s[0], c.name][::-1], n), s[1](df.iloc[s[0]+1:, c.name], n)]).prod()))).max().max())
:incoming_envelope: :ok_hand: applied mute to @faint helm until <t:1670538202:f> (10 minutes) (reason: newlines rule: sent 108 newlines in 10s).
The <@&831776746206265384> have been alerted for review.
!unmute 712372740509663294
!unmute 712372740509663294
:incoming_envelope: :ok_hand: pardoned infraction mute for @faint helm.
:incoming_envelope: :ok_hand: pardoned infraction mute for @faint helm.
!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.
... I won't unmute....
I've already gotten snucky'd once today >_>
@faint helm Try using our pastebin above for big chunks of code like that~
algorithm: Concert to hashset (dict) <Position (complex), height>.
For part1, Get the kets of all rows and columns, for k*4 rows. for each vec, only keep v[0] and rows where height > highest.
For part2: for each positon (key) count in each 4 directions until reach place outside of forest or blocking tree.
Whatever.
I'm going back to writing my C solution.
meanie.
huh, will have to try
I think it's std::hint now
I didn't really intentionally do it, but it should make sure the thing is not optimized away
how?
if it always returns an ok ig
for all it knows the input is completely different and could cause something weird to happen
it's not a pure function
that's probably the best way. currently I have input as a lazy static so if I want to time parsing I'll need to do that
I just read from file
and time the read+basic parsing and the actual logic separately
I c I c
aoc_helper can automatically run the aoc tests (usually)
(well, that calls the intrinsic, since all (most?) intrinsics are perma-unstable)
black box was made stable though I thought
right right
err, I found some rust weirdness
so this is fine
let thing = 42;
macro_rules! bleh {
() => {
thing
};
}
let res = bleh!();
that looks more like code golf!
apparently this is not
macro_rules! bleh {
() => {
thing
};
}
let thing = 42;
let res = bleh!();
did you seethe numpy solution posted on reddit?
and that kinda breaks my mental model of macros
is that a macro hygiene issue? decl macros always confuse me.
:p it does indeed, tends to be esoteric too
the second complains about thing not being in scope
no, did they find a vectorized way?
checks
import numpy as np
grid = np.array([list(x.strip()) for x in open('in.txt')], int)
part1 = np.zeros_like(grid, int)
part2 = np.ones_like(grid, int)
for _ in range(4):
for x,y in np.ndindex(grid.shape):
lower = [t < grid[x,y] for t in grid[x,y+1:]]
part1[x,y] |= all(lower)
part2[x,y] *= next((i+1 for i,t in enumerate(lower) if ~t), len(lower))
grid, part1, part2 = map(np.rot90, [grid, part1, part2])
print(part1.sum(), part2.max())
oh nice, thanks
that's...cubic?
somewhat vectorized but not fully
4HbQ posted some of my favorite solutions last year using numpy, and I swore I'd spend more time learning it, instead I picked up pyspark which is much less useful here.
I think so
his solution is the opposite of mine, he did 4 rotations for each tree, I did for each tree, 4 ranges. but similar otherwise.
i guess it's because the definition is part of the macro's syntax context?
so everything before the macro definition is included and everything after isn't?
I would have expected it to be like that but around the usage
me too. i'm not too versed in how declarative macros work. i'm somewhat more used to proc macros.
ok, so apparently that's a thing
that's an interesting choice to say the least
15-20% speed increase from generating code with macros to remove if statements in loops
neat
huh. how does that work
I have a sweep I need to to 4 times
one from each direction
but re-using code across vertical and horizontal sweeps is kinda hard
I need to swap the i and j index at one point
the macro called ended up as this, which is kinda disturbing
oh I see, that's why you were messing around with identifiers
right
I could probably have a boolean whether to transpose, rather than this thing
a wrapper macro maybe ๐ฅด
lol, apparently you can do this
oh, the @ isn't special?
no i think that's a feature
so this is a thing you can do
that's...intriguing
oh no...
I've discovered great powers
I did something like this by defining the ranges first, and then looping over the 4 ranges
for t in forest.values():
for z,d in [
(list(zip( range(0,t.x)[::-1], repeat(t.y))), t.x),
(list(zip( range(t.x+1,W+1), repeat(t.y))), W-t.x),
(list(zip( repeat(t.x), range(0,t.y)[::-1])), t.y),
(list(zip( repeat(t.x), range(t.y+1,H+1))), H-t.y)]:
t.v |= all(t.h>forest[x,y].h for x,y in z)
t.s *= next((c for c,e in enumerate(z,1) if t.h<=forest[e].h),d)
Still doesn't really buy you much though. ๐ฆ
it doesn't, but I had no idea you could do that with rust macros
I would have guessed the screenshots would be obvious ๐
in meetings so not carefully reading, not sure why I thought it would be python on this server. ๐
i wanna make rust macros in python but i've put it on hiatus because i lost motivation due to the amount of work required to do it using tokens
table_proj = lambda table, x: table[x[1]][x[0]]
line_forward = lambda x: (x[0] + 1, x[1])
line_backward = lambda x: (x[0] - 1, x[1])
col_forward = lambda x: (x[0], x[1] + 1)
col_backward = lambda x: (x[0], x[1] - 1)
class Part1Matcher():
def __init__(self):
self.max = -1
def __call__(self, v):
coord, value = v
if self.max < value:
self.max = value
return coord
def generator(cont, from_it, to_it, proj_func, next_func):
curr_it = from_it
end_it = next_func(to_it)
while curr_it != end_it:
yield proj_func(cont, curr_it)
curr_it = next_func(curr_it)
def part1(content):
def update_visibilities(table, results):
for coord in results:
visibility_table[coord[1]][coord[0]] = True
def find_visibles(gen):
matcher = Part1Matcher()
return [r for n in gen if (r := matcher(n)) is not None]
lines = [list(map(int, [*line])) for line in content.strip().split("\n")]
row_count = len(lines)
col_count = len(lines[0])
visibility_table = [[False]*col_count for _ in range(row_count)]]
table_and_index_proj = lambda table, x: (x, table_proj(table, x))
for i, _ in enumerate(lines):
col_gen = generator(lines, (0, i), (col_count-1, i), table_and_index_proj, line_forward)
update_visibilities(visibility_table, find_visibles(col_gen))
r_col_gen = generator(lines, (col_count-1, i), (0, i), table_and_index_proj, line_backward)
update_visibilities(visibility_table, find_visibles(r_col_gen))
for i, _ in enumerate(lines[0]):
col_gen = generator(lines, (i, 0), (i, row_count-1), table_and_index_proj, col_forward)
update_visibilities(visibility_table, find_visibles(col_gen))
r_col_gen = generator(lines, (i, row_count-1), (i, i), table_and_index_proj, col_backward)
update_visibilities(visibility_table, find_visibles(r_col_gen))
return sum([cell is True for row in visibility_table for cell in row])
the unsafe version of this line looks hilarious
//scenic[$ix_i][$ix_j] *= ($j as i32 - ix).abs();
*unsafe { scenic.get_unchecked_mut($ix_i).get_unchecked_mut($ix_j) } *= ($j as i32 - ix).abs();
thoughs ?
oh right
!e
print([]*1000000000)
@orchid adder :white_check_mark: Your 3.11 eval job has completed with return code 0.
[]
I would change
visibility_table = []
for i in range(len(lines)):
visibility_table.append([False]*len(lines[0]))
```into
```py
visibility_table = [[False]*col_count for _ in range(row_count)]]
tyvm that felt clumsy when I wrote it
what's the point of [*line] here?
lines = [list(map(int, [*line])) for line in content.strip().split("\n")]
actually for 2 reasons. (1) the string is iterable, (2) you don't need to convert them to ints.
'7'>'2' etc
to decompose the string into single char, and then cast them to int
strings are iterable
you can just put line
ok
lines = content.splitlines()
also these enumerate lines are weird
for i, _ in enumerate(lines):
enumerating and not using the value
this one could be just
for i in range(row_count):
you do have a point but as we're talking about actual numbers, which could lead to calculus later on on part 2 (spoiler it didn't), I wanted to work on int
similar for the one with lines[0]
fair point, I mean I also converted to int initially. ๐
Did anyone else store the trees in a dictionary, or is that more of me being me?
what does that buy you?
what for xelf ? to avoid wondering is the tree has already be deemed visible ?
granted, I suspect I didn't do the same approach as most people here
well my algo was already shit so I figured, why try to optimize
I got the idea for how to do it in O(n*m) pretty quickly, so I just implemented that
I dunno, just a habit of converting 2D arrays into a dictionary, frequently makes boundary checks easier. in retrospect I could have just kept it as a 2d list.
n the w of the array or the number of elems ?
didn't take a look yet
I got lost making a generator with projection and everything
couldn't be bothered by part2
call it a comfort dictionary. It's my happy place.
data = open(filename).read().splitlines()
forest = {(x,y):Tree(x,y,h) for y,row in enumerate(data) for x,h in enumerate(row)}
W,H = max(forest)
I feel like I might've made this more complicated for myself than it had to be
https://github.com/AM2i9/adventofcode/blob/master/2022/08.py
Nah, looks fine. Not overly complicated. Especially compared to some of the stuff we've seen.
fair
god I feel dumb now
Yeah, it's like half the size of mine
I've sink some times with my generators and my matchers, and all I had to do is build a dict to make iteration by index easier
What'd I do now?
I don't generally use a dictionary unless there is good reason for it
is there a better way to get "length of items iterated and stop at the first one"? I used next and enumerate eventually, my initail version was a for loop with break.
next((c for c,e in enumerate(z,1) if t.h<=forest[e].h),d)
(z is the range, d is the default for next)
e.g. fast member lookup
or something sparse
c = d
for c, e in enumerate(z,1):
if t.h <= forest[e].h:
break
or wait
I'm still with the loop/break. since I took the generator path, I've made a do_until just for that
the default would have to go in an else
I thought about using itertools.takewhile
this is terrible
for c, e in enumerate(z,1):
if t.h <= forest[e].h:
break
else:
c = d
oof a for else
You can also bake the default in:
for z in range(y+1,len(row)):
if t>row[z]: down+=1
if t<=row[z]:
down+=1
break
(before I had the class, and just iterated over the rows)
isn't that ?
for z in range(y+1,len(row)):
down+=1
if t<=row[z]:
break
hate for-elses. I always just use fors in functions with an early return
probably, I was typing fast and built a class instead of fixing it.
for else can be pretty nice
no for else
You have no idea how close I was to using for...else before I realized I misinterpreted the problem
but that's no fun
this pattern is fun
for thing in things:
for x in thing:
if cond(x):
break
else:
continue
break
to break out of both loops
oof
I didn't even know they were a think until this year, and I've been using Python on and off for almost 10 years now.
I've never once wanted something like that, the whole concept just doesn't make sense to me
I use 2 space indents, that makes it ok :^)
jail^jail then
it makes sense to me. You'd use it when you want to search for something in an iterable, end the search immediately if you find what you want, and do something else if you didn't find what you were looking for.
it's just a function with an early return is almost always the better (and less fun) way to do it
talking about fun, I'm up to 4 classes for my day 8
sometimes I really miss python's comprehensions
let res = visible
.into_iter()
.map(|v| {
v.into_iter()
.map(|vis| if vis { 1 } else { 0 })
.sum::<i32>()
})
.sum();
just got to write all my test cases and type hints
sum(map(sum, visible)) yk
I guess there isn't something like .int_value() on bool in rust?
Four!?
@dataclass(frozen=True,slots=True)
class Tree:
height:int
@dataclass(frozen=True,slots=True)
class Forest:
trees: list[list[Tree]]
done
boring
you literally have an SO answer written
only 2?! nowhere near enough :)
fn main() {
println!("{}", true as i32)
}
nothing type agnostic?
if?
like the if thing will work regardless of int type
.into() in the same thead
:/
idk if that works
Needs a type annotation to know what to convert into
you sum with i32 right
I do
Rust doesn't really do "agnostic"
why not just use as i32
It wants you to be specific
because it would be nice if I didn't have to repeat myself
and let rust deduce the type
(at least one of the times)
it's only a few characters
does doing that and removing ::<i32> still work
it doesn't
ok
and it's not about char count, it's about the principle ๐
Is the input a square? can we assume it always will be?
what if there's a square font
the monospace font makes squares look like rectangles
adding a space can do wonders
nice
works for fonts that are close to 2:1
err
maybe my math is wrong 
if cells are 2:1
I guess spacing matters
well a little 2 px off but it's improved
the rect from upper right dot to lower left dot is 64x68
so less than 10% off
neat
up to 5 classes now
oh no
Once you have your input you can assume anything about it you want. It won't change.
this is how i render graphics in the terminal -- upper-half block characters allow 2 "pixels" per character to make things more squre
(at least in nice compiled languages simple classes are zero cost)
from itertools import product
from functools import reduce
from operator import mul
import numpy as np
with open("08_input.txt") as file:
data = file.read().splitlines()
visible_trees = 2 * (len(data) + len(data[0]) - 2)
data = np.array([list(map(int, list(line))) for line in data])
max_score = 0
for i, j in product(range(1, len(data) - 1), repeat=2):
slices = (data[i, :j][::-1], data[i, j + 1 :], data[:, j][:i][::-1], data[:, j][i + 1 :])
current_value = data[i, j]
visible_trees += any(np.amax(slice) < current_value for slice in slices)
max_score = max(max_score, reduce(mul, (np.argmax(slice >= current_value) + 1 if np.amax(slice) >= current_value else len(slice) for slice in slices)))
print(visible_trees)
print(max_score)
can use accumulate:
def part_one():
visible = np.zeros_like(trees, bool)
visible[[0, -1]] = visible[:, [0, -1]] = True
for i in range(4):
dilated = np.maximum.accumulate(np.rot90(trees, i))
dilated[1:] -= dilated[:-1]
np.rot90(visible, i)[dilated.nonzero()] = True
return visible.sum()
Took my a while but overall happy with my solution for part1:
with open('day8\input.txt', 'r') as f:
data = f.read().split('\n')
visible = len(data[0]) * 2
for num, line in enumerate(data[1:-1], 1):
for position, tree in enumerate(line):
if position in (0, (len(data[0])-1)):
visible += 1
continue
if all(line[position] < tree for line in data[num+1:]) or all(line[position] < tree for line in data[:num]) or all(trees < tree for trees in line[position+1:]) or all(trees < tree for trees in line[:position]):
visible += 1
print(visible)```
As always, lmk if you see any obvious/simple improvements
you know, we should have some "up the ante" style challenge here much like the subreddit has
Here is a data generator, change n as you see fit
import random
random.seed(42)
n = 1000
for _ in range(n):
print(''.join(random.choice('0123456789') for _ in range(n)))
runtimes for my solution
n time part1 part2
100 386us 920 279357
500 9.30ms 4390 2462400
1000 37.5ms 9024 2330944
5000 1.11 s 44723 7562880
10000 5.14 s 89316 4520880
I'm ready to unveil my masterpiece
this is gonna be a big one
import dataclasses
import enum
import io
import math
from typing import Callable, Iterator
import numpy as np
import numpy.typing as npt
class Direction(enum.Enum):
LEFT = enum.auto()
RIGHT = enum.auto()
TOP = enum.auto()
BOTTOM = enum.auto()
@dataclasses.dataclass
class TreeDirectionDetails:
view_distance: int
is_visible: bool
@dataclasses.dataclass(frozen=True)
class Coords:
x: int
y: int
class Tree(dict[Direction, TreeDirectionDetails]):
def __repr__(self) -> str:
return f"Tree({super().__repr__})"
@property
def is_visible(self) -> bool:
return any(
[direction_details.is_visible for direction_details in self.values()]
)
@property
def scenic_score(self) -> int:
return math.prod(
[direction_details.view_distance for direction_details in self.values()]
)
class TreetopTreeHouse:
def __init__(self, tree_height_map: str) -> None:
self._tree_map = self._create_tree_map(tree_height_map)
self.trees = self._create_trees()
@classmethod
def read_file(cls) -> "TreetopTreeHouse":
with open("input.txt") as f:
return cls(f.read())
@staticmethod
def _create_tree_map(tree_height_map: str) -> npt.NDArray[np.int32]:
f = io.StringIO(tree_height_map)
first_line = f.readline().strip()
f.seek(0)
return np.genfromtxt(f, dtype="i4", delimiter=[1] * len(first_line))
@staticmethod
def _create_tree_details(
current_tree_height: int,
direction: Direction,
directional_trees: npt.NDArray[np.int32],
) -> TreeDirectionDetails:
if direction in (Direction.LEFT, Direction.TOP):
directional_trees = np.flip(directional_trees)
view_distance = 0
for view_distance, tree_height in enumerate(directional_trees, start=1):
if tree_height >= current_tree_height:
return TreeDirectionDetails(
view_distance=view_distance, is_visible=False
)
return TreeDirectionDetails(
view_distance=view_distance, is_visible=True
) # no trees can be found
def _create_tree(self, row: int, col: int) -> Tree:
tree_height = self._tree_map[row, col]
directions = {
Direction.LEFT: self._tree_map[row, :col],
Direction.RIGHT: self._tree_map[row, col + 1 :],
Direction.TOP: self._tree_map[:row, col],
Direction.BOTTOM: self._tree_map[row + 1 :, col],
}
tree = Tree(
{
direction: self._create_tree_details(
tree_height, direction, directional_trees
)
for direction, directional_trees in directions.items()
}
)
return tree
def _create_trees(self) -> dict[Coords, Tree]:
trees = {}
tree_map_iter = np.nditer(self._tree_map, flags=["multi_index"])
for _ in tree_map_iter:
row, col = tree_map_iter.multi_index
tree = self._create_tree(row, col)
trees[Coords(col, row)] = tree
return trees
def _treehouse_query(self, fn: Callable[[Iterator[int]], int], query: str) -> int:
return fn(getattr(tree, query) for tree in self.trees.values())
def sum_visible_trees(self) -> int:
return self._treehouse_query(sum, "is_visible")
def max_scenic_score(self) -> int:
return self._treehouse_query(max, "scenic_score")
def main() -> None:
tth = TreetopTreeHouse.read_file()
print(
"Sum of visible trees from outside the grid:",
tth.sum_visible_trees(),
)
print(
"Highest scenic score possible for any tree:",
tth.max_scenic_score(),
)
if __name__ == "__main__":
import timeit
print(timeit.timeit(main, number=1))
And if you thought that was crazy, here is the unit tests for it
๐ฅด

congrats for being significantly longer than my rust solution
thank you very much!
I did it like this so I could assert as much detail as I dare from the problem description
I have 29 test cases
eh almost
So, I assert whether a tree can be seen from a particular direction or not
Hey, I have two test cases
but not the actual tree that might block it if it can't be seen
that's the one piece of info that was too much even for me XD
no TreeFactory or TreetopTreeHouseFactoryProducer, smh
but everything else I have
anyone did a oneliner?
If they do a channel opposite to code golf I'll definitely go for that
do you know of a ranking/leaderboard where codeperformance/language is ranked?
i do not
What if we made one?
How do you rank by language?
Extra points for using the creators favorite language?
you can compare against my rust solution for some random larger data ๐
Also I specifically got Discord Nitro this month for the extra long comment length

no i meant
only python solutions get compared
to each other
Why seed?
so you get the same data
Consistent input, same answer for whoever runs it
Will it guarantee the exact same output every single run?
I thought even withing the seed there was still some variance?
... I hope my bank changes their seed for passwords every once and a while
that's the point of seed 
why would your bank need a seed for passwords ๐ค
Hello
Please use our pastebin for longer solutions
https://paste.pythondiscord.com/
Thanks
python's random is pseudorandom. same initial seed -> same output
And Rust BTW?
rust doesn't have one builtin
every computer rng is pseudorandom
you can sample real world stuff to get some actual randomness, but that's about it
One other thing I'm missing - a complex inheritance tree
for all the OOP I do I don't have much inheritance
gotta get me some diamond hierarchies going
Extended documentation for Rust's Rand lib
where algorithms that only people who attend university can solve?
rip english sentence ig
and preferably inheritance the wrong way around
let your point class inherit from Tree
dumb stuff like that
Released every day at 00:00 -0500 for the month of December
oh right, python supports multiple inheritance
but what is the time where the challenges get unsolvable for a pleb like me
yup exactly
maybe: how much ahrder will it get
lots
that's why I said - diamond hierarchy :)
just to mess with everyone
class Tree(Trunk, Leaves, Point):
...
follows "is a" relationships, a Tree is a Trunk (among other parts)
not enough type hinting here as well
gotta have some of that
type hints should be added, but they don't need to be correct
!e
a: (a := 42) = -1
print(a)
@orchid adder :white_check_mark: Your 3.11 eval job has completed with return code 0.
42
import numpy as np
with open("day8/forest.txt", "r") as f:
forest = f.read().split("\n")
int_forest = [list(map(int, row)) for row in forest]
np_forest = np.array(int_forest, dtype = "i")
sub_grid_end = len(np_forest) - 1
visible = 0
for coords, height in np.ndenumerate(np_forest):
x,y = coords
if (x in range(1, sub_grid_end) and y in range(1, sub_grid_end)):
#look up
if height > np.amax(np_forest[:x,y]):
visible += 1
#look down
elif height > np.amax(np_forest[x+1:,y]):
visible += 1
#look right
elif height > np.amax(np_forest[x, y+1:]):
visible += 1
#look left
elif height > np.amax(np_forest[x, :y]):
visible += 1
else:
#perimeter is visible
visible += 1
print(visible)
Really happy with this after not getting yesterday
don't know about your timezone but technically you did get it within it's first 24 hours of being released
Interesting. so numpy can do that
wish I had known that you could slice 2D arrays
i mean i didnt get the solution for yesterday
ahh, for day 7, i see
So I found the issue, wondering if anyone knows how to fix it: my slice for look up is :x,y
the problem for part 2 is that goes from the beginning to x, when I need to look from x to beginning. I read I can use -1 to reverse a slice so I tried :x:-1,y but that didnt work
actually the same issue for left as well
right and down just happened to work because of their nature
Yeah this doesn't work for x = 0, you just have to [:x][::-1]
It's very annoying because it messes with the golf effort too ๐
I don't understand the phrasing for part 2, do they want to know the "scenic score" of the best tree?
Yes
What is the highest scenic score possible for any tree?
Literally the last sentence
I thought maybe it was given each tree, what is the best possible score for each one
(there's only one score for each tree)
Yeah ik
idk the word "Possible" kinda threw me off, so its just what is the score of the highest scoring tree
Yeah
I guess I can see how the word 'possible' might throw you off yeah
I think it's used as in 'if I were to pick any tree, what is the highest scenic score it's possible for me to pick'
kek
with open('08_input.txt') as f:
inputs = f.read().splitlines()
# inputs = """30373
# 25512
# 65332
# 33549
# 35390""".splitlines()
column = ["".join(i) for i in zip(*inputs)]
row = inputs
len_column = len(column)
len_row = len(row)
n = 0
for x_i, x in enumerate(row): # int, "30373"
for y_i, y in enumerate(x): # int, ["3", "0", "3","7","3"]
column_ = column[y_i] # "32633"
# The sides
sides = [
max(row[x_i][:y_i] or "0"), # left side
max(row[x_i][y_i+1:] or "0"), # right side
max(column[y_i][:x_i] or "0"), # top side
max(column[y_i][x_i+1:] or "0"), # bottom side
]
# This is already correct dummy
if y >= min(sides):
n += 1
else:
pass
print(n)
for some reason this works on the example input but not on my input
any idea why?
FUCK
The only one problem is the "0"
arsoitnaioersntioaernstioanrsietoanrsietnarostnarosietn
it is fine now
with open('08_input.txt') as f:
inputs = f.read().splitlines()
column = ["".join(i) for i in zip(*inputs)]
row = inputs
len_column = len(column)
len_row = len(row)
n = 0
for x_i, x in enumerate(row):
for y_i, y in enumerate(x):
column_ = column[y_i]
if y > min([
max(x[:y_i] or "."), # left side
max(x[y_i+1:] or "."), # right side
max(column_[:x_i] or "."), # top side
max(column_[x_i+1:] or "."), # bottom side
]):
n += 1
print(n)```
took me 1 day to made this
actually below one hour
if only the "." didn't fucked me over
I am back with part two
def part_two():
with open("day8/forest.txt", "r") as f:
forest = f.read().split("\n")
int_forest = [list(map(int, row)) for row in forest]
np_forest = np.array(int_forest, dtype = "i")
best_score = 0
for coords, height in np.ndenumerate(np_forest):
x,y = coords
vd = 0
vds = np.empty(0, dtype = 'i')
directions = [np_forest[:x,y][::-1], np_forest[x, :y][::-1], np_forest[x+1:,y], np_forest[x, y+1:]]
for direction in directions:
for tree in direction:
if height > tree: vd += 1
else:
vd += 1
break
vds = np.append(vds, vd)
vd = 0
if np.prod(vds) > best_score: best_score = np.prod(vds)
print(f"The highest scenic score is {best_score}")
why do people keep using numpy???
it's easy to index columns and rows with numpy
My absolute mess of a solution:
with open('day8\input.txt', 'r') as f:
data = f.read().split('\n')
score = []
for num, line in enumerate(data[1:-1], 1):
for position, tree in enumerate(line):
a, b, c, d = 0, 0, 0, 0
if position in (0, (len(data[0])-1)):
continue
for line2 in data[num+1:]:
if line2[position] < tree:
a += 1
else:
a += 1
break
for line3 in data[:num][::-1]:
if line3[position] < tree:
b += 1
else:
b += 1
break
for trees in line[:position][::-1]:
if trees < tree:
c += 1
else:
c += 1
break
for trees in line[position+1:]:
if trees < tree:
d += 1
else:
d += 1
break
score.append(a * b * c * d)
print(max(score))```
If you see any optimizations lmk!
Make a copy of data and transpose it; makes your vertical lookups easier and faster
(they look very much like line but are on transposed_line instead or something)
I have no idea what that means :P
transpose it?
Transpose:
Take the grid
A B C D E
F G H I J
K L M N O
and get the grid
A F K
B G L
C H M
D I N
E J O
You can do it with [''.join(col) for col in zip(*data)]
like rotating it?
Kinda
I see... Whats the * do in this context?
It's a mirror along the diagonal
Unpacks data into zip
Basically the same as zip(data[0], data[1], data[2], ..., data[-1])
Ok, where can I find more info about all these things, like what is the * called?
- is the 'unpack operator'
Or 'iterable unpacking'
(there's also ** to do it with a dict into keyword arguments)
Why does this automatically mirror it along the diagonal
So you know what zip does, right?
yeah it takes 2 iterables and puts them together as a tuple for each pair of elements
*it takes n iterables and iterates through them all
Not necessarily 2
If we take the grid from before,
A B C D E
F G H I J
K L M N O
That's represented as
['ABCDE',
'FGHIJ',
'KLMNO']
mhm
So zip(*data) (the same as zip(data[0], data[1], data[2])) will first give us ('A', 'F', 'K')
Right?
It doesn't only do the first three
That example only had 3
I meant to make that more clear but apparently I forgot the word 'here' lmao
part 1?
with open('08_input.txt') as f:
inputs = f.read().splitlines()
column = ["".join(i) for i in zip(*inputs)]
row = inputs
len_column = len(column)
len_row = len(row)
n = 0
for x_i, x in enumerate(row):
for y_i, y in enumerate(x):
column_ = column[y_i]
# The sides
sides = [
max(row[x_i][:y_i] or "."), # left side
max(row[x_i][y_i+1:] or "."), # right side
max(column[y_i][:x_i] or "."), # top side
max(column[y_i][x_i+1:] or "."), # bottom side
]
# This is already correct dummy
if y > min(sides):
n += 1
print(n)
didn't even tried to code golf this

Finally!
We've missed you around
Nope that's part 2
can someone explain where the 5th visible tree is (excluding the 16 outer trees) in the given example? ```
30373
25512
65332
33549
35390
i marked them with an X but i only see 4```
30373
2XX12
653X2
33X49
35390
The first 5 is visible via the 0 (up)
The second 5 is visible via the 3 (up) or the 12 (right)
The third 5 is visible via the 332 (right) or the 33 (down)
The 2nd 3 is visible via the 2 (right)
The 4th 5 is visible via the 33 (left) or the 3 (down)
@silent harness
The second 5 is visible via the 3 (up) or the 12 (right)
how?
i thought a tree could not be seen if the one infront of it was taller
which are the outer ones*
Right, but I told you two paths of outer trees that aren't taller
oops, true
8-2 is really giving me a headache
might actually need a little help here
.split("\n").filter(e=>e).flatMap((e,y,a)=>[...e].map((F,x,_,f=+F)=>[Array(y).fill().map((_,Y)=>+a[Y][x]<f).reduce((a,c)=>[c&&a[0],a[1]+c*(c&&a[0])],[1,0])[1],Array(x).fill().map((_,X)=>+e[X]<f).reduce((a,c)=>[c&&a[0],a[1]+c*(c&&a[0])],[1,0])[1],Array(e.length-x-1).fill().map((_,X)=>+e[x+X+1]<f).reduce((a,c)=>[c&&a[0],a[1]+c*(c&&a[0])],[1,0])[1],Array(a.length-y+2).fill().map((_,Y)=>+(a[y+Y+1]||[Infinity])[x]<f).reduce((a,c)=>[c&&a[0],a[1]+c*(c&&a[0])],[1,0])[1]]))
basically I'm struggling with the "tree that blocks counts, but not the edge"
I know I'll have to replace the map with reduce so I can end early, or maybe insert an extra item after and redo the map?
Okay, I've just had a horrible idea
I should make a helper that adds one unless it hits an edge
as it turns out this was because the environment variable didn't persist when I closed the terminal lol
Why are you using an environment variable lmao
Can the tree house see all the trees of the same height or just one or what is it really I'm confused
If i was 5 and there were 6 6 6 in front I would only see the first one?
And if I was 5 and there were 3 3 3 in front I would see all of them?
Correct
With those rules I found a tree with a smaller score
You're skipping valid trees in the middle of the path
Yeah I know you shouldn't be able to see them but they're counted
You have superhuman eyes so you can see them anyway idk
yeahh this is weird
My dad did that as well apparently lol
That does appear to be the right answer though
Possibly OBOE with not counting the centre tree, don't remember
yeah so this is my code so far
forest = """30373
25512
65332
33549
35390""".split(
"\n"
)
for i, j in enumerate(inner:=forest.copy()):
inner[i] = inner[i][1:-1]
inner = inner[1:-1]
count=0
for i in inner:
for x,y in enumerate(map(int,i)):
pass```
it works like
but apparently i shouldnt be enumerating i
what?
idk someone in #python-discussion said that
what are your trying to accomplish?
ok yeah ive kinda hit a wall
this is what I have ```py
forest = """30373
25512
65332
33549
35390""".split(
"\n"
)
listforest = [list(map(int, x)) for x in forest]
for i, j in enumerate(inner:=forest.copy()):
inner[i] = inner[i][1:-1]
inner = inner[1:-1]
count=0
for i,j in enumerate(inner):
row=listforest[i+1]
for x,y in enumerate(map(int,j)):
# print(i, x, y)
# check left
left = row[0:i+2]
if left.index(max(left)) == i+1:count+=1
# m = left.index(max(left))```
this is what listforest is
just a 2d list of the forest
inner is this which is just like the highlighted part
thats all fine and working but now I have no idea how to count visible trees
ill try and explain what I tried to do here
so say for the first tree which is row 2 column 2
I slice the list to get this
i check if the last item is the largest item in the list
and if it is i increase the count by 1
and it works for that tree
wait im an idiot
nvm im stuck
I have no idea what to do from here
i have no clue if it would even work
can anyone give me some hints
like point me in the right direction
ive tried to explain my code the best I can
don't bother trying to remove the outer edges
just iterate over every element in the list
it's easier
listforest = [list(map(int, x)) for x in forest]
for i, j in enumerate(listforest):
for x, y in enumerate(listforest[i]):
if len(listforest) - 2 < i or i < 1:
break
if x == 0 or x == len(listforest[i]) - 1:
continue
...```
is this a better start?
wait nvm
ive basically just done the exact same thing again ๐
or have I
I actually have no idea
just use english
tell us what you're trying to do
Hey
ok so rn this is what I have ```py
forest = """30373
25512
65332
33549
35390""".split(
"\n"
)
count = 0
listforest = [list(map(int, x)) for x in forest]
vert = list(map(list, list(zip(*listforest))))
for i, j in enumerate(listforest):
for x, y in enumerate(listforest[i]):
# left + right
left = listforest[i][:x+1]
right = listforest[i][x:]
if left.index(max(left)) is x: count+=1;print(1,end=" ");continue
elif right.index(max(right)) == 0: count+=1;print(1,end=" "); continue
else:print(0,end=" ")```
ignore the prints thats just testing
Which part are you having problems with?
Is this p1 or p2?
p1
for this line in the input 65332
it says all trees are visible from the right
when the middle tree shouldnt
ik its because im checking from left to right but im not sure how to fix it
like that line would work if it was flipped to 23356
but somethings wrong with my right hand check
Yeah I think you can do that
Just flip the right hand side list
Do right = listforest[i][x:][::-1] and you can use the same check as the left hand one
right = listforest[i][x::-1]
if left.index(max(left)) is x: count+=1;print(1,end=" ");continue
elif right.index(max(right)) is x: count+=1;print(1,end=" "); continue```
yeah i tried this and it didnt work
to what?
You'll have to figure that out yourself ๐
Np
You're just comparing numbers
Using is might break for larger numbers because of how python deals with numbers (specifically >256 iirc)
oh
I gtg, gl on the puzzle
ty bye
finally got it
forest = """...""".split(
"\n"
)
count = 0
listforest = [list(map(int, x)) for x in forest]
vert = list(map(list, list(zip(*listforest))))
for i, j in enumerate(listforest):
for x, y in enumerate(listforest[i]):
left = listforest[i][: x + 1]
right = listforest[i][x:][::-1]
top = vert[x][: i + 1]
bottom = vert[x][i:][::-1]
if left.index(max(left)) == x: count += 1
elif right.index(max(right)) + 1 == len(right): count += 1
elif top.index(max(top)) == i: count += 1
elif bottom.index(max(bottom)) + 1 == len(bottom): count += 1
print(count)```
just a bit confused about how part 2 works
if Ive a row of trees [3, 3, 0, 0, 4]
can the the first tree see the last tree?
yes
and it can see everything between those two?
even if its lower?
so like is it basically just find the highest tree in the list and it can see everything in between those two?
That's not how it works iirc
It just sees the 3 and that's it
Because everything behind the 3 is blocked by it
i thought the 4 was higher than the 3 so you can see it over the 3?
There was something about the treehouse having a roof
So you just count till the first tree >= to the current

