#AoC 2023 | Day 1 | Solutions & Spoilers

1515 messages Β· Page 2 of 2 (latest)

empty tree
#

was my part 1

#

im here but i dont want to really look at other solutions much until i figure out part 2

pseudo minnow
#

Oooh, I have loads of issues reading very loopy stuff πŸ˜„

#

This is scala but you can more or less replace every map with a list comprehension or so:

def part1Solution(rawInput: String): Int =
  rawInput
    .split("\n")
    .map(_.filter(_.isDigit))
    .map(line => (line.head.asDigit * 10) + line.last.asDigit)
    .sum
empty tree
#

thats super simple

pseudo minnow
#

Second one is harder tho, especially for a day 1 πŸ’€

cosmic mica
empty tree
#

idk if im gonna figure out the part 2

pseudo minnow
cosmic mica
#

@pseudo minnow ```py
print(*(v:=sorted(map(lambda s:sum(s),map(lambda x:map(int,x.split("\n")),open("inp.txt").split("\n\n")))),v[-1],sum(v[-3:]))[1:])

pseudo minnow
#

yeah I remember that one, considerably easier πŸ˜„

cosmic mica
#

AOC 2022 start was pretty easy until they hit us with the crane and the stacked containers πŸ’€

#

ppl def cheated by splitting the input in 2 in the txt.

pseudo minnow
#

It got really ugly for me there because I overengineered it

cosmic mica
#

I wanted to get the crates with programming so this is what it turned out ```py
crates = [list(filter(lambda h: h != " ", k)) for k in list(zip(8[iter([z[1] for y in zip(*zip(9[iter(zip(4[iter(x)]))])) for z in y])]))]

#

After that it was pretty easy.

pseudo minnow
#

Reading golfed solutions give me a headache (in a good way!) πŸ˜„

cosmic mica
#

Not stuff like eval or bitwise shifting with binary, that's too far.

barren kindle
#

@upper thicket two -> two2two

upper thicket
barren kindle
#

lol

old prism
#

cleaned up my solution a bit
the slice assignment keeps the lists at a max of 2 elements```py
from pathlib import Path

with open(Path(file).parent / "input") as f:
data = f.read().rstrip()

from re import finditer, Match, compile

numbers = "zero, one, two, three, four, five, six, seven, eight, nine".split(", ")

py3.12 woo

pattern = compile(fr"(?=(\d|{"|".join(numbers)}))")

p1 = p2 = 0
for line in data.splitlines():
values_1 = []
values_2 = []

for m, in map(Match.groups, finditer(pattern, line)):
    try:
        v = int(m)
    except ValueError:
        values_2[1:] = numbers.index(m),
        # values_2.append(numbers.index(m))
    else:
        values_1[1:] = values_2[1:] = v,
        # values_1.append(v)
        # values_2.append(v)

p1 += 10*values_1[0] + values_1[-1]
p2 += 10*values_2[0] + values_2[-1]

print(p1, p2)

#

though appending is probably much faster and easier to understand lol

empty tree
#

is there a way to break up a string based on dictionary key matches

#

im trying to avoid regex. I thought just replace all instances of "one" with "1",etc

fading remnant
#

i used subarrays

#

to test each one

#

O(N^2)

#

time complexity

regal spear
#

Complexity is negligible since each line is pretty short

crisp sequoia
#

my answer is incorrect yet every test case returns the right answer

#

do i have to scan al the 1000 strings ans answers now?😒

ancient cedar
crisp sequoia
#

yep it returned that

ancient cedar
#

share your code in #aoc-solution-hints

dense roost
#

that's what i thought at first. "it'd be trivial bc i can just ctrl + f replace everything!"
then i saw overlapping words :)

crisp sequoia
empty tree
#

im learning regex for this stupid thing

#

lol

barren kindle
#

in a language I've never used before

#

not that bad at the end of the day πŸ₯΄

dense roost
barren kindle
#

I'm used to it πŸ˜›

#

ssh to my desktop editing in vim

dense roost
#

and i thought mobile spreadsheeting was bad

#

i can't imagine mobile coding

barren kindle
#

with a decent phone keyboard it's quite fine

dense roost
#

oh cool

#

like physical keyboard?

barren kindle
#

no

#

on screen keyboard

#

full qwerty keyboard

dense roost
#

looks sweet

#

i need to try this out sometime

#

what was the random language for today?

barren kindle
#

but overall that and then vim is actually quite alright

#

I guess in part because not a lot of mouse dependence

regal spear
#

1seven8seven should return 17, not 18

barren kindle
dense roost
#

istg one day you'll get a ping for malbolge

barren kindle
#

groovy is mainly used as a config language for java iirc

ancient cedar
barren kindle
#

(though maybe I'm thinking of something different there)

ancient cedar
#

piet would be an interesting one

regal spear
#

groovy is if java was python

#

all the worst parts of python were added to java

barren kindle
#

I'm curious why groovy didn't take off, but kotlin did

#

oh wait, are gradle config files groovy?

bitter copper
#

uhhh

#

Β―_(ツ)_/Β―

barren kindle
#

Gradle scripts are written in either Groovy DSL or Kotlin DSL (domain-specific language).

#

apparently it is, and I'm not entirely making stuff up

smoky escarp
upper thicket
fair glade
#

heh

#

just finished

#

couldn't do it earlier, because of school and other work πŸ˜”

#

oh huh, I didn't even consider regex

#

that was probably the way to go πŸ‘€

#

mine is sort of bad

dense roost
#

aboo! :D

fair glade
#
from more_itertools import substrings_indexes

def part_1(inp: str) -> int:
    lines = inp.splitlines()
    digits = ["".join(i for i in line if i.isdigit()) for line in lines]
    return sum((int(d[0] + d[-1]) for d in digits))


def part_2(inp: str) -> int:
    lines = inp.splitlines()
    numbers = {
        "one": "1",
        "two": "2",
        "three": "3",
        "four": "4",
        "five": "5",
        "six": "6",
        "seven": "7",
        "eight": "8",
        "nine": "9",
    }

    ret = 0

    for line in lines:
        substrings = (
            "".join(i[0])
            for i in sorted(substrings_indexes(line), key=lambda t: t[1])
            if len(i) <= 5
        )
        digits = [i for i in substrings if i.isdigit() or i in numbers]

        first = digits[0] if len(digits[0]) < 2 else numbers[digits[0]]
        last = digits[-1] if len(digits[-1]) < 2 else numbers[digits[-1]]

        ret += int(first + last)

    return ret
fair glade
dense roost
#

i emerge from the vast dark to partake in aoc

fair glade
#

heh

dense roost
#

then i lurk again

fair glade
#

πŸ˜”

dense roost
#

trying to sm cool for every early day lol

fair glade
#

I think this year's first puzzle was harder than last year's πŸ‘€

dense roost
#

today i spreadsheeted

#

and on mobile for part 1

fair glade
#

I want to do my solutions in agda

dense roost
#

what's agda?

fair glade
fair glade
#

you can also do proofs and stuff with it

bitter copper
#

yeah what's up with that anyway

drowsy kelp
#

Was stumped about the overlapping numbers like a lot of people, before watching hyper-nutrino's video and realising the problem, as I am a noob, any obvious optimizations or things that indicate I don't know about some feature that I should?


import regex as re

with open('day1/input.txt','r') as f:
    inputlines = f.read().splitlines()

def part1(inputlines):
    ans = 0
    for line in inputlines:
        numline = []
        for char in line:
            if char.isdigit():
                numline.append(int(char))
        print(numline[-1])
        ans += (int(f'{numline[0]}{numline[-1]}'))

    return ans

def part2(inputlines):
  
    wordstonum = {
        'one':'1',
        'two':'2',
        'three':'3',
        'four':'4',
        'five':'5',
        'six':'6',
        'seven':'7',
        'eight':'8',
        'nine':'9'
    }

    def num(x: str):
        if x.isdigit():
            return x
        return wordstonum[x]
    
    pattern = "(?=(" + "|".join(wordstonum.keys()) + "|\\d))"
    ans = 0

    for line in inputlines:
        digits = re.findall(pattern,line)
        digits = [*map(num,digits)]
        ans += (int(f'{digits[0]}{digits[-1]}'))

    return (ans)

print(part1(inputlines))
print(part2(inputlines))```
fair glade
#

