#AoC 2023 | Day 4 | Solutions & Spoilers
1161 messages · Page 2 of 2 (latest)
thanks for the heads up
I was really using it to remember how to use decorators I think.
More accuracy is good.
Fair enough :)
honestly it's been so long since I did anything like this so it felt really good
re sub feels a little bit unnecessary but otherwise ok. I’d probably say just do a string split on : to remove the label first
What do you mean by "strong split"?
hahah
could you split it inside the list comprehension or would you break it apart for clarity?
This is how I did it
Interesting - you used recursion
I did not think to do that
I had a brain cramp and couldn't think of how to do it iteratively
recursion was easier
I was thinking in trees
The only thing I would say with recursion is that if you had a longer file, you could get into issues
Python and recursion are not the best of bedfellows
yeah I was worried about exceeding the recursion limit
Eh - with only 200 or so that’s not going to happen
But only one extra order of magnitude and it would
Typically I’d say - unless the problem screams recursion, don’t use it in Python
Go iteratively wherever you can
Oh wow it's only 1,000
Yup
And Python does not have tail call optimisation
In your code I’m not sure that would help you anyway
But the point is - it’s not really recursion-oriented
My code shows how it can be done iteratively
Did I biff that? Should it be single? I looked up how to do private-ish methods in Python and that's what I read.
It should be single
People think double is for private methods
It really really isn’t
ok thanks. I probably read single then just did double
Double underscore prefix does a thing called name mangling
Basically you can’t call the method in a normal way outside the class if you do it - you have to call it differently
People think that it’s Python’s way of doing private methods
What it actually is is a way to prevent subclasses from overriding parent class methods when the method is critical
I see.
You use double underscore if overriding the method would break class functionality
In your estimation is geeksforgeeks.org a good resource?
Ehhhhh
haha
I go back and forth on this one
There are serious problems with it
The articles aren’t written well
Like "grain of salt" problems or "don't trust it at all" problems?
The code examples are almost never in idiomatic Python
One code example was printing its results rather than adding to a list
Which just blew my mind
So yeah - many issues
Well that's good to know. Thank you.
But - sometimes if you have a problem and GFG is there, you might be very happy it is
A lot of other resources might be very technical to understand
Where GFG just goes straight to the point with the problem you have - mostly
I guess there's also probably some xy problem where you're looking up how to do something in a way you think you should do it
that might be tangential
anyways, thanks again for looking at my code. I'm going to look at your solution to see how I could have done it differently.
oh shoot
Kinda a shame cos I wanted to see how you used it!
yeah I was going to go one way and did something else
I’ve never used that one. Never seem to have a reason to do so
that would've been for the iterative approach I was coming up with
Right
!d itertools.takewhile
itertools.takewhile(predicate, iterable)```
Make an iterator that returns elements from the iterable as long as the predicate is true. Roughly equivalent to:
```py
def takewhile(predicate, iterable):
# takewhile(lambda x: x<5, [1,4,6,4,1]) --> 1 4
for x in iterable:
if predicate(x):
yield x
else:
break
I don't even know if it would've worked the way I envisioned it.
I might've been optimizing prematurely in my head.
I’m not sure if I ever will use that one
itertools has some interesting methods
So there’s a bunch I like
Windowing and chunking are the big ones
Product is good if you want to collapse multiple for loops
Combinations is sometimes useful
Cycle and accumulate are good to know for a AoC context - one Day 1 problem was literally just combining those two together lol
!d itertools.zip_longest
itertools.zip_longest(*iterables, fillvalue=None)```
Make an iterator that aggregates elements from each of the iterables. If the iterables are of uneven length, missing values are filled-in with *fillvalue*. Iteration continues until the longest iterable is exhausted. Roughly equivalent to:
There’s one that’s good to know about in theory
that one is interesting
In practice, it’s going to be a rare day that you need to zip two things of different lengths
It just never happens
some of the methods I was reading about in the itertools documentation fit that bill I think
Yup
or at least I'm not smart enough to think of a way to use them effectively
!d itertools.starmap
itertools.starmap(function, iterable)```
Make an iterator that computes the function using arguments obtained from the iterable. Used instead of [`map()`](https://docs.python.org/3/library/functions.html#map) when argument parameters are already grouped in tuples from a single iterable (when the data has been “pre-zipped”).
The difference between [`map()`](https://docs.python.org/3/library/functions.html#map) and [`starmap()`](https://docs.python.org/3/library/itertools.html#itertools.starmap) parallels the distinction between `function(a,b)` and `function(*c)`. Roughly equivalent to:
```py
def starmap(function, iterable):
# starmap(pow, [(2,5), (3,2), (10,3)]) --> 32 9 1000
for args in iterable:
yield function(*args)
Stickie likes groupby but I’m not too interested in it
sure
Using map in general is odd for Python
Is it? I used to use map reduce and filter a lot in Perl
It’s similar to the recursion thing
Map and filter can just be replaced with comprehensions
And reduce
It’s very rare that you need to reduce. Many functions that would be nice for it already have a better way
Rather than reduce + add, use sum
sure
Rather than reduce + add, use math.prod
Rather than reduce + set operation, just use set operations because they take in multiple sets anyway
That’s the top three things I would want to use reduce for and there are already more idiomatic ways to approach them
Chain is also good to know as well but rarely will you need it
That’s about it really
math.chain?
Itertools chain
ok, I'll look it up
Use it if you need to concat a lot of stuff
It’ll prevent the formation of large temporary structures
That's good. That's what I like to do. I need to read more about how Python does that.
But yeah itertools is very important to know in general
The other one, IMO, is functools
functools.cache especially
If you love recursion you need to know that one :)
oh I'll check that out
I only looked at functools for wraps thusfar but I like fp
(if you haven't surmised yet)
I'll look into all the stuff we've discussed. Thank you very much for the insight.
I'm going to get lunch but I'm sure we'll talk again. Good luck at work tomorrow.
Thanks ☺️
my part 1:
import re
total_points = 0
def get_numbers(str):
numbers = re.findall(r'[0-9]+', str)
return numbers
with open("aoc4.txt", 'r') as file:
for line in file.readlines():
numbers = get_numbers(line)
matches = [n for n in numbers[1:11] if n in numbers[11:36]]
if len(matches) > 2:
total_points += 2**(len(matches) - 1)
else:
total_points += len(matches)
print(total_points)
part 1 and 2:
import re
total_points = 0
total_cards = 0
matches = []
bonus_cards = []
def get_numbers(st):
numbers = re.findall(r'[0-9]+', st)
return numbers
with open("aoc4.txt", 'r') as file:
for line in file.readlines():
numbers = get_numbers(line)
matches = [n for n in numbers[1:11] if n in numbers[11:36]]
bonus_cards.append([len(matches), 1])
if len(matches) > 2:
total_points += 2**(len(matches) - 1)
else:
total_points += len(matches)
for x, card_a in enumerate(bonus_cards):
total_cards += card_a[1]
for card_b in bonus_cards[x+1: x + card_a[0] + 1]:
card_b[1] += card_a[1]
print(total_points)
print(total_cards)
#!/usr/bin/python3
def read_input():
with open("input.txt") as f:
return read_cards(f.readlines())
def read_cards(lines):
cards = {}
for line in lines:
card_num, winning_numbers, numbers = read_card(line)
cards[card_num] = {"winning_numbers": winning_numbers, "numbers": numbers}
return cards
def read_card(line):
card_num, _, card_info = line.partition(":")
winning_numbers, _, numbers = card_info.partition("|")
card_num = int(card_num[5:])
winning_numbers = [int(num) for num in winning_numbers.split()]
numbers = [int(num) for num in numbers.split()]
return (card_num, winning_numbers, numbers)
def count_wins(card):
return sum([1 for num in card["numbers"] if num in card["winning_numbers"]])
def count_points(cards):
return int(sum([2 ** (count_wins(card) - 1) for card in cards.values()]))
def play_cards(deck):
owned_cards = [1 for _ in range(len(deck))]
for card_num in range(len(deck)):
cur_card = card_num + 1
if wins := count_wins(deck[cur_card]):
while wins:
cur_card += 1
owned_cards[cur_card - 1] += owned_cards[card_num]
wins -= 1
return sum(owned_cards)
if __name__ == "__main__":
cards = read_input()
print(count_points(cards))
print(play_cards(cards))```
Has day 4 been released?
This is 2023 channel. 2024 one has not been released
def xmas_counter(data: list[str]) -> int:
counter = 0
for line in range(len(data)):
for char in range(len(data[line])):
if data[line][char] == "X":
# check right
right = "X"
for i in range(1, 4):
if (char + i) >= len(data[line]):
break
right += data[line][char + i]
# check left
left = "X"
for i in range(1, 4):
if (char - i) <= 0:
break
left += data[line][char - i]
# check up
up = "X"
for i in range(1, 4):
if (line - i) <= 0:
break
up += data[line - i][char]
# check down
down = "X"
for i in range(1, 4):
if (line + i) >= len(data):
break
down += data[line + i][char]
# check diagonal (nord-ovest)
no = "X"
for i in range(1, 4):
if (line - i) <= 0 or (char - i) <= 0:
break
no += data[line - i][char - i]
# check diagonal (nord-est)
ne = "X"
for i in range(1, 4):
if (line - i) <= 0 or (char + i) >= len(data[line - i]):
break
ne += data[line - i][char + i]
# check diagonal (sud-ovest)
so = "X"
for i in range(1, 4):
if (line + i) >= len(data) or (char - i) <= 0:
break
so += data[line + i][char - i]
# check diagonal (sud-est)
se = "X"
for i in range(1, 4):
if (line + i) >= len(data) or (char + i) >= len(data[line + i]):
break
se += data[line + i][char + i]
if right == "XMAS":
counter += 1
if left == "XMAS":
counter += 1
if up == "XMAS":
counter += 1
if down == "XMAS":
counter += 1
if no == "XMAS":
counter += 1
if ne == "XMAS":
counter += 1
if so == "XMAS":
counter += 1
if se == "XMAS":
counter += 1
return counter
@keen locust
ik it's not so good, a lot of redundancy
if (line - i) <= 0:
i think should be just <
for all comparisons with 0, equality is ok
just can't be less than
that's gonna take -1, 0 is not less than 0 so it's gonna add the char in the -1 (the last line) too
what?
np
oh crap this is the wrong day. sorry about that
you too are in the wrong thread
I was guided here by an helper