#AoC 2022 | Day 3 | Solutions & Spoilers
1 messages · Page 2 of 1
pinned
lines = open("../inputs/day3.txt").read().splitlines()
lines = [lines[n:n+3] for n in range(0, len(lines), 3)]
indexes = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
print(sum(indexes.index(list(set(group[0]).intersection(set(group[1]).intersection(set(group[2]))))[0]) for group in lines))
not great but ¯_(ツ)_/¯
Crlf doesn't work for me
are you on windows and did you provide an input using stdin
Mac. Input through file
you shouldn't be using CRLF then
use the LF one
Tried that as well. P1 works. P2 doesn't falls 10 short of the actual answer
what about the xplatform one
does it give the same wrong answer
probably with CR line endings?
added a CR version
What does that mean? I mean , how do I do that?
either that's the error or some misprovided input
check the solutions and test the one labeled CR
oh
CRLF did this?
todays discovery was for first_elf, second_elf, third_elf in zip(*[iter(data)] * 3): magical
indeed ... thanks to stackoverflow I found that as well
last night you didn't want to enter the esoteric python territory, what is this today 😭
i did it in lua, I miss Sets 
i initially used the usual range but wasn't happy with it and found that gem
very neat
when i first saw the question i thought it was gonna be hard, turned out to be surprisingly ez
Yeah didn't seem easy at first, but now it looks quite simple. But still took me about 30 minutes the 2nd part
I thought part 2 was going to be difficult in particular. Was surprised it was almost exactly the same as part 1, just with slightly altered input
I can't wait for the next puzzles. Hope I get further than last year, before I lose motivation
d3a = sum(ord(e.lower())-ord('a')+1+26*e.isupper() for line in in3a.splitlines() for e in set(line[:len(line)//2])&set(line[len(line)//2:]))
in3b = in3a.splitlines()
d3b = sum(ord(e.lower())-ord('a')+1+26*e.isupper() for x,y,z in (in3b[i:i+3] for i in range(0,len(in3b),3)) for e in set(x)&set(y)&set(z))
One liners (for second task, I only did splitlines separately because it would look ugly and repeat it many times) on phone :3
im surprised i understood this 100% because its literally a one liner of my program lol
import string
scores = {letter: score for score, letter in enumerate(string.ascii_letters, 1)}
def split_strings(row):
lenstr = len(row) // 2
return row[:lenstr], row[lenstr:]
def intersections(*tups):
tups = iter(tups)
first = set(next(tups))
for tup in tups:
first &= set(tup)
return first.pop()
with open("puzzle.input") as f:
print(
"part1:",
sum(scores[intersections(*tup)] for tup in map(split_strings, f.readlines())),
)
with open("puzzle.input") as f:
lines = [line.strip() for line in f.readlines()]
zipped = zip(lines[::3], lines[1::3], lines[2::3])
print(
"part2:",
sum(scores[intersections(*tup)] for tup in zipped),
)
ahh ord() I knew there had to be a function that converted strings to ints. I just used a list.
woah
with open("inputs/input_3.txt", "r") as input_file:
inputs = [line.strip() for line in input_file.readlines()]
PRIORITIES = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
def part_one(input):
total_sum = 0
for i in inputs:
comp_one, comp_two = set(i[0 : len(i) // 2]), set(i[len(i) // 2 : len(i)])
intersection = comp_one.intersection(comp_two)
total_sum += PRIORITIES.index(list(intersection)[0]) + 1
print(total_sum)
def part_two(input):
total_sum = 0
elf_groups = [input[i : i + 3] for i in range(0, len(input), 3)]
set_elfs = [[set(k) for k in i] for i in elf_groups]
for i in set_elfs:
intersection = i[0].intersection(i[1], i[2])
total_sum += PRIORITIES.index(list(intersection)[0]) + 1
print(total_sum)
part_one(inputs)
part_two(inputs)
Any suggestions on how to clean part_two?
elf_groups = [input[i : i + 3] for i in range(0, len(input), 3)]
set_elfs = [[set(k) for k in i] for i in elf_groups]
Here I just divide into groups first and then turn each element of the groups into sets, but I feel like there is a better way haha
FYI you can use string.ascii_letters to get the string of all the letters
Yeah I just avoid imports in general for AOC
""" Day 3 Part 1 Solution """
f = open("puzzle_input.txt", "r")
puzzle_input = f.read()
priority_sum = 0
priority_value = lambda it: ord(it) - (96 if(ord(it) in range(ord('a'), ord('z')+1)) else 38)
for line in puzzle_input.split("\n"):
middle = int(len(line)/2)
first = line[:middle]
second = line[middle:]
for item in second:
if item in first:
priority_sum += priority_value(item)
break
print(priority_sum) # 7863
""" Day 3 Part 2 Solution """
import functools
f = open("puzzle_input.txt", "r")
puzzle_input = f.read()
puzzle_input_lines = puzzle_input.split("\n")
priority_sum = 0
team_size = 3
priority_value = lambda it: ord(it) - (96 if(ord(it) in range(ord('a'), ord('z')+1)) else 38)
for lineidx in range(0, len(puzzle_input_lines), team_size):
elf_team = [puzzle_input_lines[lineidx+i] for i in range(team_size)]
team_item = functools.reduce(lambda x,y: set(x) & set(y), elf_team)
priority_sum += priority_value(team_item.pop())
print(priority_sum) # 2488
Fair - I try to avoid importing anything thats not a builtin lib
🥴 meanwhile me:
import os
file_path = os.path.realpath(os.path.dirname(__file__)) + "/input.txt"
def get_priority(char):
return ord(char) - 64 + 26 if char.isupper() else ord(char) - 96
def part_1(lines):
def get_points(bag):
m = len(bag) // 2
return get_priority(*set(bag[:m]).intersection(set(bag[m:])))
ans = 0
for line in lines:
ans += get_points(line)
return ans
def part_2(lines):
def get_points(lines):
return get_priority(*set.intersection(*map(set, lines)))
ans = 0
for i in range(0, len(lines) - 2, 3):
ans += get_points(lines[i:i+3])
return ans
lines = open(file_path).read().strip().split('\n')
print(part_1(lines))
print(part_2(lines))
Just why... hahahaha
xD
they take 0 time and importing them if I need to will take a second or two
after importing all that he calculates the answer by hand. The only other line is:" print("The answer is: 12403")
i ended up not using numpy by default because it actually does take 0.3s to import
a&b&c for a,b,c in zip(*[iter(pzl)] * 3) is there a nice way to convert to sets during the zip as well?
That sounds about right
0.3 just to import really?
I feel like there is a nicer way of doing this but im pretty happy with what I have: ```py
import sys
import os
import string
Get Data
with open(os.path.dirname(sys.argv[0]) + "/input.txt", 'r') as f:
data = f.read()
data = data.split('\n')
Pt 1
priorities = []
for d in data:
splitpoint = int(len(d) / 2)
a = d[:splitpoint]
b = d[splitpoint:]
for i in a:
if i in b:
priorities.append(string.ascii_letters.index(i) + 1)
break
print(sum(priorities))
yup, you can check yourself via python -X importtime the_script.py 2>importtime_profile, and then showing that file with tuna
Huh, will do
Any ideas?
Here’s my over engineered today: https://github.com/Fiddle-N/advent-of-code-2022/blob/master/day_03/process.py
from pathlib import Path
data = Path("input.txt").read_text().strip().split("\n")
def priority(item: str) -> int:
return ord(item) - 96 + 58*(ord(item) < 97)
def part1():
total = 0
for racksack in data:
middle = len(racksack) // 2
common_letter = (set(racksack[:middle]) & set(racksack[middle:])).pop()
total += priority(common_letter)
print(total)
def part2():
total = 0
for i in range(0, len(data), 3):
common_letter = (set(data[i]) & set(data[i+1]) & set(data[i+2])).pop()
total += priority(common_letter)
print(total)
Is there a reason (other than avoiding imports) to use ord() rather than string.ascii_letters?
sum((a & b & c).pop() for a, b, c in zip(*[iter(map(set, pzl))]*3))```
alright I like this
If anything, I like how AoC brings the absolute garbage outta everyone's code
Been long since I last wrote nested listcomp, and fuck yea I'm gonna write more of that stuff
both ways, the quick and dirty + the unreadable 1 lines
And then use index? I just grabbed the first tool I had in mind, but that sounds less efficient
I used: string.ascii_letters.index(i)
You need to look through the string every time with index
The time difference is negligible probably but still
Ah right, while ord() + some maths is quicker?
that's my guess, didn't measure
ord(literal) should be converted to int on execution
There are people that make equations to solve it.
Looks a bit quicker - not much in it though.
for 10 million loops:
ord: 1074 ms
index: 1265 ms
yeah didn't expect it to be significant, the string is pretty small after all
how are u timing ur code?
!d timeit
Source code: Lib/timeit.py
This module provides a simple way to time small bits of Python code. It has both a Command-Line Interface as well as a callable one. It avoids a number of common traps for measuring execution times. See also Tim Peters’ introduction to the “Algorithms” chapter in the second edition of Python Cookbook, published by O’Reilly.
is it possible to use time.time for an entire python file and not a one line
I dont see why not?
You could also time it from the commandline. Use time on linux or get-history on powershell
im getting an error when i try subtract t1-t0
You risk timing things you dont want to doing that though
True, like interpreter startup
I use this decorator: ```py
def timeit(func):
@functools.wraps(func)
def new_func(*args, **kwargs):
start_time = time.time()
for i in range(100_000_000):
result = func(*args, **kwargs)
elapsed_time = time.time() - start_time
print('function [{}] finished in {} ms'.format(
func.name, int(elapsed_time * 1_000)))
return result
return new_func
Which i named timeit to make it super not confusing
Use time.perfcounter instead. Its got better resolution
Time.perfcounter_ns gives it in nanoseconds
time.time_ns() also gives nanoseconds idk what the difference is
#1
p = lambda x: ord(x) - 64 + 26 if x.isupper() else ord(x) - 96
g = lambda l: p(*set(l[:len(l) // 2]).intersection(set(l[len(l) // 2:])))
s = lambda k: sum(g(o) for o in k)
print(s(open("input.txt").read().split("\n")))
#2
p = lambda x: ord(x) - 64 + 26 if x.isupper() else ord(x) - 96
g = lambda l: p(*set.intersection(*map(set, l)))
s = lambda k: sum(g(k[i:i+3]) for i in range(0, len(k) - 2, 3))
print(s(open("input.txt").read().split("\n")))
It all depends on your cpu clock anway

Perf counter is made for checking how long things take. Time.time is meant for lower resolution timing
I switched it out for the perf_counter but its no different in actual performance
Probably because you're doing 100mn runs of the function. If you were running it once it would make more of a difference
Just doing it once can get you wildly varying results though
always best to average out over a longer run
what's wrong with timeit?
nothing functionally, just find the interface to it annoying
I find the decorator quicker and easier to understand
print("Part 1 : ", sum([ord(min(n))-96 if min(n).islower() else ord(min(n))-38 for n in [set(l[:len(l)//2]).intersection(l[len(l)//2:]) for l in [n.rstrip() for n in open("input.txt").readlines()]]]))
print("Part 2 : ", sum([ord(min(n))-96 if min(n).islower() else ord(min(n))-38 for n in [''.join(set([n.rstrip() for n in open("input.txt").readlines()][i]).intersection([n.rstrip() for n in open("input.txt").readlines()][i+1]).intersection([n.rstrip() for n in open("input.txt").readlines()][i+2])) for i in range(0,len([n.rstrip() for n in open("input.txt").readlines()]), 3)]]))
unreadable
these one liners doesn't really optimize your code
they optimize for unreadability
naive approach written in one line
they put so much time in it to write that one line tho 
Optimise for smallest screen space
screen space crisis
def part_1(data):
priority = 0
for line in data:
half = len(line) // 2
r = (set(line[:half]) & set(line[half:])).pop()
priority += ord(r) - 96 if ord(r) >= 97 else ord(r) - 38
return priority
def part_2(data):
priority = 0
for group in data:
r = (set(group[0]) & set(group[1]) & set(group[2])).pop()
priority += ord(r) - 96 if ord(r) >= 97 else ord(r) - 38
return priority
I use utility functions to load in the data, for part 2 I used a chunking loader, which basically just uses more-itertools.chunked to get the groups of 3.
oo i like how you played with ord utilizing ifs, i just went with .isupper() neat solution, pretty much how i did it as well
for part two i found this gem: for first_elf, second_elf, third_elf in zip(*[iter(data)] * 3)
I did consider checking for isupper but I quite liked sticking with ord() 😄
I have across this zip(*[iter(data)] * 3) before. I should write it down for times where more-itertools isn't available
ya i ended up using that in combination with ord
def part_one(data):
total = 0
for sack in data:
split_index = len(sack) // 2
first_half, second_half = sack[:split_index], sack[split_index:]
common = "".join(set(first_half) & (set(second_half)))
if common.isupper():
total += ord(common) - 38
else:
total += ord(common) - 97 + 1
return total
def part_two(data):
total = 0
for first_elf, second_elf, third_elf in zip(*[iter(data)] * 3):
common = "".join(set(first_elf) & set(second_elf) & set(third_elf))
if common.isupper():
total += ord(common) - 38
else:
total += ord(common) - 97 + 1
return total
i love how python makes these things a breeze, absolutely love it
I used string.ascii_letters.index(letter)+1 :P
wait what
!e
import string
print(string.ascii_letters.index("a")+1)
@zinc wing :white_check_mark: Your 3.11 eval job has completed with return code 0.
1
@chilly linden
I deliberately went with alphabet.lower() + alphabet.upper() as I think it's a bit more explicit. It doesn't require any special knowledge about string.ascii_letters to understand. E.g. the fact that lowercase letters come before uppercase (which is the kind of thing I would forget
).
I'll tell you what I told Tom
@karmic crow
Don't have the energy to write one-liners at the moment 😓
Cause you spent it all doing
crap
¯_(ツ)_/¯
🗿
I like it 😄 learnt some new things
alternative part2
sum(map(priority, map(set.pop, map(partial(reduce, and_), chunked(map(set, data), 3)))))
that's a elegant one liner 
Looks pretty clean. Try not to commit/push puzzle inputs though, the AoC creator would prefer that you refrain from doing so
Understood, thanks!
Oh, is it okay if I keep the repo private until the finals?
Or even then the inputs shouldn't be pushed to repo?
I wanna make it public later on
Probably best to do this. Just don’t push the input files in the first place
Why?
Okay, gotcha.
Hi
The creator is against it. Let me find the link
10 votes and 18 comments so far on Reddit
In that case, I should probably remove the inputs from my repo
it's best to just gitignore them
Hmm. Odd reasoning. And if they're bothered they should have this on the site, not some random Reddit post.
I think it’s more like they’d prefer you not to post them, rather than decreeing it as a must
Well it's sure fire way to make sure everyone doesn't know and commits them. I have. For years. As I was none the wiser.
A 1-liner for part one but in three lines.```py
def day_3():
f = open("2022/day_3.txt", "r").read().splitlines()
c = [
(((ord(p) - 96) if p.islower() else (ord(p) - 38)) if p.isalpha() else 0) for q in
[[v if v in i[len(i) // 2:] else "0" for v in i[:len(i) // 2]] for i in f]
for p in set(q)
]
print(sum(c)) # PART 1
Oh huh i thought people just pushed them because of a "fuck you" to the author. Honestly I feel like i remembered there being a big label about it on the website before, but i cant find anything.
So i may be completely out of my zone about this 😓
https://github.com/imsofi/advent-of-code/blob/main/2022/day_03.rb
Here is my ruby solution for day 3, challenging myself to try another language proper this time around.
I used to do it before completely accidentally. I think that’s the most common situation
Yeah, i just feel like i remember a login screen or similar of importance to be like "hey lets not upload the private inputs, so people cannot revere engineer the algorithmn that generates them, thanks and have fun!"
But its been like 3 years now, and i can find nothing of the sort 😓
oh dang, I need to update my gitignore then
def parse_group(sacks):
sack0 = set(sacks[0])
sack1 = set(sacks[1])
sack2 = set(sacks[2])
for item in sack0:
if item in sack1 and item in sack2:
print(item)
return item
def score_item(letter):
ord_val = ord(letter)
if ord_val>96:
return ord_val-96
return ord_val-64+26
if __name__ == '__main__':
total_score = 0
group_size = 3
group = []
with open('input.txt', 'r') as sacks:
for line in sacks:
sack = line.rstrip()
group.append(sack)
if len(group) < group_size:
continue
duped_item = parse_group(group)
total_score+= score_item(duped_item)
group = []
print(total_score)
hrmm, code highlighting not happening
Ah shoot. Yeah its no fun that it is so
But i find the reasoning good. I have seen seed crackers in minecraft, so i understand the worry.
put py after the three grave symbols
for python colors
def parse_group(sacks):
sack0 = set(sacks[0])
sack1 = set(sacks[1])
sack2 = set(sacks[2])
for item in sack0:
if item in sack1 and item in sack2:
print(item)
return item
def score_item(letter):
ord_val = ord(letter)
if ord_val>96:
return ord_val-96
return ord_val-64+26
if __name__ == '__main__':
total_score = 0
group_size = 3
group = []
with open('input.txt', 'r') as sacks:
for line in sacks:
sack = line.rstrip()
group.append(sack)
if len(group) < group_size:
continue
duped_item = parse_group(group)
total_score+= score_item(duped_item)
group = []
print(total_score)
gotcha
am I violating some group norms, I'm noticing a lot of people are trying for one liners?
no they are doing code golfs
trying to get the shortest solutions possible
in terms of number of characters used in the file
You can code however you want to
:nods: I'm familiar with the concept
Its not a group norm, so no worries
my background values clarity as well as brevity
OH wat
oops
I could probably tweak my parse_sacks to use a list comprehension though, i try for generality in the main body, but then it's kind of hard coded to groups of size 3 in the parser
I think there’s three main schools of thought for solutions :
- Get the solution done as quickly as possible and leave it at that
- Code golf it
- Overengineer it completely

Last night a huge group of ~9 ish people all collaborating made the solutions that are pinned
# LF (144)
*z,=open(0,'rb');u=sum;print(u((u({*a[(l:=len(a)//2):]}&{*a[:l]})-96)%58for a in z),u((u({*b}&{*c}&{*d})-106)%58for b,c,d in zip(*3*[iter(z)])))
# CR (144)
*z,=open(0,'rb');u=sum;print(u((u({*a[(l:=len(a)//2):]}&{*a[:l]})-96)%58for a in z),u((u({*b}&{*c}&{*d})-109)%58for b,c,d in zip(*3*[iter(z)])))
# CRLF (146)
*z,=open(0,'rb');u=sum;print(u((u({*a[(l:=len(a)//2-1):]}&{*a[:l]})-96)%58for a in z),u((u({*b}&{*c}&{*d})-119)%58for b,c,d in zip(*3*[iter(z)])))
# xplatform (156)
z=open(0,'rb').read().split();u=sum;print(u((u({*a[(l:=len(a)//2):]}&{*a[:l]})-96)%58for a in z),u((u({*b}&{*c}&{*d})-96)%58for b,c,d in zip(*3*[iter(z)])))
# xplatform one loop (161)
u=sum;print(u(u((u({*a[(l:=len(a)//2):]}&{*a[:l]})-96)%58for a in(b,c,d))+(u({*b}&{*c}&{*d})-96)%58*1j for b,c,d in zip(*3*[iter(open(0,'rb').read().split())])))
If i had fluid in my mouth it would have been all over my monitor right now
What's kind of funny is that it got so golfed that there were different versions for different platforms
because the newline style is different
lmao
CR only is quite funny. Isn’t that the old Mac OS before X that used to do that?
is there like a full form for CR and LF and CRLF?
honestly these solutions have some good principles for golfing:
,=operator:=operator*iterunpacking{*z}set conversionu=sumoverused function renamingopen(0)read from stdin- uses an overcomplicated
(x-96)%58for the value calculation (a=1, b=2, A=27) - uses
&for lots of stuff
,= operator
the WAHT
technically it's not an operator
We need this pinned bro
I am saving this in my personal Discord for safekeeping
ah, I see, a kind of unpacking
It's equivalent to saying
wait so *x, means all the elements except the last on in x?
yeah
*x, _ = list
_ = last element
*x = everything else
really nice
wait so to do x = list[:-2] we do *x, , = list? or is there like a different way?
OMD I hate the way Discord plays with embeds
,= basically means discard last elem
okay, I thought maybe there was a way to do like *x, 2*y = list or something
its basically like regex, ig? like if I think in terms of regex, I get it
after just finishing day 3, i now feel really stupid for not just doing python values = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' and index() + 1 to get the priority levels
found this video: https://www.youtube.com/watch?v=KeIMUw22SZ8
Do you know what x ,= y does in Python?
― mCoding with James Murphy (https://mcoding.io)
Source code: https://github.com/mCodingLLC/VideosSampleCode
SUPPORT ME ⭐
Patreon: https://patreon.com/mCoding
Paypal: https://www.paypal.com/donate/?hosted_button_id=VJY5SLZ8BJHEE
Other donations: https:...
unsure if it's good
but hopefully it explains it
i made this whole function thinking i was being so smart ```python
def prio(char):
if char.islower():
return ord(char) - 96
else:
return ord(char) - 38
you can do
import string
values = string.ascii_letters
I got halfway through typing out something like this before I remember string existed
Artemis, hsop, cereal, cefqrn, tesseract, jenna, salt-die, indie, and xelf
were everyone who helped
with the golfing
I think
shoutout to them
at the very least, im still happy I learned a little bit of new stuff today
with the ord() function
would the ord function be a bit faster?
like the if else?
no, the list would have lesser lines, it just getting the index
index() is linear, while ord is constant? admittedly small n, but principle of the thing
Actually we ended up doing a hacky solution and ignored ord by reading in "rb" (binary mode) to get the characters as binary numbers (what ord would return)
so we skipped the conversion entirely
O(0)
true ig, then best solution would be the (ord(x)-96)%58 that these guys found
yeah or just call as rb
is the else necessary in this case? return breaks out of the function so everything after it doesn't exist
I just did everything the hard way, I have no clue how I finished in about 8 minutes considering I was using the wrong index for the second half of the lines while splitting them for like the first 5 minutes..
I wonder when I'll be writing test cases for my helper functions...
helper functions?
Why the 58%?
for the negative values iirc
Yeah it's because it ends up being negative
yes? because if the character is not a lower case character I want to return something different
and then you do the %58 to get it correct
like my parse_group or score_item functions, when will they be doing complicated enough things I'll want to verify they work on simpler cases
the first return statement is not guaranteed to always trigger
oh wait
i think i know what u mean nvm
but if the if doesn't trigger, then what's after it will always run, so the else provides some semantic feedback, but it's not quite necessary
Oh, so with %58 you wouldn't have to do -38 on the lowercase ones?
yea i see what u mean now 🙂
I dunno, I just habitually try to early-return to avoid indentation as much as possible
Don't laugh at my solutions https://github.com/HeavenEvolved/Advent_Of_Code_2022
wait aah I didnt change it back to public while removing the data files
Uh, it doesn't return negative for me.
sec
what?
can you try now?
what do you mean?
!e ```py
print(ord("A")-96)
print((ord("A")-96)%58)
@naive oriole :white_check_mark: Your 3.11 eval job has completed with return code 0.
001 | -31
002 | 27
it returns negative cuz in ASCII uppercase comes before lowercase
so uppercase has lower value than lowercase
!e ```py
print(ord("a")-96)
print((ord("a")-96)%58)
@naive oriole :white_check_mark: Your 3.11 eval job has completed with return code 0.
001 | 1
002 | 1
@frail schooner :white_check_mark: Your 3.11 eval job has completed with return code 0.
97 65
okay I was right
Only thing I can say is why also check for upper? What are you expecting? 👀
!e ```py
for a in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ":
print(ord(a)-96,(ord(a)-96)%58)
@naive oriole :white_check_mark: Your 3.11 eval job has completed with return code 0.
001 | 1 1
002 | 2 2
003 | 3 3
004 | 4 4
005 | 5 5
006 | 6 6
007 | 7 7
008 | 8 8
009 | 9 9
010 | 10 10
011 | 11 11
... (truncated - too many lines)
Full output: https://paste.pythondiscord.com/befifafamu.txt?noredirect
ord("a")-96 and ord("A")-38 works for me, where should I use the %58?
I have no idea bro, my brain was doing donuts while doing the code
my brain just thinks there will be an issue so I did that
def day_3():
f = open("2022/day_3.txt", "r").read().splitlines()
c = [
(((ord(p) - 96) if p.islower() else (ord(p) - 38)) if p.isalpha() else 0) for q in
[[v if v in i[len(i) // 2:] else "0" for v in i[:len(i) // 2]] for i in f]
for p in set(q)
]
print(sum(c)) # PART 1
I'm also using ord-38
(ord(character)-96)%58 is the full code where character is any character from "a-zA-z"
see this output
and it will demonstrate what is happening
(ord(p) - 96) if p.islower() else (ord(p) - 38)) if p.isalpha()
youre using this so you can replace it with (ord(p)-96)%58
wait.. else 0?
This my part 1 solution ```py
print(sum([ord(x) - 96 if x.islower() else ord(x) - 38 for r in data for x in set(r[:int(len(r) / 2)]) if x in set(r[int(len(r) / 2): len(r)])]))
If I remove it, I get invalid syntax.
Doing 0 doesn't effect the sum.
One thing I was worndering is this is how I split the lines into groups of three, is there a way I could have done this in the forloop without having to have that second list comp?
sacks = input.splitlines()
for i in range(0, len(sacks), 3):
group = [set(s) for s in sacks[i:i+3]]
ord(x) - 96 if x.islower() else ord(x) - 38
# Is equivalent to:
(ord(x)-96)%58
That's quite cool ngl.
[data[i:i + 3] for i in range(0, len(data), 3)]
I guess, but then I have to loop over that comp
I was imagining that there was something like
for group in something(sacks):
There was some version of that
that we came up with when golfing
I don't know exactly what it was
you can try and find it
would for a, b, c in range(0, len(data), 3) work?
That's very cool.
or do you need the zip thing?
Who knew I'd learn something on day =< 3 :3
nope doesnt work
Honestly I should compile a list of code golfing tips for python
I would just have to find them all
one thing I've been trying to is to parse the inputs as streams rather than loading it all into memory at once, am I actually doing that?
I think they used open(0,'rb') to read directly from stdin
but like,
if __name__ == '__main__':
total_score = 0
group_size = 3
group = []
with open('input.txt', 'r') as sacks:
for line in sacks:
sack = line.rstrip()
group.append(sack)
if len(group) < group_size:
continue
duped_item = parse_group(group)
total_score+= score_item(duped_item)
group = []
print(total_score)
is line 5 doing that?
my mental model of how iterators work is lacking
line 5 is just opening the input.txt file in read mode
if you want to parse the input from stdin, then I think you use open(0,'r')
I'm still finding more code golf tips
wait @naive oriole with the stdin open() thing, do you just run the code and then paste the input data in the terminal?
the program is just exiting for me
Yeah you paste it I think
would it work in any terminal? for example vscode?
Not sure actually
Other people were doing more of the testing
I was mainly just looking for more techniques
My attempt to part 1:
sum(ord((c:=({*s[:(l:=len(s)//2)]}&{*s[l:]}).pop()).lower())-96+26*(c<'a')for s in I.strip().split())
101 character
aah only part 1
didnt read part 2 yet, wait a bit
The part 1 section of the code:
*z,=open(0,'rb');u=sum;print(u((u({*a[(l:=len(a)//2):]}&{*a[:l]})-96)%58for a in z))
not sure how many chars
85
no, it has no errors
good
as far as vscode is concerned
it took me almost an hour for part 2 (including me eating lunch) bc i didn't read it properly 😭
thought it said that we had to split it in half
not in groups of 3
rip
here's my solution for part 2 tho
@submit(2, 2022, 3, test=True, submit=False, parser=part_two_parser)
def part_two(daily_input):
"""Solution for AOC Day 3 Part 2."""
return sum([
calculate_priority(set(batch[0]).intersection(set(batch[1])).intersection(set(batch[2])).pop())
for batch in daily_input
])
oh that's formatted weirdly
; that's cheating
replace it with \n
anyway it is one char
replace the usage of the var
with the command
it would be shorter too
remove .strip -> 95 chars
I'm still thinking if my solution could be shorter ```py
print(sum([(ord(x)-96)%58 for r in data for x in set(r[:int(len(r) / 2)]) if x in set(r[int(len(r) / 2): len(r)])]))
you would have to also add [:-1] because of the ,= I think
so it would still be ~85 chars
but one line
# Part 1: 93 chars
sum(ord((c:=({*s[:(l:=len(s)//2)]}&{*s[l:]}).pop()).lower())-96+26*(c<'a')for s in I.split())
# Part 2: 97 chars
sum(ord((d:=({*a}&{*b}&{*c}).pop()).lower())-96+26*(d<'a')for a,b,c in zip(*[iter(I.split())]*3))
pandas keeps rocking
m = pd.Index([*pd._testing._random.string.ascii_letters]).to_series().pipe(lambda s: s.groupby(s, sort=0).ngroup().add(1))
# Part 1
print(pd.read_csv("day_3.input", header=None, squeeze=True).pipe(lambda s: s.apply(list).explode().groupby(pd.RangeIndex(len(s)*2).repeat(s.str.len().floordiv(2).repeat(2)))).agg("".join).groupby(lambda idx: idx // 2).agg(list).apply(pd.Series).agg(lambda r: next(iter({*r[0]}.intersection(r[1]))), 1).map(m).sum())
# Part 2
print(pd.read_csv("day_3.input", header=None, squeeze=True).groupby(lambda idx: idx // 3).agg(list).apply(pd.Series).assign(**{"3": lambda fr: fr.iloc[:, :2].agg(lambda r: {*r[0]} & {*r[1]}, 1)}).rename(columns=int).agg(lambda r: next(iter({*r[2]} & r[3])), 1).map(m).sum())
Indeed they do.
who needs import string when i can do pd._testing._random.string
lol
Hey @naive oriole!
You either uploaded a .txt file or entered a message that was too long. Please use our paste bin instead.
@frail schooner
https://paste.pythondiscord.com/xocamutoce
I will save this for later reading! Thank you so much!
Yeah I'm probably missing some tips, and just keep in mind that sometimes these will not be shorter (there are always exceptions)
it's not much of a difference but you can remove the len(r) in the list splicing in the secod set, so it would be:
this:
print(sum([(ord(x)-96)%58 for r in data for x in set(r[:int(len(r) / 2)]) if x in set(r[int(len(r) / 2):])]))
instead of this:
print(sum([(ord(x)-96)%58 for r in data for x in set(r[:int(len(r) / 2)]) if x in set(r[int(len(r) / 2): len(r)])]))
Replace int(len(r) / 2) with len(r)//2, it does the same thing
true
so this:
print(sum([(ord(x)-96)%58 for r in data for x in set(r[:len(r) // 2]) if x in set(r[len(r) // 2:])]))
yup thought about that
because you then don't need to calculate len(r) // 2 twice
exactly
is there a way to do something like print(x:= a for a in [0,1,2])?
like reduce for loop and printing the elements one by one into one line?
print(*(0,1,2))
or better yet
print(*"012")
equivalent to doing
print("0", "1", "2")
oh shit
sep='\n'?
print(*data,sep='\n') I just did this?
that would prob work
aah okay
unless '\n'.join(data) is shorter but it looks longer
okay so again this * is just taken as a value in data without even having to iterate over it?
hmm..
Unpacking
this would give error if the values were not str
the real best answer would be if you shorten your variable name
n='\n'
p=print
p(*d,sep=n)
p(n.join(d))
just prints the contents 5 times
how would I do like each entry multiplied by 5?
oh ok
hm
it might have to just be that comprehension
and we can't use 5for because it could be 5f or or 5 for
although it might work in previous versions
My solution seems to skip a line if there are two consecutive matches 😭
two consecutive matches?
So my list is 299 when there's 300 characters
For for part A
For if there's a match of B for one line and another match of B on the second line, it skips it
did you check the number of lines in your data file?
and the sum list has 299 entries?
Yep
just take the intersection of the two halves and use that?
can you send whatever conditional statements youre using?
Sure
from string import ascii_lowercase, ascii_uppercase
file = open('day_3.txt', encoding="utf-8")
last_i = None
counter = 0
stuff = []
for line in file.readlines():
line = line.strip('\n')
reference = int(len(n)/2)
part_a = line[:reference]
part_b = b[reference:]
print([art_a, part_b)
for i in part_a:
for it in part_b:
if i == it and last_i != i:
stuff.append(i)
last_i = i
table = [None]
for digit in ascii_lowercase:
table.append(digit)
for digit1 in ascii_uppercase:
table.append(digit1)
print(stuff)
for item in stuff:
counter += table.index(item)
if table.index(item) == -1:
raise TypeError
print(counter)
file.close()
I should have probably used with but oh well (for file handling)
Doesn't really matter if your program stops in 20ms anyways
I'm approaching it as a practice situation, practice makes permanent
Splits it in half
it's the middle index, odd choice of variable name
odd choice yeah
that's copied weirdly
Could someone maybe explain // rq?
yes it should be
Floor division
Divide x by y and round down to next whole number
as opposed to running the result through int?
// is floor division, it rounds down the result of / to the lowest integer value possible with rounding
so 9.6 result would give 9
instead of 10
probably a bit more performant since it's staying in the same type the whole time
doesn't it come from bit shifting?
it does
@oak estuary is it just me or is that another issue?
let me send an updated one, the variable names were a little confusing if I was going to share it
from string import ascii_lowercase, ascii_uppercase
file = open('day_3.txt', encoding="utf-8")
last_i = None
counter = 0
stuff = []
for line in file.readlines():
line = line.strip('\n')
reference = len(line)//2
part_a = line[:reference]
part_b = line[reference:]
print(part_a, part_b)
for i in part_a:
for it in part_b:
if i == it and last_i != i:
stuff.append(i)
last_i = i
table = [None]
for digit in ascii_lowercase:
table.append(digit)
for digit1 in ascii_uppercase:
table.append(digit1)
print(stuff)
for item in stuff:
counter += table.index(item)
if table.index(item) == -1:
raise TypeError
print(counter)
file.close()
there
also dont use this
Is there also a ceil operator then?
use ```py
part_a&part_b
I think there is a ceil function somewhere? not sure might be in the math or numpy libraries
!d math.ceil
math.ceil(x)```
Return the ceiling of *x*, the smallest integer greater than or equal to *x*. If *x* is not a float, delegates to [`x.__ceil__`](https://docs.python.org/3/reference/datamodel.html#object.__ceil__ "object.__ceil__"), which should return an [`Integral`](https://docs.python.org/3/library/numbers.html#numbers.Integral "numbers.Integral") value.
math.ceil but dont use that
You'll get floating point errors for some numbers
Use -(a // -b)
dont use this
set(part_a) & set(part_b)
!e
a = 21
b = 8
print(-(a // -b))
to get the intersection
@eternal cipher :white_check_mark: Your 3.11 eval job has completed with return code 0.
3
what does the & operator do?
.
!e print(set([1, 2, 3]&set([2,3,4])))
@oak estuary :x: Your 3.11 eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 1, in <module>
003 | TypeError: unsupported operand type(s) for &: 'list' and 'set'
hmm
aah
oh
roller coaster
!e print(set([1, 2, 3])&set([2,3,4]))
@frail schooner :white_check_mark: Your 3.11 eval job has completed with return code 0.
{2, 3}
xD
it gives the common elements in both the sets
there's unions, intersections, etc.
ok, why not use &? or the .intersection() method?
I suppose you can't do an early escape because we know from context that there'll only be one item
with .intersection() you would have to write lots of .intersections if you wanted to do more than two at a time
so there's some performance left on the table
but with set(a)&set(b)&set(c)
its a single line to get the intersection between all the sets you want
Hey @full scarab!
It looks like you tried to attach a Python file - please use a code-pasting service such as https://paste.pythondiscord.com
you said not to use & but that reads like an argument in it's favor to me
no I said use &
I said dont use this cuz I didnt type cast it to set
set takes unique values and then you can do the intersection
part 1:
# priority = letters.index(LETTER) + 1
letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
total_priority = 0
with open("input.txt", "r", encoding="UTF-8") as f:
for line in f:
line = line.replace("\n", "")
half_len = int(len(line) / 2)
compartment_1, compartment_2 = set(line[:half_len]), set(line[half_len:])
total_priority += letters.index(list(compartment_1.intersection(compartment_2))[0]) + 1
print(total_priority)```
part 2:
```py
# priority = letters.index(LETTER) + 1
letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
total_priority = 0
buffer = []
with open("input.txt", "r", encoding="UTF-8") as f:
for count, line in enumerate(f):
line = line.replace("\n", "")
count += 1
buffer.append(line)
if count % 3 == 0:
total_priority += letters.index(list(set(buffer[0]) & set(buffer[1]) & set(buffer[2]))[0]) + 1
buffer = []
print(total_priority)
anything that could be shortened/simplified?
just used to it, pylint complains otherwise (i use vscode)
no worries
doesnt for me?
int(len(line) / 2) -> len(line) // 2
I personally would put list(compartment_1.intersection(compartment_2))[0] on it's own line just for readability
intersection can be done with &
is pylint activated? lol
yep
yeah i was using that before but decided to use &
also you can ignore pylint errors per line if you want
is there a difference or preference between & or .intersection apart from readability?
yeah had to use that for part 2
well it's probably good practice to specify encoding + i'm used to specifying it anyway 🤷♂️
Also assigning to two variables two unrelated values is pretty meh
I would assign to two variables in the same line only if it was something like unpacking
@full scarab use to ignore the error on the line
i thought i was going crazy, len(line) / 2 returns a float so i just cast it to int, i checked if all of the lines were even to determine to use // or not
My message just sent like 20 mins late
xD
Man I really should go to sleep.. its 1:20AM..
but I really wanna sit here and try new things xD
i'm aware, but if pep says it's better to specify encoding then i may aswell do it 🤷♂️
i'm doing this during the day anyway, if i was sweaty i'd probably not specify it
but sadly it releases at 4 am for me so can't compete
!e
a = [1,2]
print(len(a) / 2)
@full scarab :white_check_mark: Your 3.11 eval job has completed with return code 0.
1.0
like what
I found out what happened, it's skipping a "B"
and why is it doing that?
it's more about clarity than shortness. Also as we are indexing the list the extra chars would ruin the indexes.
I think its how I coded it
ah okay
the B matches are consecutive
but obviously my logic was to ensure that each line has a unique match - I obviously coded it wrong the first time
I think you made everything a little more complicated than was needed xD
since you already have the uppercase and lowercase lists
Hoepfully part 2 is more straightforward
you can also pop from the set, you don't need to convert it to a list
you could've just used the indexes to get the values
oh nice, i did not think of that
on a set?
not you, was talking to mesub
I will ping you when I reply to you xD
wrong emoji..
line.strip() is a bit more elegant than line.replace("\n", ""), although for aoc I just read the whole input and then split by \n
i also did from string import ascii_lowercase, ascii_uppercase but realized the string is literally hardcoded into the module so i copy pasted it into my open program
isnt there like a string callable for the whole list of alphabets?
thats seems like a missed opportunity
I think I saw someone import something from string library in the morning.. dont remember it now
in part two you do count += 1, but you can do enumerate(f, start=1)
or ascii_letters
@real musk :white_check_mark: Your 3.11 eval job has completed with return code 0.
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
wait why didnt you just import that instead of those two separate ones xD
.
ah didn't know you could do that, thanks
you also could use ord
well i don't think start= is required
wait
since start is the 2nd arg
With all the new knowledge this is my final result for part 1
print(sum(((l:=len(r)//2,ord(*(set(r[:l])&set(r[l:])))-96)[1]%58for r in data)))
chr*
what
talking about generating the alphabet
for what?
in general though I would just iterate over indices with a step of 3, or user more_itertools.chunked. I think that's better than appending to a list and then checking if it's modulo 3
generating the alphabet (but you have to use chr)
i'm not too familiar with itertools, i'll check it out
!e print(sum(((l:=len(r)//2,ord(*(set(r[:l])&set(r[l:])))-96)[1]%58for r in [[1,2,3,4,5,6,7,8]])))
@frail schooner :x: Your 3.11 eval job has completed with return code 1.
001 | <string>:1: SyntaxWarning: invalid decimal literal
002 | Traceback (most recent call last):
003 | File "<string>", line 1, in <module>
004 | File "<string>", line 1, in <genexpr>
005 | TypeError: ord() takes exactly one argument (0 given)

print(sum(((l:=len(r)//2,ord(*(set(r[:l])&set(r[l:])))-96)[1]%58for r in [[1,2,3,4,5,6,7,8]])))
!e print(sum(((l:=len(r)//2,ord(*(set(r[:l])&set(r[l:])))-96)[1]%58for r in [[1,2,3,4,5,6,7,8]])))
@frail schooner :x: Your 3.11 eval job has completed with return code 1.
001 | <string>:1: SyntaxWarning: invalid decimal literal
002 | Traceback (most recent call last):
003 | File "<string>", line 1, in <module>
004 | File "<string>", line 1, in <genexpr>
005 | TypeError: ord() takes exactly one argument (0 given)
I am just copying your code
That isn't a valid input tough
i was trying to simplify ```py
set(buffer[0]) & set(buffer[1]) & set(buffer[2])
e.g can't do the first two then do the last one (or am i wrong?)
oh right
!e print(sum(((l:=len(r)//2,ord(*(set(r[:l])&set(r[l:])))-96)[1]%58for r in [['xAbchasjhHgsdG']])))
@frail schooner :x: Your 3.11 eval job has completed with return code 1.
001 | <string>:1: SyntaxWarning: invalid decimal literal
002 | Traceback (most recent call last):
003 | File "<string>", line 1, in <module>
004 | File "<string>", line 1, in <genexpr>
005 | TypeError: ord() takes exactly one argument (0 given)
!e print(sum(((l:=len(r)//2,ord(*(set(r[:l])&set(r[l:])))-96)[1]%58for r in ['xAbYHu'])))
@frail schooner :x: Your 3.10 eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 1, in <module>
003 | File "<string>", line 1, in <genexpr>
004 | TypeError: ord() takes exactly one argument (2 given)
check pinned for best golf till now. current 144 chars
!e print(sum(((l:=len(r)//2,ord(*(set(r[:l])&set(r[l:])))-96)[1]%58for r in ['xAbYHu'])))
@frail schooner :x: Your 3.11 eval job has completed with return code 1.
001 | <string>:1: SyntaxWarning: invalid decimal literal
002 | Traceback (most recent call last):
003 | File "<string>", line 1, in <module>
004 | File "<string>", line 1, in <genexpr>
005 | TypeError: ord() takes exactly one argument (0 given)
!e print(sum(((l:=len(r)//2,ord(*(set(r[:l])&set(r[l:])))-96)[1]%58for r in ['xAbYAu'])))
@frail schooner :white_check_mark: Your 3.11 eval job has completed with return code 0.
001 | <string>:1: SyntaxWarning: invalid decimal literal
002 | 27
okay finally
okay I found the actual solution to what I had 😭
basically for every new line, I had to stop comparing to the last line
which fixed it
!!!!
xD
part 2 :3 ```py
print(sum([(ord(*(set(x[0])&set(x[1])&set(x[2])))-96)%58 for x in [data[i:i+3] for i in range(0, len(data), 3)]]))
check pinned for best golf till now. current 144 chars
I saw but I don't like that "short" one. It's good but using ; is kinda cheating imo
you can shave off some whitespace there
print(sum([(ord(*(set(x[0])&set(x[1])&set(x[2])))-96)%58for x in[data[i:i+3]for i in range(0,len(data),3)]]))
what do the different codes mean in this? (lf, cr...)
that's the newline sequence
windows uses carriage return (cr) line feed (lf), unix uses line feed, old macos uses carriage return
Actually I updated it to ```py
print(sum([(ord(*(set(x)&set(y)&set(z)))-96)%58 for x,y,z in zip(3[iter(data)])]))
the space before the for is unneeded
lmao yeah but my ide is on drugs if I do that.
And... I like it like this. I'm gonna stop now... ||maybe if I kept going it will be 10 characters long.||
does that get the right answer?
ah the iter pops 1 each time? so the zip does work?
Yep
what does your data = line look like? You could move the ord part there.
sets are cool py def day3(inp: str) -> tuple[int, int]: sco = 0 slc = inp.strip().split("\n") for i in slc: num = int(len(i) / 2) a, b = i[:num], i[num:] ins = list(set(a) & set(b))[0] sco += list(" abcdefghijklmnopqrstuvwxyz" + "abcdefghijklmnopqrstuvwxyz".upper()).index(ins) gsco = 0 for i, j, k in zip(slc[::3], slc[1::3], slc[2::3]): gsco += list(" abcdefghijklmnopqrstuvwxyz" + "abcdefghijklmnopqrstuvwxyz".upper()).index(list(set(i) & set(j) & set(k))[0]) return (sco, gsco)
add a .splitlines() to the text test data
f.read().split("\n")
!e ```py
data = """dWlhclDHdFvDCCDfFq
mGdZBZBwRGjZMFgvTvgtvv
jwwJrzdzGdSbGGnNlzWczHzPHPhn
cczcbMBszhzzDBTBPPPGjtvtlt
LqJLfpwdLnvQLRGQjGtj
gSgnSJJCGSGpGSrwgfhchmmmHzcrHDmbrmMm""".split("\n")
print(sum(((l:=len(r)//2,ord(*(set(r[:l])&set(r[l:])))-96)[1]%58 for r in data)))
@dark fjord :white_check_mark: Your 3.11 eval job has completed with return code 0.
192
Doesn't matter since it won't have any matches.
but for the actual data...
There aren't any trailing whitespaces so it doesn't matter.
#part 1
def dupe(sttr):
'''finds dupe characters in the string but needs to make sure
:param sttr: string
:return string: character that is in twice, but only once in each half of the string
'''
for char in sttr:
if sttr[len(sttr) // 2:].count(char) >= 1 and sttr[:len(sttr) // 2].count(char) >= 1:
return char
def summer(lll):
'''sums up the priority score for each duplicate character
:param lll: list of string
:return int: sum of priority scores
'''
import string as str
PRIORITIES = dict(zip(str.ascii_lowercase, range(1,28)))
UPPERCASE = dict(zip(str.ascii_uppercase, range(27,53)))
PRIORITIES.update(UPPERCASE)
sum = 0
for line in lll:
sum += PRIORITIES[dupe(line)]
return sum
with open('advent3.txt', 'r') as file:
text = file.read().splitlines()
print(summer(text))
#part2
def dupe2(sttr1, sttr2, sttr3):
''' finds the character in three given strings
:param sttr1: string
:param sttr2: string
:param sttr3: string
:return string: character that is in twice, but only once in each half of the string
'''
for char in sttr1:
if char in sttr2 and char in sttr3:
return char
def summer2(lll):
'''sums up the priority score for each duplicate character in the three string group
:param lll: list of list of string
:return int: sum of priority scores
'''
import string as str
PRIORITIES = dict(zip(str.ascii_lowercase, range(1,28)))
UPPERCASE = dict(zip(str.ascii_uppercase, range(27,53)))
PRIORITIES.update(UPPERCASE)
sum = 0
for list in lll:
sum += PRIORITIES[dupe2(list[0], list[1], list[2])]
return sum
with open('advent3.txt', 'r') as file:
text = file.read().splitlines()
lolos = [[text[index], text[index + 1], text[index + 2]] for index in range(0, len(text), 3)]
print(summer2(lolos))
I didn't even know about sets. 🤡 🤡 🤡 🤡
Not in my input.
Then you're going to find & hilarious.
is that the same thing as set?
No it shows if both sets have the same values.
bloody hell
!e ```py
print({1, 2, 3} & {3, 4, 5})
@dark fjord :white_check_mark: Your 3.11 eval job has completed with return code 0.
{3}
Thank you
set intersection is great today
i'm very proud of how concise my solution was because of it
What did you come up with?
# part 1
s = 0
for r in data.splitlines():
a, b = r[:int(len(r)/2)], r[int(len(r)/2):]
c = list(set(a) & set(b))[0]
s += string.ascii_letters.index(c) + 1
print(s)
# part 2
s = 0
for g in chunked(data.splitlines(), 3):
c = list(set(g[0]) & set(g[1]) & set(g[2]))[0]
s += string.ascii_letters.index(c) + 1
print(s)
that was not particularly clean though
i was trying to get a solution as fast as possible
the int(len(r)/2) should really have been floor div
What's chunked?
!d more_itertools.chunked
more_itertools.chunked(iterable, n, strict=False)```
Break *iterable* into lists of length *n*:
```py
>>> list(chunked([1, 2, 3, 4, 5, 6], 3))
[[1, 2, 3], [4, 5, 6]]
``` By the default, the last yielded list will have fewer than *n* elements if the length of *iterable* is not divisible by *n*:
```py
>>> list(chunked([1, 2, 3, 4, 5, 6, 7, 8], 3))
[[1, 2, 3], [4, 5, 6], [7, 8]]
``` To use a fill-in value instead, see the [`grouper()`](https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.grouper "more_itertools.grouper") recipe...
it's coming as itertools.batched in 3.12
you can also find something equivalent named grouper in https://docs.python.org/3/library/itertools.html#itertools-recipes
curious why they decided on batched as a name
I thought they use grouper
so maybe they just mostly copied that
oh they have both
def batched(iterable, n):
"Batch data into lists of length n. The last batch may be shorter."
# batched('ABCDEFG', 3) --> ABC DEF G
if n < 1:
raise ValueError('n must be at least one')
it = iter(iterable)
while (batch := list(islice(it, n))):
yield batch
```is in there
I also have issues with pairwise
Rust Structs for the win
because from math that feels like it should be about all pairs
not adjacent pairs
rust structs aren't that special
rust's enums are fun though
just go line by line with a counter, adding each line to a list, when the counter reaches 3 go to the next list index
A dynamically-sized view into a contiguous sequence, [T]. Contiguous here means that elements are laid out so that every element is the same distance from its neighbors.
neato
nothing like the trick with zip
sadly array_chunks https://github.com/rust-lang/rust/issues/74985 isn't in stable
eh who doesn't use nightly anyway /hj
if you're writing a binary, nightly is fine
it's not like it breaks all that often
I usually do
this time I ended up using tuples from itertools
I'm assuming I could drop the 53 from the type side
or maybe something better still
oh _ for array lengths is not in stable
sad
ok, I'm dumb, I can do
let mut counts = [0u8; 53];
day three 🎉
here was my solution ```py
priorities = dict(zip(string.ascii_letters, range(1, 53)))
def part_1(data: list[str]) -> int:
halves = [
next(iter(set(i[: int(len(i) / 2)]).intersection(set(i[int(len(i) / 2) :]))))
for i in data
]
priorities_sum = sum(priorities[s] for s in halves)
return priorities_sum
def part_2(data: list[str]) -> int:
chunks = chunked(data, 3)
badges = [
next(iter(set(a).intersection(set(b)).intersection(set(c))))
for a, b, c in chunks
]
priorities_sum = sum(priorities[s] for s in badges)
return priorities_sum
sets are nice
oh, you can do intersection with &, can't you
mhm
ah, my solution would've been more concise then
yeah that's better ```py
priorities = dict(zip(string.ascii_letters, range(1, 53)))
def part_1(data: list[str]) -> int:
halves = [
next(iter(set(i[: int(len(i) / 2)]) & set(i[int(len(i) / 2) :]))) for i in data
]
priorities_sum = sum(priorities[s] for s in halves)
return priorities_sum
def part_2(data: list[str]) -> int:
chunks = chunked(data, 3)
badges = [next(iter(set(a) & set(b) & set(c))) for a, b, c in chunks]
priorities_sum = sum(priorities[s] for s in badges)
return priorities_sum
still blazing fast
||never gonna give you up||
with open("the.txt","r") as f:
inpt = f.read().split("\n")
letters = "abcdefghijklmnopqrstuvwxyz"
letters += letters.upper()
x = []
total = 0
dic = {}
for i in range(1,53):
x.append(i)
the_list = list(zip(letters,x))
for i in the_list:
dic[i[0]] = i[1]
print(dic)
print(dic)
for i in inpt:
number_of_elements = len(i)
half_indx = (number_of_elements/2) -1
first_half = i[0:int(half_indx)]
second_half = i[int(half_indx):]
common_element = set(first_half).intersection(second_half)
for n in common_element:
total += dic[n]
print(total)```
It says too low
Why?
You're taking away 1 when you divide, why?
I mean the task is super tiny, basically any solution will be fast, mightnas well write something sebsible
https://github.com/algmyr/aoc-2022/blob/master/src/solutions/day03.rs
time to do it in rust
used no outside info, the only thing I looked at was the aoc prompt
the time taken is...insignificant
I wonder what the first day with some meat to it will be 
none
ah
i hand rolled my benchmark thing also. your percentage progress bar thing looks nice
I have my own code for timing runs
and yeah, the display code is probably where the biggest chunk of work went 😛
i wrote a macro to generate a match case for all the days, which was fun
what does it do?
I wrote a macro too, but nothing clever
Index star from 0
While the len starts at 1
so in rust you can't dynamically use things, but i wanted to do that. i wrote a proc macro that generated a function with a match case that went
match day {
0 => {
// load day 0
}
...
}
``` basically
Oh wait
mind just does the tiniest bit more
macro_rules! days {
($($day:literal)*) => {
paste! {
$(mod [<day $day>];)*
pub fn parts(day: u8) -> (fn(&str) -> String, fn(&str) -> String) {
match day {
$($day => ([<day $day>]::part_one, [<day $day>]::part_two),)*
_ => panic!("invalid day"),
}
}
}
};
}
days! {
01
}
the machinery is very very yucky
man I'm just manually cargo running all my solutions
i might make it into a trait
I was thinking of writing some small helper to automatically fetch and submit stuff, though
your solutions are forced into returning strings?
right now. i'll planning to change that later.
whenever i stop being lazy
will probably do some trait nonsense or something to make it cleaner
currently my thing just runs the functions. i want to return it also, but then i'd either have to make the functions all return strings which is sad or something else
I have a run result struct that holds Box<dyn Display> for the answers
dumb but works
I wouldn't use it for anything performance critical
i mean Box isn't actually horrible
but i hate that it's magic
since it has the aliasing rules of &mut T or something like that
it's a very good type for what's actually needed, just something that I can display
though I guess just wrapping stuff in format! works as well
and just deal with strings
but you can at least move that out of solutions
(don't actually do this, but it's fun to when you can)
it still feels like python should have an easier way of splitting a string in 1/2 the three methods I've seen so far all feel clunky, and 2 of them involve downloading other libraries.
I just sliced by dividing the len in half
and they're all even, so it'll work fine
yeah, I would say this is quite nice already
n = len(s)
s[:n//2]
s[n//2:]
but in rust 👀 let (l, r) = s.split_at(s.len() / 2); 👀
more-itertools has split_at too >.>
more itertools has divide
that was one of the 2
numpy reshape was the other.
map(set,np.fromiter(map((' '+items).index,d),int).reshape(2,-1))
eww
yeah
import numpy as np
from string import ascii_letters as items
data = [[*map((' '+items).index,line.strip())] for line in open(filename)]
λ=lambda d:set.intersection(*map(set,np.array(d).reshape(2,-1))).pop()
print(sum(map(λ,data)))
oh, popping is probably a nicer way to get the first item
I did next(iter(...)) >.>
yeah .pop() seems simple.
!e
s = 'abcdefgh'
a, b = zip(*zip(*len(s)//2*[iter(s)]))
print(a, b)
@stray spear :x: Your 3.11 eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 2, in <module>
003 | ValueError: too many values to unpack (expected 2)
err, maybe I should have test run this
oh, one zip too many
!e
s = 'abcdefgh'
a, b = zip(*len(s)//2*[iter(s)])
print(a, b)
@stray spear :white_check_mark: Your 3.11 eval job has completed with return code 0.
('a', 'b', 'c', 'd') ('e', 'f', 'g', 'h')
it's terrible, but it works
!e ```py
s = 'abcdefgh'
print(*zip(4[iter(s)]))
*zip
@dark fjord :white_check_mark: Your 3.10 eval job has completed with return code 0.
('a', 'b', 'c', 'd') ('e', 'f', 'g', 'h')
oh hey, this works ```py
priorities_sum = sum(
[priorities[(set(a) & set(b)).pop()] for a, b in [divide(2, i) for i in data]]
)
l=len(s:="abcdef")//2
print(*zip(*l*[iter(s)]))
that's part 1 one-lined, pretty much
yeah that's a lot cleaner to read too.
except I hate seeing i be a non-int. 🙂
sure
map
remove the []
I was doing some fancy str.maketrans stuff before I realized I could just skip it and do ```py
priorities = dict(zip(string.ascii_letters, range(1, 53)))
ah right, sum will take the generator
the chad (string.ascii_lowercase + string.ascii_uppercase).index
I dunno, left, right = string[mid:], string[:mid] is pretty legible
ok, I got those backwards, but still
it would've been if I calculated mid on its own line >.>
I did
fn priority(n: u8) -> u8 {
if n & 32 == 0 {
(n & 31) + 26
} else {
n & 31
}
}
because knowing the logic of ascii is neat
halves = partial(divide,2)
for a,b in map(halves,data)
a map would be more concise here, yeah
fn priority(n: u8) -> u8 {
n & 31 + 26 * !(n & 32)
}
not that it matters for this challenge
can you do ! for not?
yeah
n & 31 + 26 * !(n & 32)
err 1 - (n&32) is for sure wrong
yeah, at a min it needs the ==0 in there
priorities_sum = sum(
priorities[(set(a) & set(b) & set(c)).pop()] for a, b, c in chunked(data, 3)
)
``` part 2 one-lined too
it is
chunked hmmm
more-itertools!
fn is a giveaway
chunked is good
actually, I think salt's aoc_lube has a chunked function too, but whatever
yeah, it's just chunk though
yeah, I'm sure there are other languages that use it too.
i don't live in the past
lolol
I don't know of any off hand
actually found that often the easiest way to parse input is with a combination of extract_ints and chunk
I just did index + 1 or added a space at the start and then indexed.
map((' '+items).index,line.strip())
like, 2018 day 3 input looked like:
#1 @ 596,731: 11x27
#2 @ 20,473: 23x22
#3 @ 730,802: 23x23
and you can just
chunk(extract_ints(raw_input), 5)
gleam uses fn, but its a lesser known thing
mm, yeah that's more concise
elixir uses fn
lol, if there is any critique I can give to AoC, many of the input formats kinda suck
i think it uses fn for lambdas? i'd have to check.
there's a library for reverse format strings that can help parse if you have issues
yeah it uses fn for anonymous functions
if I really need it I'll probably just bash things with regex or do something hacky
omg i should've used binary and
i did. intersection
i really wanna do aoc in kotlin but there aren't many good helpers 😭
nim uses proc
nim seems cool
nim does seem cool, and iirc can use python libraries and has an option to compile to python code or something like that?
I just don't have time at the moment for yet another language. 🙂
yes, i use aoc_lube to fetch input in nim
import nimpy
proc fetch*(year, day: int): string =
## Get AoC input.
"aoc_lube".pyImport.callMethod(string, "fetch", year, day)
that star after the proc name means it's public function
it just feels like nim should be a natural progression from python, there's a lot of coupling there. I hope it does well in the future.
I wanted to do AOC in haskell, but I forgot to properly learn it 😔
my day 3 solution:
include aoc
type Sacks = seq[string] | seq[seq[char]]
let sacks = fetch(2022, 3).splitLines()
proc priority(c: char): int =
1 + c.ord - (if c >= 'a': 'a'.ord else: 'A'.ord - 26)
proc sumPriorities(groups: seq[Sacks]): int =
sum groups.mapIt(
it.mapIt(it.toSet).foldl(a * b).chooseOne.priority
)
part 1: sumPriorities sacks.mapIt(it.distribute(2))
part 2: sumPriorities sacks.chunk(3)
that doesn't seem like a great choice for syntax though, like one of the devs was being clever instead of having a "public" keyword.
yeah, I'm not a fan of the star
feels like latex
does nim support macros
i prefer it, it makes it very easy to see public functions at a glance with little boilerplate
yes, nim has templates and macros
templates, like, C++ templates?
so technically you could just make a macro replacing public with *
templates are like compile-time code replacements
templates would probably work better
like pub proc fetch( seems better. but I'm complaining about a language I don't use so I have little room to stand on.
so you could put the public first
variable names can have spaces?
AoC in LaTeX 👀
did you know that snake_case and snakeCase are the same name in nim
(lualatex is cheating)
yeah, but you need to use ```
I see
actually i don't know if spaces work
what's up with part 1, then?
what the fuck i hate it
Writing an explanatory pdf with latex code computing the answer would be wack
but operators are done with:
proc `+`(a: MyClass): MyClass =
...
does it ignore cases and special characters?
the devs were really trying to be clever with nim
no, the first letter case matters
impure operator definitions 👀
i think it's turing complete
tfw arithmetic has side effects >.>
there is clever, and there is that
the reason they chose to do that is not to force a style on anyone -- similarly you can take a c-library and bind the functions to nim and keep your libraries style
still less "clever" than JS (==, var, {} + {})
I mean the naming thing isn't a huge issue
JS isn't clever, it's a clusterfuck
you don't really need two variables with a nearly exact same name
so I could alternate between camel_case and snakeCase between every usage? sounds like fun
aLMOST_SCREAMING_SNAKE_CASE
the reason i like nim though is the uniform function call syntax:
type Vector = tuple[x, y: int]
proc add(a, b: Vector): Vector =
(a.x + b.x, a.y + b.y)
let
v1 = (x: -1, y: 4)
v2 = (x: 5, y: -2)
# all the following are equivalent
v3 = add(v1, v2)
v4 = v1.add(v2)
v5 = v1.add v2
core dev sponsored obfuscation
i can get on board with those first two, but the last scares me
You don't need, but they are telling me I can't.
I think it is my decision as a programmer to make, not theirs as a language designer.
i don't mind parenless function application, but i've never seen them optional
you whitespace hater
Whitespace is an esoteric programming language developed by Edwin Brady and Chris Morris at the University of Durham (also developers of the Kaya and Idris programming languages). It was released on 1 April 2003 (April Fool's Day). Its name is a reference to whitespace characters. Unlike most programming languages, which ignore or assign little ...
but it makes dot auto-complete really good
i like that
i use haskell sometimes, which does function application without parens too
but having both is weird to me
i really hate parens, mostly because of the difficulty in typing them
shift + 9/0 is the most awkward combination
Shift + 9 and Shift + 0 are so far
Though none of !@#$%^&*() are particularly simple, yet are used very often
the curly and square brackets are much better
That's why real programmers swap the top letter row with the number row
i usually keep my right hand a little further right on my keyboard than normal
so it's strange to hear that these are difficult for some
I don't have much problem typing special characters, yeah. But I guess it does vary person to person
not difficult per se but difficult compared to asdfhjlk
i do shift combinations one-handed --- pinky on right shift + middle-finger on parens is so weird
i've been tempted to completely remap certain key combos on my keyboard to arbitrary symbols, but i can't figure out how to do it nicely on windows 
Razer supports complete keyboard remapping
[](){} is good to practice of you need to type C++ lambdas
are you on a laptop?
Maybe something for u
i hate this
Or do you use an external keyboard?
I got the keychron k8 pro recently.
mechanical keyboards >>
sadly []()<>{} isn't quite valid C++
if you want to see some crazy macros -- this is me trying to implement multiple inheritance in nim with macros:
https://github.com/salt-die/nimheritance/blob/main/nimheritance/classes.nim
but very close
I've been putting together my first custom board for the past few weeks
@clever dune Use <url> to suppress embeds.
wow that is unique although tbf I'm not particularly familiar with nim
oh god, vtables
the macro *class looks insane for a macro
I know they exist and what they are for, but I don't want to think about them...
for this, they only exist at compile time
spits out some normal nim objects and functions at the end
i've seen some aoc helpers in nim that use macros that turn something like:
day 1:
part 1:
some_code()
part 2:
some_more_code()
into auto-submissions
idk, I'm all for automating tedious things
I mean that sounds good
but I've never seen a need for that
submitting is close enough to being a one-off thing that I don't feel the need to automate it
I've just been too lazy to automate it honestly
I'm not fast enough that automating the submission would help, worse it might get the wrong answer. lol
it's not just saving time --- it's very easy to read
(and don't get me wrong, I love automating stuff, I use vim and write a lot of small scripts)
(thinks just have to start annoy me by being tedious for me to automate them)
whilst others code golf, I've been adding type hints to my code instead XD
all my solutions pass mypy with strict mode
uppercase = string.ascii_uppercase
lowercase = string.ascii_lowercase
letters = dict(enumerate((lowercase + uppercase),1))
common_letters = ''
total = 0
def clean(first_half,second_half):
global common_letters
for letter in first_half:
if letter in second_half:
common_letters += letter
return
with open('zzzinp.text','r') as file:
compartment = file.readlines()
for line in compartment:
half = len(line)// 2
clean(line[:half],line[half:])
#
def get_key(val,my_dict):
for key, value in my_dict.items():
if val == value:
return int(key)
return 0
print(common_letters)
for common in common_letters:
total += get_key(common,letters)
print(total) ```
onto 2nd
!global
When adding functions or classes to a program, it can be tempting to reference inaccessible variables by declaring them as global. Doing this can result in code that is harder to read, debug and test. Instead of using globals, pass variables or objects as parameters and receive return values.
Instead of writing
def update_score():
global score, roll
score = score + roll
update_score()
do this instead
def update_score(score, roll):
return score + roll
score = update_score(score, roll)
For in-depth explanations on why global variables are bad news in a variety of situations, see this Stack Overflow answer.
also, you can reverse the indexes of a dict by swapping the key/value.
letters = {v:k for k,v in enumerate((string.ascii_letters),1)}
oh and ascii_letters is lowercase + uppercase, so you can use that too. 🙂