I'll still do a solution in groovy, just after the other handful of languages on my list to do the solutions in

drowsy kelp
urban saddle
flint hazel
#
with open("day1.txt") as f:
  puzzle = f.read().split("\n")

wordsToDigit = {
  "one": "o1e",
  "two": "t2o",
  "three": "t3e",
  "four": "f4r",
  "five": "f5e",
  "six": "s6x",
  "seven": "s7n",
  "eight": "e8t",
  "nine": "n9e"
}

digits = []
windowSize = 5
words = wordsToDigit.keys()
for i in puzzle:
  iterations = [i]
  for n in range(len(i) - windowSize + 1):
    smallS = iterations[-1][n: n+windowSize]
    for word in words:
      if word in smallS:
        i = i.replace(word, str(wordsToDigit[word]), 1)
        iterations.append(i)
  digitized = "".join(filter(str.isdigit, iterations[-1]))
  digits.append(int(f"{digitized[0]}{digitized[-1]}"))

print(sum(digits))```
tunnel vision: the puzzle
mortal siren
# drowsy kelp Yup!

It is curious that you use third-party regex without the main reason to use it in this day's puzzle :)

#

But regardless the solution looks clean

fair glade
#

from what I can tell, editor support for agda isn't amazing

#

at least, in terms of what I'm used to

drowsy kelp
pure widget
#

crimes (groovy solution)

def part1(lines) {
    lines.sum {
        def digits = it.findAll(/\d/).collect { it.toInteger() }
        digits[0] * 10 + digits[-1]
    }
}

def part2(lines) {
    def wordForm = ["", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"]
    def fore_regex = /\d|one|two|three|four|five|six|seven|eight|nine/
    def back_regex = /\d|eno|owt|eerht|ruof|evif|xis|neves|thgie|enin/
    lines.sum {
        def first = it.find(fore_regex)
        def first_num = first.isNumber() ? first.toInteger() : wordForm.indexOf(first)
        def second = it.reverse().find(back_regex)
        def second_num = second.isNumber() ? second.toInteger() : wordForm.indexOf(second.reverse())
        first_num * 10 + second_num
    }
}

File input = new File("inputs/day1.txt")
def lines = input.readLines()
println "part 1: ${part1 lines}"
println "part 2: ${part2 lines}"
pure widget
#

Groovy, the language roulette of the day (see the heading under #announcements message)

hasty void
#

the syntax is interesting

hasty void
#

I'm too committed to doing it in my own, but sounds fun!

#

can i suggest my language as part of the roulette? πŸ‘€

pure widget
#

you'd have to ask kat

#

I think they're all slightly more well-known ones though, and I believe the schedule is set?

bitter copper
#

source?

fair glade
#

your mother

#

dawn I'm having a crisis

#

agda-mode is making me do emacs shortcuts in vscode

pure widget
#

lovely

pure widget
bitter copper
barren kindle
pure widget
#

uhhh no, i was not aware there was one

uneven lava
#

it's up there somewhere, I have to pin it still

bitter copper
#

I'm getting clowned on in the other leaderboards I'm in

barren kindle
#

we be growing

bitter copper
#

but I don't wanna stay up late

barren kindle
#

to me it's just nice to see collective progress

barren kindle
pure widget
#

ty

bitter copper
#

half the time I spent today was installing the language ngl

pure widget
#

nix ftw

south badger
#

Done in like three minutes

#

And then it took me 90 minutes to get a working solution firRip

crude ledgeBOT
#

flake.nix line 14

groovy # day 1```
bitter copper
#

neat

#

it may be devcontainer time

fair glade
#

Agda took like 20 minutes to install ;-;

keen gulch
fair glade
#

my intention is to nuke my fedora install, reinstall, and then set up everything using nix πŸ‘€

pure widget
#

Do you want some resources that helped me with the language?

fair glade
#

I looked into silverblue, but I’m not sure how well that plays with dual booting

keen gulch
pure widget
#

The real pain is working with nixpkgs and writing derivations πŸ˜…

#

I'm decent at the language at this point, but that is just beyond me for anything nontrivial

fair glade
#

are you fully nixed, dawn?

pure widget
#

Nah, I'm using it in wsl

fair glade
#

I see

pure widget
#

still need some software that exists only on or is better on windows

keen gulch
#

I started going through the pills at one point, but when I asked some follow up questions on the nix discord about nix-env, they told me thats out dated and not to use pills because its focus on an older style πŸ€·β€β™‚οΈ . My whole system configuration is in a flake using home manager, so it seems like there are a lot of resources that aren't really targetted to a newer setup like that

fair glade
#

I dual boot, which is nice with a drive dedicated each to windows and Linux

pure widget
#

The word "flake"

fair glade
#

Christmas reactions

pure widget
#

Huh, interesting. I also use a Nix flake config, although it's quite minimal because I use WSL, as I said

#

I used to not know the home-manager option search existed, that made my config so much cleaner

#

oh woops, we've gotten wildly off topic

dense roost
#

christmas

#

flake

#

pretty cool

grave shale
#

gpt4 can't get part 2 right!

upbeat eagle
#

i decided to give it a shot with dumb approaches. for whatever reason, i decided to check the str from both directions to get short circuiting, but then did the checks in the dumbest ways possible 😌

#
accumulator = 0

templates = {"o": ["one"],
             "t": ["two", "three"],
             "f": ["four", "five"],
             "s": ["six", "seven"],
             "e": ["eight"],
             "n": ["nine"]}
mappings = {"one": "1", "two": "2", "three": "3", "four": "4", "five": "5", "six": "6", 
            "seven": "7", "eight": "8", "nine": "9",}

def check_digit(index, line, templates, mappings):
    total_length = len(line)
    char = line[index]

    if char.isdigit():
        return char

    inventory = templates.get(char, [])
    for item in inventory:
        
        item_length = len(item)
        if index + item_length - 1 < total_length:
            chunk = line[index : index + item_length]
            number = mappings.get(chunk)
            if number is not None:
                return number
    
    return "F" # to pay respects

with open("inputs/input_day1_.txt", "r") as file:
    for line in file:
        number = ""

        n = 0
        while n < len(line):
            digit = check_digit(n, line, templates, mappings)
            if digit.isdigit():
                number = digit
                break
            n += 1
        
        n = len(line) - 1
        while n >= 0:
            digit = check_digit(n, line, templates, mappings)
            if digit.isdigit():
                number += digit
                break
            n -= 1

        accumulator += int(number)
        
print(accumulator)
scenic jay
#
    return "F" # to pay respects

i'm stealing this

crimson coral
#

Where can I get help with my day 1 solution?

oak epoch
crisp sequoia
#

finally did it ```py
import aoc_lube
import re
LINES = aoc_lube.fetch(year=2023, day=1).splitlines()

LINES = ['1abc2', 'pqr3stu8vwx', 'a1b2c3d4e5f', 'treb7uchet']

lines = 0
index = 0
total_number = 0
ValueError_count = 0
nummers = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
spelled_nummers = {
'zero': 0,
'one': 1,
'two': 2,
'three': 3,
'four': 4,
'five': 5,
'six': 6,
'seven': 7,
'eight': 8,
'nine': 9,
}
reverse_spelled_nummers = {
0: 'zero',
1: 'one',
2: 'two',
3: 'three',
4: 'four',
5: 'five',
6: 'six',
7: 'seven',
8: 'eight',
9: 'nine',
}

print(LINES)

for string in LINES:
index_list = []
index_library = {}
print(string)
for number in spelled_nummers.keys():
if number in string:
indexes = [m.start() for m in re.finditer(number, string)]
for item in indexes:
index_library[item] = spelled_nummers.get(number)

for number in reverse_spelled_nummers.keys():
    if str(number) in string:
        print(str(number))
        indexes = [m.start() for m in re.finditer(str(number), string)]
        for item in indexes:
            index_library[item] = number

print(index_library)
index_list = []
for index in index_library.keys():
    index_list.append(index)
index_list.sort()
print(index_list)
first_number = index_library.get(index_list[0])
last_number = index_library.get(index_list[-1])
number_to_add = int(f'{first_number}{last_number}')
total_number = total_number + number_to_add
print(number_to_add)

print(total_number)

woeful gazelle
#
mappings = {
    "one": "1",
    "two": "2",
    "three": "3",
    "four": "4",
    "five": "5",
    "six": "6",
    "seven": "7",
    "eight" : "8",
    "nine": "9",
}
with open('day1.txt') as puzzle_file:
    for line in puzzle_file:
        line = line.strip()

        reduce_one = reduce(lambda a, kv: a.replace(*kv), mappings.items(), line)
        reduce_two = reduce(lambda a, kv: a.replace(*kv), reversed(list(mappings.items())), line)

        numbers = []
        final, final_reversed = zip(reduce_one, reduce_two), zip(reversed(reduce_one), reversed(reduce_two))

        for r1, r2 in final:
            if r1.isdigit():
                sum += int(r1)*10
                break
            if r2.isdigit():
                sum += int(r2)*10
                break

        for r1, r2 in final_reversed:
            if r2.isdigit():
                sum += int(r2)
                break
            if r1.isdigit():
                sum += int(r1)
                break

Non-Regex solution (because I forgot regex existsted)

#

Basically, just using .replace() twice - first time it replaces going down from 10->9->8 etc, second time 1->2->3

graceful trail
#
import re
f = open("input.txt", "r")

digit_pattern = r"\d|one|two|three|four|five|six|seven|eight|nine"

cor = {"one": "1", "two": "2", "three": "3", "four": "4", "five": "5", "six": "6", "seven": "7", "eight": "8", "nine": "9"}

items = f.readlines()
_sum = 0
for item in items:
    reg = re.search(fr".*?({digit_pattern}).*({digit_pattern}).*", item)
    nb = None
    if reg is None:
        reg = re.search(fr".*?({digit_pattern}).*", item).group(1)
        nb = cor.get(reg, reg)*2
    else:
        nb = "".join(cor.get(i, i) for i in reg.groups())
    _sum += int(nb)
print(_sum)

f.close()

``` everyone seems to be using findall, didn't even came to my mind
lime hatch
#

I aspire to write as clean code. Ultra performative

silk oracle
#

Raku:

my @lines = "day_01.input".IO.lines;
## Part 1
@lines>>.comb(/\d/)>>.[0, *-1]>>.join.sum.put;

## Part 2
my %digs = <one two three four five six seven eight nine> Z=> 1..9;
# Fast to write, slow to execute
@lines>>.match(/ \d | @(%digs.keys) /, :ex)>>.[0, *-1]>>.map({%digs{$_} // $_})>>.join.sum.put;
# Slow to write, fast to execute
@lines.map(-> \β„“ { (.min({.value.min}).key, .max({.value.max}).key).join given (%digs{$_} // $_ => β„“.indices($_) for %digs.kv) }).sum.put;
pure widget
#

initial d1p1 solution

lβ†βŠƒβŽ•NGET'./day1.txt' 1
βŽ•β†+/{⍎v[1,β‰’v←⍡/β¨β΅βˆŠβŽ•d]}Β¨l
bitter bluff
#
nums = {
    "one": 1,
    "two": 2,
    "three": 3,
    "four": 4,
    "five": 5,
    "six": 6,
    "seven": 7,
    "eight": 8,
    "nine": 9,
}

with open("aoc/2023/input.txt") as file:
    total = 0
    for line in file.readlines():
        ans = []
        for iteration in (enumerate(line), reversed(list(enumerate(line)))):
            for i, c in iteration:
                if c.isdigit():
                    ans.append(int(c))
                    break
                for numk, num in nums.items():
                    if line[i:].startswith(numk):
                        ans.append(num)
                        break
                else:
                    continue
                break
        total += ans[0] * 10 + ans[1]

    print(total)

shitass solution i spent an hour on because i was not breaking out of a nested loop, just remembered for else exists pithink

mortal siren
#

for-else exists but using an early return in a function is much nicer (and can break out of nested loops easily)

cosmic mica
#

Who loves parsing?

limber pulsar
#

I am actually really salty that this was the issue

#

Also somewhat disappointed in the fact that python's builtin re doesnt' have overlapping matches at all

mortal siren
limber pulsar
#

Normally AOC is good about catching these technical issues that you're not really expected to be searchign for on day one in the example...

mortal siren
mortal siren
mortal siren
limber pulsar
#

I thought python's regex module did overlapping

#

I'm also sleep deprived

mortal siren
limber pulsar
#

AOC should have caught it
Python should've fixed it by now
I should have noticed

#

sigh

mortal siren
#

Well I guess it depends if you count ?= as overlapping support or not

limber pulsar
#

I'd call that a hack

mortal siren
#

In some sense it does support it but not in an obvious way

#

Yeah that’s fair

limber pulsar
#

Not like whatever but

#

If I had to do that in production code

#

I'd comment it with #hack required because _insert reason here_

mortal siren
#

Yeah indeed

#

I try not to use crazy regex in prod code

regal merlin
#

# hack required for job security trollface

mortal siren
#

I don’t want to be that guy whose Python looks like Morse code lol

limber pulsar
#

I hacked some django theming code with

#
# I know why this was written this way, but it's wrong. 
# So we actually do it differently. This is noted in the project readme, since the library documentation is now wrong.
# Speeds up page load times by 50%
mortal siren
#

lol πŸ˜‚

limber pulsar
#

The actual technical reasons is a conflict between two different use cases for the theming

#

between different themes for different pages and different theme "layers"

mortal siren
#

I do love coming across comments like that

limber pulsar
#

the engine didn't actually support both at once so I hacked it to

#

moved on

mortal siren
#

A classic I enjoy is a comment saying β€œthis is temporary”

#

You just know that code is going to be there for the next 50 years

limber pulsar
#

One of my favorite comments is

#

"this is technically like O(n^5) but it never runs on more than 15 items or outside of debug mode"

mortal siren
#

πŸ˜‚

limber pulsar
#

Actually solving the problem would be mentally hard and well, debug mode, not too slow, time to move on and whistle

#

it's part of a table layout engine

#

kind of proud of it, it makes pretty tables

#

If I were to go back and owrk on this code

#

I would probably un-nest it a bit and make the comments longer

#

code's 4 years old now

mortal siren
#

We’re probably getting off topic for the channel, btw. But yeah, who cares about performance with small inputs - certainly not me :)

limber pulsar
#

lol

fluid ivy
#

Are non-Python solutions alright for this channel?

#

For part 1 I did
|| ```
Ax: input
Bx: =REGEX(Ax, "[0-9]")
Cx: =REGEX(Ax, "0-9")
Dx: =Bx*10 + Cx
E1: =SUM(D1:D99999)

For part 2 I did
|| ```
Ax: input
Bx: =SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(Ax, "nine", "n9e"), "eight", "e8t"), "seven", "s7n"), "six", "s6x"), "five", "f5e"), "four", "f4r"), "three", "t3e"), "two", "t2o"), "one", "o1e")
Cx: =REGEX(Bx, "[0-9]")
Dx: =REGEX(Bx, "[0-9](?!.*[0-9])")
Ex: =Cx*10 + Dx
G2: =SUM(E1:E99999)
``` ||
limber pulsar
#

excel?

fluid ivy
#

yeah

limber pulsar
#

brave

fluid ivy
#

idk why all the solutions are so long ducky_concerned maybe I am missing something

#

Basically the reason a naive .replace() chain doesn't work is overlapping digits. So something likeeightwo wouldn't work (it can't be an input because it wouldn't fit day 1, but whatever).
But if you replace eight with e8t instead of just 8, you get e8two which will work just fine

fluid ivy
# upper thicket

ok this one is even better and covers more edge cases theoretically (like if two letters can overlap, imagine if woops was a number and it overlapped with two)

south badger
#

Wait
Wait wait wait wait wait wait

waitjustadangdarnminute

Excel has REGEX !?!?

scenic jay
#

regexcel

fading axle
#

does kinda suck it's not builtin

plucky sentinel
#

gaaaahhhhhhhhhhhhhh

fluid ivy
vocal pecan
#

What is DIGIT_MAP?

carmine ibex
#

i don't know what im doing anymore ```py
import re
import functools

with open("./input.txt") as file:
input = file.read().strip()

nums = lambda input: sum(
int(n[0] + n[-1]) for n in re.sub(r"[a-z]", "", input).split("\n")
)
nums = lambda input: sum(
int((match := re.findall(r"(\d)", line))[0] + match[-1])
for line in input.split("\n")
)
nums = lambda input: sum(
int((s := [*filter(str.isdigit, l)])[0] + s[-1]) for l in input.split("\n")
)

print(nums(input))
print(
nums(
functools.reduce(
lambda v, ie: v.replace((e := ie[1]), e + str(ie[0] + 1) + e),
enumerate("one two three four five six seven eight nine".split()),
input,
)
)
)

carmine ibex
#

they do

fluid ivy
#

str.isdigit matches a whole bunch of characters that are not 0123456789

carmine ibex
#

Oh?

#

I did not know that

fluid ivy
crude ledgeBOT
#

@fluid ivy :white_check_mark: Your 3.12 eval job has completed with return code 0.

True
fluid ivy
#

!charinfo ΰ―«

crude ledgeBOT
fluid ivy
#

same with \d in regex

carmine ibex
#

I see.

hidden schooner
#

me using .isdigit

fluid ivy
#

I imagine thousands of projects have some inconsistency because of using \d in regex

hidden schooner
#

!e ```py
import re

print(re.search(r"\d+", "xΰ―«d"))

crude ledgeBOT
#

@hidden schooner :white_check_mark: Your 3.12 eval job has completed with return code 0.

<re.Match object; span=(1, 2), match='ΰ―«'>
hidden schooner
#

dam

vocal robin
#
import re


with open('input.txt') as f:
    calibration_document = f.read().splitlines()


def part1():
    calibration_values = []
    for line in calibration_document:
        digits = re.findall(r'\d', line)
        calibration_value = int(digits[0] + digits[-1])
        calibration_values.append(calibration_value)

    print(sum(calibration_values))


def part2():
    digit_map = {
        'one': 'o1e',
        'two': 't2o',
        'three': 't3e',
        'four': 'f4r',
        'five': 'f5e',
        'six': 's6x',
        'seven': 's7n',
        'eight': 'e8',
        'nine': 'n9e'
    }
    calibration_values = []
    for line in calibration_document:
        for digit in digit_map:
            line = line.replace(digit, digit_map[digit])

        digits = re.findall(r'\d', line)
        calibration_value = int(digits[0] + digits[-1])
        calibration_values.append(calibration_value)

    print(sum(calibration_values))


if __name__ == '__main__':
    part1()
    part2()
#

ignore overlap issue by just letting it hang out, ya feel?>

#

it looks so dumb but it works

tired zenith
#

That's so powerfully cursed

#

I hate it and love it

vocal robin
#

beats doing the ugly ass initial solution i had

tired zenith
#

Fair

vocal pecan
#
import re


file = open('input.txt', 'r')
input = [i for i in file.read().strip('\n').split('\n')]

def part1(lines):
    total = 0
    for line in lines:
        numbers = re.findall(r'[0-9]', line)
        if len(numbers) == 1:
            double = f'{numbers[0]}{numbers[0]}' 
            total += int(double)
        else:
            total += int(numbers[0] + numbers[-1])
    return total

def part2(lines):
    wordstonum = {
        'one':'1',
        'two':'2',
        'three':'3',
        'four':'4',
        'five':'5',
        'six':'6',
        'seven':'7',
        'eight':'8',
        'nine':'9'
    }

    def num(x: str):
        if x.isdigit():
            return x
        return wordstonum[x]
    
    pattern = "(?=(" + "|".join(wordstonum.keys()) + "|\\d))"
    ans = 0

    for line in lines:
        digits = re.findall(pattern,line)
        digits = [*map(num,digits)]
        ans += (int(f'{digits[0]}{digits[-1]}'))

    return (ans)


print(part2(input))
print(part1(input))
barren kindle
#

e.g.

re.findall('a+', 'a'*10000)
#

and I suspect zero length matches complicate things as well

#

(it's kinda breaking how regex works)

dreamy edge
#

Took me 3 hours to solve day 1 and man, I made a lot of mistakes. You can watch my dumbass on stream trying to tackle it here: https://www.youtube.com/watch?v=QbmDmoC1PS0

Or be spoiled and see the solution. Do note that the code is now broken for Part 1 of Day 1. I might reorganize it to accommodate Part 1 and Part 2 solution in a single Python file. And looking at other people's code made me realize how overcomplicated my code is. Dammit. https://github.com/Qoyyuum/adventofcode/blob/2023/2023/01/trebuchet.py

Powered by Restream https://restream.io

Coding late nights demands coffee. Buy me some at https://www.buymeacoffee.com/qoyyuum

You can join along or tackle the code challenges at https://adventofcode.com

The Github code repo for Advent of Code:
https://github.com/Qoyyuum/adventofcode

#Python #AdventOfCode #AOC

β–Ά Play video
GitHub

Advent of Code to all of the daily puzzles of each year. - Qoyyuum/adventofcode

open bay
#

My Day1/Part 1
And youdont want to know how long it took me. a very Looooooooooooooooooooooooooooooooooooooonnnnnnnnnnnnnnnnnnnngggggggggggggggggggggggggggg time indeed. Though there were lot of iterations till arriving here. Thank you stackoverflow.

||```py
def get_integers(string):
# parse our string for integers
return [int(c) for c in string if c.isdigit()]

def count_sums():
sums = []
# loop through our data
result = [get_integers(item) for item in data]
for z in result:
# if we have more than one integer, we need to combine them
if len(z) > 1:
x = int(f"{z[0]}{z[-1]}")
# add our sum to our list
sums.append(x)
else:
# if we only have one integer, we need to double it
x = int(f"{z[0]}{z[0]}")
sums.append(x)
return sums

def main():
print(sum(count_sums()))

if name == "main":
main()

mortal siren
#

Regarding your code, a style point - try to avoid naming variables as _ if you plan to use them. _ is a convention to label only variables that would be thrown away.

#

I have other code suggestions if you want to hear them :)

open bay
mortal siren
#

Last comment is that count_sums and the sums list could be renamed because you aren’t actually summing the values until later.

open bay
#

so count_sums and sums should be different names for readability of code?
i dont see how i could have evaluated teh single digit or double digit unless i had the iff. can you show me what you mean?

scenic jay
open bay
#

I am appending a zero to the number if the length of the _ is 1, ( x = int(f"{[0]}{[-1]}"))
But I see your point having re-examined the actual input text, there are no single digits like there were in the test data.

scenic jay
#

wdym appending a zero?

open bay
#

im getting confused with day 4, scratchcards. Methinks comments might have been helpful

mortal siren
#

This is AOC - no one writes comments :P

open bay
#

haha, I just did. Ok so now that I know what im talking about and not scratchcards, I use the if to either output the number if its more than a single digit geting the fist and last figit from any number {0} or {-1} and if it is a single digit I convert it to two digits [0][0]

mortal siren
#

You can do 0 and -1 for both cases

scenic jay
mortal siren
#

:O
I need to look at the code for that

#

I’ll do that now even though I don’t have my IDE with me :(

gray timber
#
file = open("day1_input.txt", "r")
cum_sum = 0
number_words = {
    "one": "1",
    "two": "2",
    "three": "3",
    "four": "4",
    "five": "5",
    "six": "6",
    "seven": "7",
    "eight": "8",
    "nine": "9"
}
window_size = 5


def check_for_replace(line_to_replace):
    for word in number_words.keys():
        line_to_replace = line_to_replace.replace(word, number_words[word])
    return line_to_replace


def check_word(word_to_check):
    for i in range(len(word_to_check) - window_size + 1):
        x = check_for_replace(word_to_check[i:i + window_size])
        if x != word_to_check[i:i + window_size]:
            word_to_check = word_to_check.replace(word_to_check[i:i+window_size], x)
    return word_to_check


for line in file:
    line = check_word(line)
    nums = [i for i in line if i.isdigit()]
    line_value = int(nums[0] + nums[-1])
    cum_sum += line_value
    break
print(cum_sum)

Does anyone know where i went wrong?

mortal siren
gray timber
#

yeah

#

!e

#file = open("day1_input.txt", "r")
cum_sum = 0
number_words = {
    "one": "1",
    "two": "2",
    "three": "3",
    "four": "4",
    "five": "5",
    "six": "6",
    "seven": "7",
    "eight": "8",
    "nine": "9"
}
window_size = 5


def check_for_replace(line_to_replace):
    for word in number_words.keys():
        line_to_replace = line_to_replace.replace(word, number_words[word])
    return line_to_replace


def check_word(word_to_check):
    for i in range(len(word_to_check) - window_size + 1):
        x = check_for_replace(word_to_check[i:i + window_size])
        if x != word_to_check[i:i + window_size]:
            word_to_check = word_to_check.replace(word_to_check[i:i+window_size], x)
    return word_to_check


#for line in file:
line = "oneightwoneightwo"
line = check_word(line)
print(line)
nums = [i for i in line if i.isdigit()]
line_value = int(nums[0] + nums[-1])
cum_sum += line_value
print(cum_sum)
crude ledgeBOT
#

@gray timber :white_check_mark: Your 3.12 eval job has completed with return code 0.

12
gray timber
#

ah wait

#

12 is wrong for oneightwoneightwo

#

1igh2n8wo
18 right?

mortal siren
#

no it should be 12

ancient cedar
gray timber
#

so oneightwoneightwo is 18282?

ancient cedar
#

you could see it that way, yes

gray timber
#

ah ok thx

ancient cedar
#

as long as you're taking the "one" and "two" from the start and end respectively it's fine

gray timber
#

wouldnt it be the easiest way to just put all the overlaps alos in my dict like "oneight": "18", etc.?

ancient cedar
#

one workaround several people found was ||replacing one -> o1e, two -> t2o, etc. so the overlaps are preserved||

ancient cedar
#

or "threeighthreeighthree..."

gray timber
gray timber
mortal siren
#

ninesixmlfjxhscninehqcdvxf8nzfivetwonehhd
an example from my input - two is overwriting the o from one so you don't pick it up

gray timber
mortal siren
#

cool :)

frigid nacelle
#

I'm building up a for loop and trying to go through all of this. it's a bit of a head scratcher. This is where I gotten to:

for a in puzzle_input:
    calibrated_1st_value = int(a[0][0].isdigit())
    calibrated_2nd_value = int(a[0][-1].isdigit())

calibrated_values = calibrated_1st_value + calibrated_2nd_value

print(calibrated_values)```

Sigh. I'm a bit lost. I managed to convert puzzle input into the list so I could build a for loop out of it
#

What I understand from the day 1 site (https://adventofcode.com/2023/day/1)>

  • I need to pull 1st and last numbers from a string that can be only be int.
  • Add just 1st and last numbers as 1 number
    Then make a sum of all the calibration values.

What doesn't make sense to me is that If I look at this example:
For example:

1abc2
pqr3stu8vwx
a1b2c3d4e5f
treb7uchet

that last line, 7. According to this site, the calibration would be 77, not 77. Is it because that 7 is a sole remaining number that it can be somehow turn into 77?

scenic jay
#

the 1st number in treb7uchet is 7
the last number in treb7uchet is 7
hence, the calibration is 77

#

doesn't matter if they're the same digits

frigid nacelle
#

Okay, fair enough, that makes sense.

#

Thanks STickie. Now to figuire out the rest.

frigid nacelle
#

I'm still working this out but is this output correct?

#

['186', '5713', '736', '716', '711', '1262', '9351', '342124', '998', '77', '75447']

#

Nvm, no. This output is incorrect. Back to work then.

drowsy locust
#

thoughts on my solution?

import re
import time

def main() -> None:
    with open(r"D:\01 Libraries\Documents\Coding\Advent of Code\Advent2023\Day 1\puzzle.txt",
              "r", encoding="utf-8") as f:
        puzzle: str = f.read()

    puzzle: list = puzzle.split("\n")
    total: int = 0
    for line in puzzle:
        digits = re.findall(r"\d", line)
        if len(digits) == 0:
            pass
        else:
            temp_digit = digits[0] + digits[-1]
            total += int(temp_digit)
    print(total)

if __name__ == "__main__":
    start = time.perf_counter()
    main()
    end = time.perf_counter()
    print('\n', end - start)
mortal siren
#

Also, nice type hinting but usually you wouldn't just typehint a list on its own - you'd indicate what was in that list. In this case it's a list of strings so you'd do list[str]

drowsy locust
mortal siren
#

not a big deal though

drowsy locust
#

I was getting index errors until I put that exception πŸ˜…

mortal siren
#

again, are you sure it wasn't just an empty line?

#

left over from the last split?

drowsy locust
#

let me check to be sure

#

yeah

#

apparently it was for the last line which is empty

#

the error I mean

mortal siren
#

yup - so that's where splitlines() helps

drowsy locust
#

I just assumed there were lines without digits lol

mortal siren
#

ha fair :)

mortal siren
#

finally you can use timeit to time the solution easier

#
import timeit
print(timeit.timeit(main, number=1))
drowsy locust
mortal siren
#

no - as long as you didn't call main beforehand :)

drowsy locust
#

so it would be like this?

if __name__ == "__main__":
    print(timeit.timeit(main, number=1))
mortal siren
#

that's basically how I do it, yup

#

one more thing about typehinting as well - typically you don't need to typehint many individual vars

#

total: int = 0 - the type hint here probably isn't necessary because most tools will be able to infer that total is an int simply because you assigned 0 to it

drowsy locust
#

alright

mortal siren
#

even if you were to run mypy on strict mode, it wouldn't insist that you type hint that var

drowsy locust
mortal siren
#

as for datetime.now, I suspect it's just not as precise

drowsy locust
#

I see

mortal siren
#

anyhow - have a go at p2 now :)

#

oh - perhaps one other thing to mention - your regex solution is fine, and actually a good idea considering p2 coming up. But if you were only doing P1, jumping straight to regex probably is overkill for solving this

drowsy locust
#

why is it overkill?

mortal siren
#

digits = [char for char in line if char.isdigit()] works just as well and is more readable

drowsy locust
#

ah okay

#

to be honest

#

regex is my go to solution to anything that requires matching stuff

mortal siren
#

in the end, you do you - but I would suggest not to jump to regex immediately

#

it's a great tool, but used sparingly - when the occasion calls for it

drowsy locust
#

just out of complexity sake or something else?

mortal siren
#

I think readability

#

regex patterns aren't always the most readable

#

in general, favour pure Python unless the resulting pure Python would be more complicated to express than the regex

drowsy locust
#

ah okay

mortal siren
#

P2 is an example where regex becomes more viable as a solution

#

just because the resulting pure Python might be quite a bit less readable

drowsy locust
#

I see

#

tbh, I have used regex as a primary tool for so long that I kind of can just read it lol

mortal siren
#

again, you do you - after all, it's AOC

#

that's just my thoughts on it

drowsy locust
#

oh I understand. was jut giving my thoughts as well

drowsy locust
#

@mortal siren code for the second part:

import re
import timeit

c_digs = {
    'one': '1', 'two': '2', 'three': '3',
    'four': '4', 'five': '5', 'six': '6',
    'seven': '7', 'eight': '8', 'nine': '9'
}

def main() -> None:
    with open(r"D:\01 Libraries\Documents\Coding\Advent of Code\Advent2023\Day 1\puzzle.txt",
              "r", encoding="utf-8") as f:
        puzzle: str = f.read()

    puzzle: list[str] = puzzle.splitlines()
    total = 0
    for line in puzzle:
        digits: list[str] = re.findall(r"\d|one|two|three|four|five|six|seven|eight|nine", line)
        if digits[0].isdigit() and digits[-1].isdigit():
            total += int(digits[0] + digits[-1])
        elif digits[0].isdigit() and not digits[-1].isdigit():
            total += int(digits[0] + c_digs[digits[-1]])
        elif not digits[0].isdigit() and digits[-1].isdigit():
            total += int(c_digs[digits[0]] + digits[-1])
        elif not digits[0].isdigit() and not digits[-1].isdigit():
            total += int(c_digs[digits[0]] + c_digs[digits[-1]])
    print(total)

if __name__ == "__main__":
    print(timeit.timeit(main, number=1))

I'm seing it flow in the debugger, and it should work, but AoC is saying that my resut is too low

mortal siren
#

Firstly - does your code work for test input?

#

(it looks like it should but do check that first)

drowsy locust
#

test input? wdym?

#

it outputs a result if that's what you're asking

mortal siren
#

so in the puzzle description they give you examples

#

and what result you should expect from the examples

drowsy locust
#

right

mortal siren
#

whenever you have issues, the first thing to do is run your code over the examples and check that you get the result they say

#

I actually go to the trouble of writing automated tests for all example inputs, but that's generally overkill :)

drowsy locust
#

ah okay

mortal siren
#

so check that out and report back whether it failed any or passed them all

drowsy locust
#

so

#

apparently splitlines() is not ignoring the empty string

#

@mortal siren

#

also, yes

#

the code I wrote prints what the example says it's supposed to

mortal siren
drowsy locust
drowsy locust
mortal siren
#

So you have an extra newline at the beginning and extra space at the end

#

To get rid of the extra newline at the beginning, type \ right after the opening triple quotes

scenic jay
#

or you could attach a .strip() to the end of the string

mortal siren
#

To get rid of the spaces at the end, remove the spaces at the end :) To the left of the closing triple quote

mortal siren
scenic jay
#

fair

mortal siren
#

But yes you right they can strip() too ofc

#

Anyway this isn’t the main thing

#

The main thing is

drowsy locust
#

gotcha

mortal siren
#

Have you considered the oneight case?

drowsy locust
#

what about it?

#

ah

#

i see

#

but my logic still captured it appropriately

#

so what's the issue?

mortal siren
#

If you test oneight on its own, do you get 18?

drowsy locust
#

no

drowsy locust
# drowsy locust

as you can see here, it captured everything exactly like the example

mortal siren
#

You need to capture both one and eight

drowsy locust
#

ah

mortal siren
#

Or put another way, overlapping numbers count

drowsy locust
#

interesting the puzzle not mentioning that

mortal siren
#

I did consider this case and thought that overlapping should not count because one word counts as a digit, so if you count that as a unit it shouldn’t be allowed to share letters with neighbouring words

#

But it counts

#

So there

drowsy locust
#

bs lol

mortal siren
#

I think this puzzle is the most BS of any in AOC, though others disagree

#

And the fact that it’s day 1 puzzle makes that even worse

drowsy locust
#

btw

mortal siren
#

Hardest, most BS day 1 puzzle

#

But there it is

scenic jay
#

I disagree with you solely because day 8 exists

drowsy locust
#

is that if/elif block the most elegant solution for the problem?

scenic jay
#

but yeah this was kinda bad too

mortal siren
#

But there you go

scenic jay
drowsy locust
#

is there a way to do it in less lines?

mortal siren
scenic jay
#

there's almost always to do it in one line sure, but that usually isn't good looking nor well written so I wouldn't aim for it

mortal siren
#

Idk I think using dict.get would be good looking

mortal siren
#

dict.get(key, default)

#

If the key exists in the dictionary, retrieve it - else retrieve the default value

drowsy locust
mortal siren
#

There is actually a way to make it work with your existing regex pattern

#

Let me know if you want to know about that

drowsy locust
#

nah, I'll figure it out

#

and yeah, I know

#

I have done this kind of thing before

mortal siren
#

Alrighty!

drowsy locust
#

last letter/first letter matching

#

just don't remember off the cuff, because it was literally only once lol

mortal siren
#

That’s not actually what I would suggest

#

You can make it work with literally your pattern unchanged

#

But change the code around it

#

Or change the pattern and don’t change the code

#

Both are viable options

drowsy locust
#

ah okay

mortal siren
#

And yeah, the get. For example you can do c_digs.get(digits[0], digits[0])

#

Are you familiar with dict.get at all?

drowsy locust
#

used once for a problem long time ago, didn't like and it didn't really fit my use, so no

#

yay

#

with reddit's spoilers I did it

#

I didn't know enough about regex operations to do this without spoilers

#

didn't even what lookahead or non-overlapping was

scenic jay
#

the lookahead is basically just there to avoid consuming the string as you match it

drowsy locust
#

I didn't know that was a thing!

#

well, I guest htat's the point of the event after all

#

foce you to learn new things

#

well onto to day 2

#

@mortal siren if you wanna talk about better ways to do it than that if/elif block, I'm all ears

analog ice
mortal siren
drowsy locust
mortal siren
#

can do it here, but let's do it later - I want to finish day 16 first

drowsy locust
#

np

quasi vapor
old ember
#

"oh this question is easy... there's a part two YOU SCOUNDRELS"

patent steeple
#
with open("input1.txt") as f:
    data2 = f.read()
    data2 = data2.splitlines()


list_of_matches = [
    "1",
    "2",
    "3",
    "4",
    "5",
    "6",
    "7",
    "8",
    "9",
    "0",
    "one",
    "two",
    "three",
    "four",
    "five",
    "six",
    "seven",
    "eight",
    "nine",
]


def str_num_to_int(stringnumber):
    dct2 = {
        "0": 0,
        "1": 1,
        "2": 2,
        "3": 3,
        "4": 4,
        "5": 5,
        "6": 6,
        "7": 7,
        "8": 8,
        "9": 9,
        "10": 10,
        "one": 1,
        "two": 2,
        "three": 3,
        "four": 4,
        "five": 5,
        "six": 6,
        "seven": 7,
        "eight": 8,
        "nine": 9,
    }

    return dct2[stringnumber]


print(data2[0])


def bob(lst):
    func_lst = []
    for line in lst:
        dct1 = {}
        for word in list_of_matches:
            if word in line:
                index = line.index(word)
                dct1[index] = word
                print(dct1)
            else:
                pass
        final_number = str(str_num_to_int(dct1[min(dct1)])) + str(
            str_num_to_int(dct1[max(dct1)])
        )
        func_lst.append(int(final_number))
        print(int(final_number))
    test = sum(func_lst)
    print(test)


bob(data2)

Apparently my day 1 part 2 solution returns +10 on the correct number, does anyone know why its returning 54540 instead of 54530 (the solution to the dataset of day1 part 2). Thanks.

old prism
patent steeple
old ember
#

Is it cheating to use Perl for part 1?

#
# aoc day one part one
use v5.36;

open my $fh, '<', 'input.txt' or die;

my $total = 0;
while (my $line = <$fh>) {
    my ($x) = ($line =~ /\D*(\d)/);
    my ($y) = ((reverse $line) =~ /\D*(\d)/);
    $total += "$x$y";
}
say $total;```
verbal hinge
copper inlet
#

I know this is pretty far past, but this is my first bit of code and I wanted to share it

check = 0
value = 0
#this function uses a for loop to iterate through a string and extract all numeric characters
def numextract(current):
    extracted = ""
    for char in current:
        if char.isnumeric():
            extracted += char
    return extracted
#this function removes all numbers besides the outer two
def numslim(num):
    final = 0
    snum = str(num)
    fir = snum[0:1]
    firI = int(fir)
    lisbac = snum[::-1]
    sec = lisbac[0:1]
    secI = int(sec)
    hold = firI * 10 + secI
    final = hold + final
    return final
data = input("Dataset: ")
datalist = data.splitlines()
length = len(datalist)
while length != check:
    current = datalist[check]
    num = numextract(current)
    digit = numslim(num)
    value = value + digit
    check = check + 1
print("Calibration Value", value)
mortal siren
copper inlet
#

Not right now! Thank you for asking though. I know there are tools I'm not aware of that I could have done much better if I had used them, but I used what I know, and I think that's enough to be proud of for now

mortal siren
thin comet
#
file = open("aoc1.txt", "r")
data = file.readlines()
number_strings = {"one": "1", "two": "2", "three": "3", "four": "4", "five": "5", "six": "6", "seven": "7",
                  "eight": "8", "nine": "9"}
calibration_sum = 0


def reverse_check(line):
    temp_str = ""
    for char in line:
        if line[0].isnumeric():
            line_numbers.append(line[0])
            break
        temp_str = line[0:3]
        temp_str = temp_str[::-1]
        if temp_str in number_strings:
            line_numbers.append(number_strings[temp_str[0:3]])
            break
        temp_str = line[0:4]
        temp_str = temp_str[::-1]
        if temp_str in number_strings:
            line_numbers.append(number_strings[temp_str[0:4]])
            break
        temp_str = line[0:5]
        temp_str = temp_str[::-1]
        if temp_str in number_strings:
            line_numbers.append(number_strings[temp_str[0:5]])
            break
        else:
            line = line[1:]


for line in data:
    line_numbers = []
    temp_string = ""
    running = True
    while running:
        if len(line_numbers) > 1:
            break
        while line[0].isnumeric():
            line_numbers.append(line[0])
            line = line[::-1]
            reverse_check(line)
        if line[0:3] in number_strings:
            line_numbers.append(number_strings[line[0:3]])
            line = line[::-1]
            reverse_check(line)
        elif line[0:4] in number_strings:
            line_numbers.append(number_strings[line[0:4]])
            line = line[::-1]
            reverse_check(line)
        elif line[0:5] in number_strings:
            line_numbers.append(number_strings[line[0:5]])
            line = line[::-1]
            reverse_check(line)
        else:
            line = line[1:]

    calibration_sum += int(line_numbers[0] + line_numbers[len(line_numbers) - 1])
file.close()
print(calibration_sum)```
#

my full solution, im sure it's ugly but it works

smoky escarp
#

this is similar to the other solution you posted where there is a lot of repeated code. You might want to think about how you could write a couple of functions that take in a parameter.