#Advent of Code 2025

1 messages · Page 1 of 1 (latest)

brittle void
#

day1pt1 with awk
||

#!/usr/bin/awk -f

BEGIN {
    i=0
    x=50
}

{
    num=substr($1, 2)

    if (substr($1, 0, 1) == "L")
        x = (100 + x - (num % 100)) % 100
    else
        x = (x + num) % 100
    fi

    if (x == 0)
        i = i + 1
    fi
}

END {
    print i
}

||

#

day1pt2 with awk
||

#!/usr/bin/awk -f

BEGIN {
    i=0
    x=50
}

{
    num=substr($1, 2)

    #print "---"
    #print "x", x, "i", i
    #print "NEW LINE", $1
    if (substr($1, 0, 1) == "L")
    {
        if (int((num / 100)) > 0)
        {
            #print "ADD ROLL UNDER", i + int(num / 100)
            i = i + int(num / 100)
        }

        flip=100 - x
        if ((flip != 100) && (int((flip + (num % 100)) / 100) > 0) && ((flip + num) % 100 != 0))
        {
            #print "ADD ROLL UNDER", x, "-", (num % 100), ", i =", i + 1
            i = i + 1
        }

        x = (100 + x - (num % 100)) % 100
    }
    else
    {
        if (int((num / 100)) > 0)
        {
            #print "ADD ROLL OVER", i + int(num / 100)
            i = i + int(num / 100)
        }
        if ((int((x + (num % 100)) / 100) > 0) && ((x + num) % 100 != 0))
        {
            #print "ADD ROLL OVER, i =", i + 1
            i = i + 1
        }

        x = (x + num) % 100
    }

    if (x == 0)
    {
        #print "ADD x == 0, i =", i + 1
        i = i + 1
    }
}

END {
    print i
}

||

smoky owl
#

barren

brittle void
#

Desolate lands 😔

smoky owl
#

i figured out why it was Internal-Erroring

#

this is not supposed to look like this

zealous jungle
#

i just made naive brute force approach after my math for part 2 was wrong

#

which is

for _ in 0..steps {
    pos += dir;

    if pos < 0 {
        pos += 100;
    }
    if pos >= 100 {
        pos -= 100;
    }

    if pos == 0 {
        all_zeros += 1;
    }
}
brittle void
zealous jungle
#

i should write the solver in const lol

#

just for the extra challenge

#

input is already const

#

if only RA didn't just ignore include_str

#

now it builds in 2.5 seconds instead of 0.1 lol

#

but it solves at compile time!

#

did you know you can't match on strings in const

abstract meteor
zealous jungle
#

for what

#

i only see a match possible for pos < 0 and >= 100

abstract meteor
#

I think match is cleaner

#

If you want a better reason, a match statement would force you to cover all of the possible values of a integer

zealous jungle
#

pos will be in range 0 ..= 99 and dir is either -1 or 1, so pos + dir is -1 ..= 100

pseudo quest
#

doing it with rust to practice it a bit :3

#

my d1 solution is a mess lol

zealous jungle
#

it's mostly the same except some parts that you need to do manually

#

like splitting into lines

pseudo quest
zealous jungle
#

though you can't slice manually in const

#

and have to use split_at

#

and for parsing int you can't just .parse, you need to do $int::from_str_radix

pseudo quest
#

oh catbucket

zealous jungle
#

it's same but different

#

and it makes compiler unhappy :3

#

runtime version compilation + run is faster than comptime version lol

smoky owl
#

new problem in three minutes

pseudo quest
#

new problem in 30 seconds

smoky owl
#

personal faster aoc ever

#

eleven minutes

pseudo quest
#

goddamn

#

i dont get it ngl

smoky owl
pseudo quest
#

i dont uiua

brittle void
smoky owl
#

but it's what happens when solving at speed

pseudo quest
#

whwre do the invalid ids come from 😭
how is 222220-222224 = 222222

brittle void
#

Oh day 2 is up

smoky owl
#

here is also my entire day 1

smoky owl
#

it's "all numbers from 222220 to 222224"

#

which contains 222222, which is invalid

smoky owl
pseudo quest
#

ooh sorry

#

missed the part where it said ID ranges

zealous jungle
#

or is there a loop too

smoky owl
#

to show the symmetry better

#

the first image had "multiple then undo the multiplication" in it 😌

smoky owl
zealous jungle
#

to step once and check zero

smoky owl
smoky owl
#

like

#

there's not more code

#

that's the whole thing

#

there's not a loop in the code

zealous jungle
#

i don't understand uiua lol

brittle void
#

wtf part 2

zealous jungle
#

so i don't know what those symbols mean

smoky owl
smoky owl
#

scan

#

If this is the input: [50 ¯68 ¯30 48 ¯5 60 ¯55 ¯1 ¯99 14 ¯82]

#

After \+ (scan add), we have [50 ¯18 ¯48 0 ¯5 55 0 ¯1 ¯100 ¯86 ¯168]

zealous jungle
#

second day needs to count all passes through 0

#

not second day

#

second stage

#

anyway, my solution for both steps of day 2
||```rust
const INPUT: &str = include_str!("../inputs/2");

pub fn main() {
let mut twice_sum = 0;
let mut many_sum = 0;

for range in INPUT.split(',') {
    let (start, end) = range.split_once('-').unwrap();
    let start: usize = start.parse().unwrap();
    let end: usize = end.parse().unwrap();

    for v in start..=end {
        if v == 0 {
            continue;
        }
        let width = v.ilog10() + 1;
        'twice: {
            if width % 2 == 1 {
                break 'twice;
            }

            let power = 10usize.pow(width / 2);

            let left = v / power;
            let right = v % power;

            if left == right {
                twice_sum += v;
            }
        }
        'many: {
            'subwidth: for subwidth in 1 ..= width / 2 {
                if width % subwidth != 0 {
                    continue;
                }

                let repeats = width / subwidth;
                if repeats <= 1 {
                    continue;
                }

                let power = 10usize.pow(subwidth);

                let number = v % power;
                let mut rest = v / power;
                for _ in 1..repeats {
                    let rep_number = rest % power;
                    if rep_number != number {
                        continue 'subwidth;
                    }
                    rest /= power;
                }

                many_sum += v;

                break;
            }
        }
    }
}

println!("{twice_sum}, {many_sum}");

}

#

yay

smoky owl
#

so i scan, but after expanding every number into units

#

so 10 would become 1 1 1 1 1 1 1 1 1 1 1

#

then you cannot miss a zero

zealous jungle
#

oh god

smoky owl
#

it's instantaneous to run

#

surprisingly

zealous jungle
#

go write a uiua to rust macro lol

#

to not write that much

smoky owl
brittle void
#

I haven't even written any code yet

smoky owl
#

i keep forgetting what the problem is oh my god

#

right, repeated chunks in ranges

smoky owl
brittle void
#

Like how to, imperatively

smoky owl
#

oh, hm

brittle void
#

How could I even catch the 824 824 824

smoky owl
#

like
||

let l = input.len();
for d in divsors(l):
  let c = chunk(input, d);
  if c.deduplicate().length() == 1: return invalid

return valid

||

zealous jungle
#

oh wow

#

it took 80 seconds to compute the constant-time solution

#

runtime is 0.4s for everything

zealous jungle
#

no, the whole input

smoky owl
#

wdym constant-time solution?

zealous jungle
#

in a const fn

smoky owl
#

whar

zealous jungle
#

through poor miri

smoky owl
#

oh 😭

zealous jungle
#

and it spits out 5 warnings about const evaluation taking too long

#

also you have to code like a caveman

#

no for loops, no find in string

#

also is aoc website half dying for anyone else

#

it throws ssl errors for me sometimes

smoky owl
smoky owl
wild otter
#

whatever

#

i was trying to find a solution that isnt just the dumbest loop in existence

#

but i failed, and wrote the dumbest loop

wild otter
#

whats with the labels

#

so overcomplicated

zealous jungle
#

separate first and second part of the solution

smoky owl
wild otter
#

in part 1 i did math
in part 2 i did strings

#

||```rs
fn count_invalid(start: u64, end: u64) -> u64 {
(start..end + 1)
.filter(|&num| {
let half_divisor = 10u64.pow(num.ilog10().div_ceil(2));
num / half_divisor == num % half_divisor
})
.sum()
}
fn count_invalid_part_two(start: u64, end: u64) -> u64 {
(start..end + 1)
.filter(|&num| {
if num < 10 {
return false;
}
let num_string = num.to_string().into_bytes();
for chunk_size in (1..num_string.len().div_ceil(2) + 1).rev() {
let mut iter = num_string.chunks(chunk_size);
let first = iter.next().unwrap();
if iter.all(|c| c == first) {
return true;
}
}
false
})
.sum()
}

smoky owl
#

x..y+1 instead of x..=y 😔

wild otter
#

=y is slower

zealous jungle
#

that much slower?

wild otter
#

no

#

but why write slower code on purpose

#

it adds an extra check every iteration or something like that

zealous jungle
#

a couple nanoseconds won't matter if your runtime is in milliseconds

wild otter
#

if it cant prove y is not u64::MAX

smoky owl
#

wdym slower

#

does it not change < for <=

zealous jungle
#

no

wild otter
#

it cant

smoky owl
#

wdym it cant

zealous jungle
#

it has a bool for if it has emitted the last element or not

wild otter
#

x..=y cant be optimized to x..y+1 if it cant prove y is not u64::MAX

smoky owl
#

aren't they identical

wild otter
#

no

zealous jungle
#

y+1 may overflow

wild otter
#

^

smoky owl
#
if curr < end { yield Some(curr) }
if curr <= end { yield Some(curr) }
smoky owl
#

in the next implementation of RangeInclusive

#

is that not a thing

zealous jungle
#

same thing, you need to have the next value

wild otter
#

ill find the full explantion brb

zealous jungle
#

which will overflow if the end is MAX

smoky owl
wild otter
#

its basically rust saving you from a cpp mistake

zealous jungle
wild otter
#

im still not fully convinced there isnt a better way

#

is there anything wrong with turning for i in x..=y{thing(i)} into for i in x..y{thing(i)} thing(y)?

zealous jungle
#

more code

wild otter
#

okay but it should be faster than current x..=y

zealous jungle
#

also ranges may not be directly used in for loops

zealous jungle
#

they're iterators

wild otter
#

true

zealous jungle
#

though for iterator range specifically there should be a better way

smoky owl
# smoky owl hmm

fair enough
i guess adding another bool to keep track of if its done would be too much overhead

wild otter
#

its an almost unmeasurable difference usually, but its only 1 more character to do it the fast way

#

so thats what i do

#

it still feels like the stupid solution

#

surely theres a more mathy way to go about it

zealous jungle
#

my mathy way is slower than yours in release lol

#

120ms for the second part

#

though you probably just have a faster clock cpu

#

120ms at 3.6 ghz

wild otter
#

no, i mean something that doesnt go through each number in the ranges

#

because usually part 1 is like
"here are 1000 numbers to do checks on"

#

and part 2 is "oops we meant 1000000000000000"

#

and anyone who did part 1 the naive way gets fucked

wild otter
#

im not even doing multithreading this year

zealous jungle
#

just 2 million

wild otter
#

i know that this time its different

#

but i was expecting

#

also every person gets a different input

#

so its not exactly that for everyone

zealous jungle
#

oh interesting

wild otter
#

sometimes you might get "you got the answer to someone elses input!" when you submit

zealous jungle
#

that's why it throws 400 bad request when i tried wgeting it

wild otter
#

mhm

#

aoc-cli requires your cookie to fetch stuff

#

the owner of the site really cares about people cloning it for some reason

zealous jungle
#

i wonder if there's an automatic bot that gives each day's problem to an llm then tries to compute the solution

wild otter
#

yes

#

thats why i think the global leaderboard is gone

#

llms ruined it

wild otter
#

it took like 30 seconds to add rayon

#

||```rs
pub fn part_one(input: &str) -> Option<u64> {
let mut input = input.as_bytes();
let invalid = AtomicU64::new(0u64);
rayon::scope(|s| {
while !input.is_empty() {
let (start, rem) = fast_parse::<u64>(input);
let (end, rem) = fast_parse::<u64>(&rem[1..]);
input = &rem[1..];
let invalid_ref = &invalid;
s.spawn(move |_| count_invalid(start, end, invalid_ref));
}
});
Some(invalid.load(Relaxed))
}

fn count_invalid(start: u64, end: u64, invalid_counter: &AtomicU64) {
invalid_counter.fetch_add(
(start..end + 1)
.filter(|&num| {
let half_divisor = 10u64.pow(num.ilog10().div_ceil(2));
num / half_divisor == num % half_divisor
})
.sum(),
Relaxed,
);
}

brittle void
wild otter
#

i have never seen a separate AI leaderboard

#

every day there were those 5s clears that were obvious cheaters

#

you probably mean some private leaderboard someone made for a subreddit or something

#

to be fair, the official response doesn't mention llms

#

thats my interpretation for part of the reason

#

What happened to the global leaderboard? The global leaderboard was one of the largest sources of stress for me, for the infrastructure, and for many users. People took things too seriously, going way outside the spirit of the contest; some people even resorted to things like DDoS attacks. Many people incorrectly concluded that they were somehow worse programmers because their own times didn't compare. What started as a fun feature in 2015 became an ever-growing problem, and so, after ten years of Advent of Code, I removed the global leaderboard. (However, I've made it so you can share a read-only view of your private leaderboard. Please don't use this feature or data to create a "new" global leaderboard.)

brittle void
# wild otter llms ruined it

What happened to the global leaderboard? The global leaderboard was one of the largest sources of stress for me, for the infrastructure, and for many users. People took things too seriously, going way outside the spirit of the contest; some people even resorted to things like DDoS attacks. Many people incorrectly concluded that they were somehow worse programmers because their own times didn't compare. What started as a fun feature in 2015 became an ever-growing problem, and so, after ten years of Advent of Code, I removed the global leaderboard. (However, I've made it so you can share a read-only view of your private leaderboard. Please don't use this feature or data to create a "new" global leaderboard.)

wild otter
#

i literally just pasted this

brittle void
#

Your new messages didn't load before I pasted that :>

wild otter
#

anyway

#

since llms are faster and can be artificially delayed
it is trivial to slip through any attempt to get rid of them

#

assuming its a day they have a decent chance of solving

#

i know they one shot older ones, but solutions for those are in the training set

brittle void
#

Bleh can't see any old leaderboards without a login which I cba to do on mobile

rapid pumice
#

my part 2 runs in 300 ms

#

:')

smoky owl
#

Mine runs in twelve 😌

#

||twelve seconds, that is||

rapid pumice
#

I love abusing rust nightly

#
const unsafe fn ascii_to_usize(src: &'static [Char]) -> usize {
    unsafe { usize::from_ascii(src.as_bytes()).unwrap_unchecked() }
}```
#
pub const INPUT: &[Char] = unsafe { include_str!("./input.txt").as_ascii_unchecked() };```
#
debug_assert!(range.0.iter().all(|c| c.to_char().is_numeric()));
debug_assert!(range.1.iter().all(|c| c.to_char().is_numeric()));
// SAFETY: Asserted that input only uses numbers :^)
unsafe { (ascii_to_usize(range.0), ascii_to_usize(range.1)) }```
it can have a little safety
as a treat
pseudo quest
rapid pumice
#

???

#

wild

pseudo quest
#

tbf it is the opposite of optimization and has a bunch of dead code because

#

i haven't finished

rapid pumice
#

damn

wild otter
#

all the ranges in the input are fairly small

pseudo quest
#

nvm it was just a weird read

#

its around 50ms

latent heathBOT
#

we are about to cry why isn't our solution working TwT day1p2
||

input() -> 
    {ok, IOdev}=file:open("input", read),
    input(IOdev).
input(IOdev) -> 
    receive 
        close -> file:close(IOdev);
        PID -> PID ! file:read_line(IOdev), input(IOdev)
    end.


exec_line([$L|Tl], Total) -> 
    {Clicks, _}=string:to_integer(Tl),
    (100 + (Total-Clicks)) rem 100;
exec_line([$R|Tl], Total) -> 
    {Clicks, _}=string:to_integer(Tl),
    (Total + Clicks) rem 100.

    
exec_line_rev(Data=[Mov|N], Position) ->
    {Diff,_} = string:to_integer(N),
    Revs=case Mov of
        $R -> floor((Diff+Position) / 100);
        $L -> if 
            (Diff > Position) and (Position =/= 0) -> floor(abs(Diff-Position) / 100) + 1;
            (Diff > Position) and (Position == 0) -> floor(abs(Diff-Position) / 100);
            (Diff rem 100) == Position -> 1;
            true -> 0
        end
    end,
    NewPos=exec_line(Data,Position),
    {Revs, NewPos}.


calc_crossed_zeroes() ->
    put(file,spawn(fun() -> input() end)),
    calc_crossed_zeroes(50,0).
calc_crossed_zeroes(Position, Count) ->
    get(file) ! self(),
    {Revolutions, NewPos}=receive
        eof -> 
            get(file) ! close,
            io:format("~nCount ~p~n", [Count]),
            exit(0);
        {ok, Data} -> 
            Revs=exec_line_rev(Data, Position),
            io:format("~p -> ~p revs:~p~n", [Data, exec_line(Data,Position), Revs]),
            Revs;
        Error -> io:format("~nError: ~p~n", [Error])
    end,
    calc_crossed_zeroes(NewPos, Count+Revolutions).

||
ignore the terrible io handling :3

rapid pumice
#

what's with the terrible io handling :3

latent heathBOT
#

the thread holding the file handle isn't the one that automatically closes it on termination but waits for the get(file) ! close message being sent <w>

#

by dr.jaska's solution we are lowballing it by a grand total of 38 edge cases TwT

latent heathBOT
#

we cheated TwT why the fuck isn't it working why can't we get the fucking math right what edge cases are we missing >:c

#

||

exec_line_rev(Data=[Mov|N], Position) ->
    {Diff,_} = string:to_integer(N),
    NewPos=exec_line(Data,Position),
    Revs=case Mov of
        $R -> floor((Diff+Position) / 100);
        $L when Diff < Position -> 0;
        $L when (Diff > Position) and (NewPos =/= 0)->
            floor((Diff-Position) / 100);
        $L when (Diff >= Position) ->
            floor((Diff-Position) / 100) + 1
    end,
    {Revs, NewPos}.

||

#

about to Crash The Fuck Out

#

@brittle void could you explain how your solution works for day1p2?

#

anyone 🥺

smoky owl
rapid pumice
#

I understand almost nothing

#

why are you dividing by 100?

#

gwah

smoky owl
#

||for each divisor of the length d, chunk the input into chunks of size d. if all chunks are equal, it's invalid||

smoky owl
latent heathBOT
#

what the helly is d meant to be TwT

rapid pumice
#

not day 2

smoky owl
#

checks what day 1 was

rapid pumice
#

the safe dial

smoky owl
#

right!

#

my approach might not work in an imperative language

latent heathBOT
#

-# @rapid pumice why are you dividing by 100?
to get how many times the operation "rolls over" zero OwO

smoky owl
#

so
||
you've got the input like -68 -30 48 -5 ...
i expand this out to a long sequence of -1 and 1, then do a scan (+)
||

#

then you just count the zeros

latent heathBOT
#

killing you /lh

wild otter
#

cursed solution

smoky owl
#

you might think this will be absurdly slow, but it was pretty instant
||the numbers are pretty small||

smoky owl
rapid pumice
smoky owl
wild otter
#

cursed language

smoky owl
#

the only difference between the days is the multiply versus the backkeep

#

the backkeep is what does the ||expansion||

#

multiply, uh, multiplies instead

#

so you only check the exact values it lands on

#

(they both act on the signs and the magnitudes)

rapid pumice
#

I'm confuse :3

latent heathBOT
#

-# @rapid pumice I think the last floor has to be a ceil?
oh why? the floor is to get the "whole" part of the division, if we ceil we get a skewed result even in the example input

wild otter
#

applying each line in chunks up to 100?

#

no, its doing it all at once

#

so many cases

latent heathBOT
#

-# @wild otter what is this even doing
it's trying to calculate how many times a operation has the thingy go over zero :3

rapid pumice
#

I was thinking of how weird ceil, floor, trunc, and round are

wild otter
#

you can just see how i did it

smoky owl
#

🫵 bracket typo

latent heathBOT
#

-# @wild otter so many cases
||```
first case is stuff like 5-3, never goes over zero
second case is for stuff like 53-100
third case is for stuff that lands on zero 53-153

wild otter
#

i just had one simple division
and a correction of one for the negative case

smoky owl
#

three hundred lines

latent heathBOT
#

-# @smoky owl that is so much code oh my god
exacly what i was thinking OwO

wild otter
#

the solutions itself is like 20

smoky owl
#

ooo it has the visualizer too!

#

ok that's p neat

wild otter
#

i kept the important at the top

wild otter
#

i modifier this template repo to also have a tui flag

smoky owl
#

i should try to make one too

wild otter
#

instead of just a solve flag

#

i posted a gif yesterday

#

very basic

#

first time using ratatui

smoky owl
#

hmm

#

i'm not sure how to draw a line, actually

wild otter
#

canvas

#

like stuffing an svg into html when you need something complex

#

i still dont fully understand how ratatui works

latent heathBOT
#

what is .rem_euclid?

wild otter
#

they have a weird separation between the coordinates and the buffer itself

wild otter
#

as in, it works on negatives as expected

latent heathBOT
#

huh

wild otter
#

-4 % 3 is -1

#

-4 rem euclid 3 is 2

rapid pumice
#

ohhh

#

that would have been useful to know yesterday

#

lmao

wild otter
#

a rem euclid b = ((a%b)+b)%b

smoky owl
#

(fun fact, uiua's modulo does rem_euclid)

wild otter
#

good

#

im sure theres a hardware reason for doing it the way it is

smoky owl
#

my main documentation contribution was realizing that, then adding a snippet to get standard %

rapid pumice
#

meanwhile

latent heathBOT
#

this is exactly what i'm doing but with num and dial switched so i don't have to deal with doing abs on anything TwT

let virtual_dial = dial - num;
let passes = virtual_dial / 100;
#

why isn't mine working 😭

wild otter
#

are you correcting for the negative case

#

when you go from positive to negative the division will be off by 1

smoky owl
latent heathBOT
#

only when the new position doesn't land on 0 tho right?

wild otter
#
        if virtual_dial <= 0 && dial > 0 {
            passes += 1;
        }
#

is what i used

latent heathBOT
#
$L when (Diff > Position) and (NewPos =/= 0)->
    floor((Diff-Position) / 100);
$L when (Diff >= Position) ->
    floor((Diff-Position) / 100) + 1

this is that no? 🥺

#

wait

wild otter
#

i dont really know this language so
dunno

#

youre not accounting for when you start at 0 i think

latent heathBOT
#

is that dial>0 because yours can be negative or because you didn't wanna type dial != 0?

wild otter
#

!=0 would also work

#

dial is always 0-99

#

virtual dial can do whatever

latent heathBOT
#

i think i found the problem -w-

wild otter
#

i dont see the issue

latent heathBOT
#

that last line is the dial set at 1, the comand being L2, and the result not being 1

latent heathBOT
#

that is counting the times it clicks zero not the dial

#

should be at least OwO

latent heathBOT
#

because |a-b|=|b-a| and i don't want to make my life harder by having to think about the sign of variables :3

wild otter
#

i dont think thats going to work

latent heathBOT
#

in fact it isn't TwT

rapid pumice
#

oh

#

I was confused about that when you mentioned it earlier

#

but presumed I misunderstood something

#

you can, in fact, not just switch the bigger and smaller number in subtraction

wild otter
#

it will give you they correct amount of passes, but only once

latent heathBOT
#

?

rapid pumice
#

what?

wild otter
#

em

latent heathBOT
#

-# @rapid pumice you can, in fact, not just switch the bigger and smaller number in subtract…
it's just for the sign of the result

rapid pumice
#

no like

#

you don't want to use abs

#

you should not be using abs nor swapping the order

#

why would you want to use abs

wild otter
#

i tried something similar at the some point
keep it positive, convert left movement to right movement from (100-dial)

latent heathBOT
#

because i'm trying to calculate the number of revolutions not the final dial position TwT

wild otter
#

you get the correct amount of passes only on the first line

#

anyway

#

just do normal math

#

negative numbers exist

#

make sure your language does rem euclid

rapid pumice
smoky owl
#

(my entire computation uses integers then does mod100 at the end)

rapid pumice
#

because it won't go from positive to negative uhhh

#

ever?

latent heathBOT
#

what

rapid pumice
#

if you take abs?

#

the thing you'd have to abs would be the result of the floor or division

#

not of the subtraction

latent heathBOT
#

my reasoning is: Diff/100 is how many revolutions happen if the thing starts at zero, Diff-Position is the NewDiff that corrects the original to have it beheave like it's starting at zero but looses the click for going from Position to 0 so you need to add 1 to NewDiff/100 to get the times it crosses 0.............. but it doesn't fucking workkkkkkkk

rapid pumice
#

Diff+Position, no?

craggy birchBOT
#

no because we are going left

#

that is the case for going right

rapid pumice
#

so diff is the amount of clicks you're turning left?

craggy birchBOT
#

yes

wild otter
#

that does sound okay

#

a bit overcomplicated
but should work

#

do you pass the example at least

craggy birchBOT
#

not anymore TwT

#

we fiddled too much let me fix at least the example

#

ok now we do

#

and we are back at missing 38 edge cases from the proper input -w-

wild otter
#

probably cases where you stopped at 0

craggy birchBOT
#

OH!

#

it changed nothing -w-

rapid pumice
#

no?

craggy birchBOT
#

wait let me resend because idk wtf that was

rapid pumice
#

or is NewPos 100

craggy birchBOT
#

||```hs
Revs=case Mov of
$R -> floor((Diff+Position) / 100);
$L when Diff < Position -> 0;
$L when (Diff >= Position) and ((Position=/=0) or (NewPos==0)) ->
floor((Diff-Position) / 100)+1;
$L when (Diff > Position) ->
floor((Diff-Position) / 100);
$L when (Diff == Position) -> 1
end,

rapid pumice
#

ok

craggy birchBOT
#

the or NewPos==0 is redundant

#

or at least didn't get us any closer to the proper solution

smoky owl
#

(i am trying to make the visualizer, but it seemed wrong)
(i made the pixels persist and got this shape)

rapid pumice
#

@mild temple :v

craggy birchBOT
#

nope, straight from the aoc page :3

rapid pumice
#

gwuh...

craggy birchBOT
#

i'm manually stepping at each line to see where it fucks up cause this is too much

smoky owl
#

it should be fine

#

but it follows a very odd path

#

thank you discord for not embedding apngs

craggy birchBOT
#

the dial is going negative????????????????????????????

#

FUCK

#

I FORGOT THE ABS IN THE FUCKING exec_line() FUNCTION WHEN I SIMPLIFIED IT AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

rapid pumice
#

did that fix it?

smoky owl
#

they frogor the abdominals,,,

craggy birchBOT
#

nope TwT, we now just miss 33 passes

#

instead of 38.....

rapid pumice
#

here have this combination input/output/debug file I just hacked together

#

ignore the start which I forgor has the cargo output

#

:Godo:

#

squint

#

L38 Hit 0 1 times current: 94

#

what

wild otter
#

did you guys see the formula someone posted on reddit

rapid pumice
#

no

rapid pumice
#

-> Sum function
literally just for loop >:)

wild otter
#

sure but it doesnt iterate on the range

#

it takes logarithmic time in relation to the upper range

rapid pumice
#

oh gwuh

wild otter
#

(+another logarithmic in relation to the lower)

#

correction, log squared

rapid pumice
#

have better file where I don't forget to print half the lines

#

@mild temple have a source of truth you can compare your output against

#

just check when your thing doesn't line up with mine and you'll know what's wrong in theory :^)

craggy birchBOT
#

thank you <3 we are awking your output to ours so we can diff the files and see where shit is the problem

rapid pumice
#

yeag I made sure to format it to make that easy

#

all aligned columns and such

craggy birchBOT
#

you hit the problem in the head, i'm missing about 800 from your example !

brittle void
rapid pumice
#

no?

#

that's mine

brittle void
rapid pumice
#

yes?

#

right there

#

in the file

brittle void
#

But 👍

craggy birchBOT
#

hmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm

rapid pumice
#

:)

#

717-15%100 -> 2

#

so you're saving the wrong number somewhere

craggy birchBOT
#

because (15-717) mod 100 is -2.... which should be 100-2 in the wheel representation

rapid pumice
#

yes

#

which is what I was confused about earlier when you talked about subtracting the position from the difference instead of the other way around

#

your problem wasn't in the 0 counting

#

it was when setting NewPos, seems like

#

which would explain why you couldn't find the mistake there

craggy birchBOT
#

yeah

#

we did it...................................................................................................................................................................................................................

#

we hate it

#

||```hs
exec_line([$L|Tl], Total) ->
{Clicks, _}=string:to_integer(Tl),
if
(Total >= Clicks) or (Clicks rem 100 == Total) ->
Total - (Clicks rem 100);
true -> 100-(abs(Total-Clicks) rem 100)
end;
exec_line([$R|Tl], Total) ->
{Clicks, _}=string:to_integer(Tl),
(Total + Clicks) rem 100.

this is the monster that saved us
#

horrible thing

#

a blight to the eye

#

not worth the 2+ hours we spent on this problem

#

thank you so much for the help @rapid pumice and @wild otter <3

rapid pumice
#

ew <3

smoky owl
#

(what language is this, again?)

craggy birchBOT
#

erlang

smoky owl
#

this is what erlang looks like?

#

wild,,,

pliant tangle
#

Did day 2 in Haskell, which I haven't touched since 2nd year of uni hah

#

I might try and do a different lang every day

zealous jungle
#

do it in typescript type system /j

#

or rust type system

brittle void
#

do it in brainfuck

zealous jungle
#

ooh the snow in the day selector art is generated new on refresh

zealous jungle
#

day 3!

zealous jungle
#

done it :3

#

funny that code was easy to extend to part 2

#

and i think my algorithm is O(2*input.len()) lol

#

and it takes 1.5 seconds for miri to run the const time solver

#

way better than yesterday's 80 lol

zealous jungle
pseudo quest
#

i should have collected into a vec

wild otter
#

cant solve part 1 for some reason

#

🤔

#

passes example, fails real
no idea why

#

the result from every line i manually checked so far is correct

zealous jungle
#

how are you solving it?

#

the principle

wild otter
#

||scan through each line, if digit bigger than current tens digit, set it as tens digit and the following digit as ones digit, otherwise compare to ones digit and pick biggest||

#

||rs pub fn part_one(input: &str) -> Option<u64> { let mut input = input.as_bytes(); let mut sum = 0u64; let mut lines = 0u64; while !input.is_empty() { println!("new line"); lines += 1; let mut max_tens = input[0]; let mut max_ones = input[1]; input = &input[2..]; while input[0] != b'\n' { let digit = input[0]; if digit > max_tens && input[1] != b'\n' { max_tens = digit; max_ones = input[1]; input = &input[2..]; println!("{}{}", max_tens - b'0', max_ones - b'0'); continue; } else if digit > max_ones { max_ones = digit; println!("{}{}", max_tens - b'0', max_ones - b'0'); } input = &input[1..]; } // skip \n input = &input[1..]; sum += max_tens as u64 * 10 + max_ones as u64; } sum -= b'0' as u64 * 11 * lines; Some(sum) }||

#

prints are just debug

zealous jungle
#

why are you not using .lines() and making it harder for yourself?

wild otter
#

lines slow

#

im already used to parsing like this

zealous jungle
#

actually no nvm

wild otter
#

can you give me an input output pair to test

zealous jungle
#

from my long input?

wild otter
#

sure

#

ideally just full input file and the result for each line

zealous jungle
wild otter
#

thanks

zealous jungle
#

i think i ca nalso give you the indexes of the digits

#

need?

wild otter
#

7 differences

#

nah im good

#

ah i got it

#

if theres a new tens digit immediately followed by another new tens digit, the result is wrong

#

fuck, still wrong

#

ok part 1 done

#

i had the same mistake if it happened on the first pair of the line

#

i dont like this part 2

#

seems tedious

zealous jungle
#

||instead of two variables you'd do an array, in your case||

wild otter
#

I know

#

i just dont like it

#

im going for a different approach

#

this time with lines

zealous jungle
#

my algorithm was ||find first biggest digit in the line - few characters from the end so all digits fit, substr the line after it, search for the next digit in that line, repeat for all digits||

wild otter
#

not clicking for now, i have an idea how to do it nicely

wild otter
#

part 2 done

wild otter
zealous jungle
#

nice :3

wild otter
#

this might be faster than part 1 if i set it do 2 digits..

#

lets see

#

nope

#

oh wait

zealous jungle
#

your first implementation is more or less linear

wild otter
#

debug

#

sure its linear but its complicated

#

1 complicated pass vs simple 2 pass

zealous jungle
#

3

#

lines, tens, ones

wild otter
#

oh right

#

stupid lines

#

see its all its fault

#
Part 1: 16858 (18.0µs)
Part 2: 16858 (29.0µs)```
#
Part 1: 16858 (17.4µs)
Part 2: 167549941654721 (54.8µs)
#

i could combine the lines with the first max pass

#

probably makes it about as complicated as the normal single pass

zealous jungle
#

mine is around 90us for both solutions combined

#

interesting that const solver is slower by about 10us

#

well

#

probably because its line splitting is slow

#

literally scanning forward for a \n

wild otter
#

tried this:
||rs pub fn part_one(input: &str) -> Option<u64> { let mut input = input.as_bytes(); let mut sum = 0u64; let mut lines = 0u64; while !input.is_empty() { lines += 1; let mut tens = input[0]; let mut tens_index = 0usize; let mut curr_index = 0usize; while input[curr_index] != b'\n' { let digit = input[curr_index]; if digit > tens && input[curr_index + 1] != b'\n' { tens = digit; tens_index = curr_index; } curr_index += 1; } // go back and find the second digit let ones = *input[tens_index + 1..curr_index].iter().max().unwrap(); // skip to next line input = &input[curr_index + 1..]; sum += tens as u64 * 10 + ones as u64; } sum -= b'0' as u64 * 11 * lines; Some(sum) }||

#

its slower

#

a bit faster than setting part 2 to 2 digits

#

this is my part 2 btw
||rs pub fn part_two(input: &str) -> Option<u64> { const DIGITS: usize = 12; input .lines() .map(|line| { // println!("For {line}:"); let mut line = line.as_bytes(); let mut sum = 0u64; for digit in (0..DIGITS).rev() { let mut max = line[0]; let mut max_index = 0; // we need to reserve `digit` digits at the end of the line for the rest of the process for (index, &digit) in line[0..line.len() - digit].iter().enumerate() { if digit > max { max = digit; max_index = index; } } sum = sum * 10 + (max - b'0') as u64; line = &line[max_index + 1..]; } sum }) .sum::<u64>() .into() }||

zealous jungle
#

btw you can optimize biggest digit scan by stopping on 9

wild otter
#

i know

#

didnt try that yet

#

annoying to do in part 1 as well

zealous jungle
#

mine is more code lol
||```rust
const fn find_max_joltage(bank: &str, batteries: usize) -> usize {
let mut rest = bank;

let mut value = 0;

let mut i = 0;
while i < batteries {
    let remaining_batteries = batteries - i;
    let minimum_remaining = remaining_batteries - 1;
    let bank_search = rest.split_at(rest.len() - minimum_remaining).0;

    let digit = find_biggest_digit(bank_search).unwrap();

    rest = rest.split_at(digit.0 + 1).1;

    value = value * 10 + digit.1 as usize;

    i += 1;
}

value

}

const fn find_biggest_digit(s: &str) -> Option<(usize, u8)> {
let mut max_digit = None;

let mut i = 0;
while i < s.len() {
    let digit = s.as_bytes()[i];

    let digit = digit - b'0';

    max_digit = Some(match max_digit {
        None => (i, digit),
        Some((_, md)) if md < digit => (i, digit),
        Some(old) => old,
    });

    if digit == 9 {
        break;
    }

    i += 1
}

max_digit

}

wild otter
#

no improvement with 9 optimization

#

i should do the \n split in bytes

zealous jungle
#

not beating compiletime calculated result /j

#

unless you count compilation time too

#

then easily

#

since it takes more than a second to calculate in const

wild otter
#

this benchmark is also not very stable

#

whoever wrote this template didnt make it easy to benchmark

#

oh it doesnt do any benchmarking if you dont pass --time

#

it just runs once

#

why

    let bench_iterations =
        (Duration::from_secs(1).as_nanos() / cmp::max(base_time.as_nanos(), 10)).clamp(10, 10000);
#

oh, --time is just "do you want to benchmark"

smoky owl
#

my ass has no idea how to do P2

smoky owl
#

i've got a rough idea

pliant tangle
#

having never looked at Zig before today lol

#

It has a very interesting approach to heap allocation

#

I believe I have an O(m*2n) solution, where m is bank size and n is number of batteries

smoky owl
#

this is absolute ass oh my god

#

(Part2, Part1 is pretty good)

zealous jungle
#

i think the base at the north pole might not survive our decorating lol

zealous jungle
#

damn

#

part 2 generates nice patterns

#

looks like a map almost

#

also since the input is constant width, you only need to find the newline once

#

and then just assert that the line is still same width

#

would be cool if aoc had an ssh interface

#

since web ui is done like a terminal anyway

pliant tangle
#

Julia today

#

And boy did I pick the right language

latent heathBOT
#

gcc is betraying me because why is the number array beheaving as if it starts in the same location as the line_buffer

#

when passed as char* parameter

rapid pumice
#

char* are null-terminated, no?

latent heathBOT
#

not necessarily, only if you want to use them as string which for me number isn't, it is a simple array of chars

rapid pumice
#

then I lack context for being helpful :3

latent heathBOT
#

fair day2p2
||```C
void find_largest(char const * const line_buffer, num line_remaining_length, char* remaining_digits, num digit_position) {
if( digit_position==12 ) return;

for(num idx=0; idx<line_remaining_length; idx++) {
    if( remaining_digits[0] < line_buffer[idx] )
    {
        remaining_digits[0] = line_buffer[idx];
        for(num n_idx=1; n_idx<12; n_idx++) remaining_digits[n_idx]='0';
    }
    find_largest(line_buffer+idx, line_remaining_length-idx, remaining_digits+1, digit_position+1);
}
if( digit_position==0 )
{
    printf("\nLine: %s\n Joltage: ", line_buffer);
    for(int i=0; i<12; i++) printf("%d", remaining_digits[i]);
    printf("\n");
}

}

num get_line_joltage2()
{
char line_buffer[1024] = {0};

for(
    num idx=0, curr_char = (num)getc(input); 
    curr_char != '\n' && curr_char != EOF; 
    curr_char=getc(input), idx++)
{
    line_buffer[idx] = (char)curr_char;
}
printf("Line-first: %s; ", line_buffer);
num line_lenght = strlen(line_buffer);

char number[12];
memset(number, '0', 12);

find_largest(line_buffer, line_lenght, number, 0);


num final_number = 0;
for(num idx=0,dec_pow=12; idx < 12; idx++, dec_pow--)
{
    final_number += (number[idx]-'0') * ipow(10, dec_pow);
}
return final_number;

}

#

the idea is to have a recursive alg like this
||
start with the biggest digit, iterate over the line and keep the biggest number you find
for the next digit do the same starting from the next position of the previous digit
when you find a bigger digit put to zero all the next digits to avoid having then be at an earlier position than the current
||

#

the algorithm isn't the problem.... it's gcc overlapping arrays

#

it's not that either, i'm fucking up in the find_largest

#

fixed it, i was overruning the array while resetting the next digits to '0'

wild otter
#

I HATE 2D DAYS THAT DONT HAVE A BORDER

#

I DONT WANT TO HANDLE THE EDGES

#

finally finished the day

#

part 2 took a lot less time than part 1

#

because its ||just running part 1 multiple times||

zealous jungle
#

black magic

zealous jungle
#

and i = 0 and i = len + 1 are special edge cases

wild otter
#

thats even more out of bounds

#

for these you either handle the edges with a bounds check on every indexing operation, or you split it into multiple loops

zealous jungle
#

well for my solution it made sense to go 1 element past the end

wild otter
#

how did you handle neighbors on the first row?

rapid pumice
zealous jungle
#

it doesn't really makes sense going 1 before since it will overwrite zeros with zeros

rapid pumice
#

or am I misunderstanding

wild otter
#

must have a very different algorithm

zealous jungle
#

ig

wild otter
#

you either distribute your existence to your neighbors or count your neighbors

zealous jungle
#

||

fn solve_row(above: Option<&[u8]>, row: &mut [u8], below: Option<&[u8]>) -> usize {
    // [] sliding along, keeping track of counts
    //     i:    v
    // above: [x 0 1] 2 3 4 x
    // middl: [x 0 1] 2 3 4 x
    // below: [x 0 1] 2 3 4 x

    let mut counts = [[0usize; 3]; 3];

    let mut total_reachable = 0;

    for i in 0..row.len() + 2 {
        let rolls = if i == 0 \|\| i == row.len() + 1 {
            [false; 3]
        } else {
            let i = i - 1;
            let above = above.map(|s| s[i] == b'@').unwrap_or(false);
            let middle = row[i] == b'@';
            let below = below.map(|s| s[i] == b'@').unwrap_or(false);
            [above, middle, below]
        };

        for row in counts.iter_mut() {
            row.rotate_left(1);
            row[2] = 0;
        }

        for (roll, counts) in rolls.into_iter().zip(counts.iter_mut()) {
            if roll {
                counts[0] += 1;
                counts[1] += 1;
                counts[2] += 1;
            }
        }

        let mut reachable = false;
        if i > 1 && row[i - 2] == b'@' {
            let neighbours_with_center: usize = counts.iter().map(|c| c[0]).sum();
            if neighbours_with_center - 1 < 4 {
                reachable = true;
            }
        }

        if i > 1 {
            if reachable {
                // print!("x");
                total_reachable += 1;
                row[i - 2] = b'.';
            } else {
                // print!("{}", char::from_u32(row[i - 2] as u32).unwrap());
            }
        }
    }

    // println!();

    total_reachable
}
```||
wild otter
#

either way you risk out of bounds access

#

||troll

zealous jungle
#

omfg discord figure out your markdown

wild otter
#

add \ before each |

#

in the lambda

#

almost there

zealous jungle
#

that's or

wild otter
#

same thing

#

and it decided to write |

#

fun

#

whatever, it works

zealous jungle
#

i keep rolling counts

#

and check if previous column needs neighbor checking

wild otter
#

oh

#

you are doing the bounds check

#

exactly with that line with the or

#

thats my point

#

some AoC days have a border around the relevant grid

#

anyway

#

neat solution

zealous jungle
#

now i'm confused what i was thinking when writing it

#

it works though

#

so i won't touch it

#

ah

#

also for the first solution, with .lines(), i was processing rows one off

#

so i had a reference to the row below

#

was something like
||```rust
let mut prev_rows = [None; 2];

for line in input.lines().map(Some).chain([None]) {
if let Some(prev_row) = prev_rows[1] {
solve_row(prev_rows[0], prev_row, line);
}
prev_rows[0] = prev_rows[1];
prev_rows[1] = line;
}

craggy birchBOT
#

what a silly thing, who could have thought that the best time to recurse would be when you update the output

#

/sarcasm

#

ohhhhhhhhhhhhhhhhhhhh i wanna do day 4 with BQN but it's gonna suck so much asssss

#

if part2 isn't particualarly arraylanguageable i will cry

smoky owl
#

today was so niceee

smoky owl
#

my sol (unspoilered because yea)

smoky owl
#

D2 and D4 were super nice

smoky owl
smoky owl
rapid pumice
#

I still haven't opened AoC since day 2

#

:c

smoky owl
#

open it

rapid pumice
#

No I'm going to bed

smoky owl
#

do the solve-y solve

smoky owl
rapid pumice
#

It's eepy time o clock

wild otter
craggy birchBOT
#

with struggles

#

my biggest problem rn is idk how to get the diagonals of an element

wild otter
#

what are you having trouble with?

#

its the same as non-diagonals neighbors but 2 changes to the coordinate instead of 1

craggy birchBOT
#

........ i just realized i can iota the entire matrix and from that extract the x and y by using the rank of the input thank you B

#

that capital 'b' is supposed to be a '!' lol

smoky owl
smoky owl
#

But it is what it is

smoky owl
#

It's very array-y

#

Uses the classic Game of Life trick

wild otter
smoky owl
#

We check neighbors by rotating the array all right directions (so we get eight new arrays) and adding it up along the new axis

#

You can tell the rotation to not wrap, to instead insert whatever value you want

#

If you look at my solution, in Adjacents, you'll see something like
<purple square> 0 <rotation glyph>

#

This is that

smoky owl
craggy birchBOT
#

how do you rotate the matrix diagonally? im having a hard time figuring it out

zealous jungle
#

i'm spending time bugfixing the fast solution while slow solution isn't even that slow

#

and that's only the first part of the day

smoky owl
#

The length of the amount to rotate by is how deep that value acts

#

(the yellow glyph that's three horizontal lines and the right angle that's white right next to it are superfluous, they can just be deleted)
(I didn't know rotate was semi pervasive and was told when i shared my sol)

zealous jungle
#

part 2 was a simple one liner and solved in 0.42us after the first part

#

my day 5 algorithm is
||while reading ranges, read them in a BTreeMap::<start, end> and merge new ranges with existing ones, then for id checking it's trivial, search for the last start point before the id, then check if the range contains it ||

#

||first line is the new range to be added, second line is existing map of ranges, third line is new map of ranges||

brittle void
#

Wtf Discord do you know what SPOILERS are..?

#

In mobile's launchpad

zealous jungle
#

lol

wild otter
#

another easy day

#

O(N^2) algo, idc

#

||```rs
pub fn part_two(input: &str) -> Option<u64> {
let (mut fresh, _) = parse_fresh(input.as_bytes());
fresh.sort_unstable_by_key(|r| r.start);
// deduplicate ranges
let mut fresh_count = 0u64;
for i in 0..fresh.len() {
let (a, b) = fresh.split_at_mut(i + 1);
let this_range = &mut a[i];
if this_range.len() == 0 {
continue;
}
for other_range in b {
dedup(this_range, other_range);
}
fresh_count += this_range.len();
}
Some(fresh_count)
}

fn dedup(r1: &mut MyRange, r2: &mut MyRange) {
// r2 cant end before r1 starts because the ranges are sorted
if r1.end < r2.start {
return;
}
r1.start = (r1.start).min(r2.start);
r1.end = (r1.end).max(r2.end);
*r2 = MyRange {
start: u64::MAX,
end: u64::MAX - 1,
};
}

#

was missing a shortcircut:

#

||```rs
if this_range.end < other_range.start {
fresh_count += this_range.len();
continue 'outer;
}

#

only 5% in the O(N^2) part so whatever

#

parsing 69% and sorting 20%

zealous jungle
#

except it's not logn but an abomination that is usually smaller than logn

#

because ||it combines ranges while parsing them||

wild otter
#

i assume by N we mean amount of ranges

#

and M amount of item ids

zealous jungle
#

but then part 2 is free

#

yes

wild otter
#

sure but i time seperately

#

full parse in both

#

part 1 is N*M for me

#

could easily improve with some sorting and binsearch but meh

zealous jungle
#

my range parsing does dedup

#

though i'm using a btreemap and not in the most efficient way

#

there's no straightforward way to get the item before/after some key

wild otter
#

write your own btree

zealous jungle
#

meh

#

it works and fast enough for me

zealous jungle
pliant tangle
#

My approach was to ||construct a 10-ary tree with Boolean leaves, you check an ID is valid by walking the tree digit-wise||

smoky owl
#

hmm

#

i tried to do p2 the easy way (materializing everything) but it said no 😔

#

it won't let me cheat 😭

latent heathBOT
#

that is indeed a lot of MB

zealous jungle
#

just make a multiterabyte zram swap

#

it will only run for like what, 10 days?

wild otter
#

why 8 byte elements

#

you only need 1 bit

zealous jungle
#

at least not 16, like old d3d shaders do

#

or 64, i don't remember

smoky owl
wild otter
#

f..

smoky owl
#

(if the interpreter knows all values are ints less than 256, it'll store it as u8s, but it's not something the user has control over)

rapid pumice
#

uiua is just like javascript

smoky owl
wild otter
smoky owl
#

it's also got complex numbers

#

the types are:
number
complex
box
charac

#

err, it's all Array of

#

a scalar is still an array

wild otter
#

charac

#

why specifically that substring

rapid pumice
#

cuz casenc is weirdo

wild otter
#

was lazy with todays

#

solved part 2 by ||rotating the input||

#

||```rs
fn rotate_input(input: &[u8]) -> Vec<u8> {
let mut rotated = Vec::with_capacity(input.len());
let width = input.iter().position(|&c| c == b'\n').unwrap() + 1;
let height = input.len() / width;
for x in 0..width - 1 {
rotated.push(input[width * (height - 1) + x]);
for y in 0..height - 1 {
rotated.push(input[y * width + x]);
}
rotated.push(b'\n');
}
rotated
}

#

and then it makes the rest easy

zealous jungle
#

making an unhinged day 6 const solution

wild otter
#

cant you just write normal code in const

zealous jungle
#

yes but that's boring

wild otter
#

🤔

#

so its not const thats making it unhinged

zealous jungle
zealous jungle
wild otter
#

i did not expect part 2 to be this little behind part 1

wild otter
#

generators when

zealous jungle
#

or are you consting the input

wild otter
#

not sure

#

its a template from gh
pretty sure it reads before running

#

and then passes the &str

zealous jungle
#

ah

wild otter
#

yep, it first reads the entire file at runtime, and then passes to each and measures just the solve function

#

it does include parsing

zealous jungle
#

my times are about 78 and 43 us

#

with part 1 being lazy and collecting everything into vecs

zealous jungle
#

2.6 seconds to run const solutions

wild otter
#

should probably make these static

zealous jungle
#

if only RA could tell me what the values are for the real input

#

lmao

#

the constants were too big for vscode to show me

#

yea that might be a bit too much

#

terminal window is 4x the size of my monitor

#

that's just around 130kb of consts what do you mean big values

pliant tangle
#

I'm sure this isn't going to bite me in part 2

zealous jungle
#

oh god scratch

smoky owl
pliant tangle
#

it is done

zealous jungle
#

seven hours?????

latent heathBOT
#

my average solve time for a aoc day :3

zealous jungle
#

"why is time that big"

#

took 40 minutes to solve nice

wild otter
#

another easy day

#

after writing a 6.6us solution, i decided to test the brute force solution

#

||```rs
pub fn part_two(input: &str) -> Option<u64> {
let input = input.as_bytes();
let width = input.iter().position(|&c| c == b'\n').unwrap() + 1;
let start_index = width / 2 - 1;
debug_assert!(input[start_index] == b'S');
let jump_size = 2 * width;
Some(beam_splitter(input, start_index + jump_size, jump_size))
}

fn beam_splitter(input: &[u8], index: usize, jump_size: usize) -> u64 {
if index >= input.len() {
return 1;
}
if input[index] == b'^' {
beam_splitter(input, index + jump_size - 1, jump_size)
+ beam_splitter(input, index + jump_size + 1, jump_size)
} else {
beam_splitter(input, index + jump_size, jump_size)
}
}```||

#

trigger warning: its stupid

#

i stopped it after a few minutes

#

adding a cache: &mut HashMap<usize, u64> did lower it to 100us

#

this is my real solution btw
||```rs
pub fn part_two(input: &str) -> Option<u64> {
let input = input.as_bytes();
let width = input.iter().position(|&c| c == b'\n').unwrap() + 1;
let start_index = width / 2 - 1;
debug_assert!(input[start_index] == b'S');
let mut beams = vec![Beam {
col: start_index,
particles: 1u64,
}];
let mut next_beams: Vec<Beam> = vec![];
for layer in input.chunks_exact(width).step_by(2).skip(1) {
for &beam in beams.iter() {
if layer[beam.col] == b'^' {
// beam splits
if let Some(last_beam) = next_beams.last_mut()
&& last_beam.col == beam.col - 1
{
// there is a previous beam at that column
last_beam.particles += beam.particles;
} else {
next_beams.push(Beam {
col: beam.col - 1,
particles: beam.particles,
});
}
// right split can always be added
next_beams.push(Beam {
col: beam.col + 1,
particles: beam.particles,
});
} else if let Some(last_beam) = next_beams.last_mut() {
// no splitter
if last_beam.col == beam.col {
last_beam.particles += beam.particles;
} else {
next_beams.push(Beam {
col: beam.col,
particles: beam.particles,
});
}
}
}
swap(&mut beams, &mut next_beams);
next_beams.clear();
}
Some(beams.iter().map(|b| b.particles).sum())
}

zealous jungle
#

interesting

wild otter
#

the real or the stupid

zealous jungle
#

real

wild otter
#

isnt it kinda standard

#

another person i spoke with did something similar but hashmap instead of vec

zealous jungle
#

||my beams array was just a Vec<usize> as wide as the input width, with 0 being no particles||

wild otter
#

and hashset for part 1, while in part 1 i just had index instead of index and count

wild otter
#

probably faster

#

hmm, you do need to skip through the 0s in yours

#

but the flow is so simple

#

now i wanna try too

zealous jungle
#

||and i iterate over input bytes without skipping rows, lines, all that, if the reader hits a \n, it does the array swapping||

zealous jungle
#

not the beams

#

which is slower

#

actually maybe equal

#

||```rust

pub fn main() {

let start = Instant::now();

let line_width = INPUT.find('\n').unwrap();

let mut beam_timelines = vec![0; line_width];
let mut next_beam_timelines = vec![0; line_width];

let mut splits = 0;

let mut x = 0;
for b in INPUT
    .trim_end()
    .as_bytes()
{
    match b {
        b'S' => {
            next_beam_timelines[x] = 1usize;
        }
        b'^' => {
            if beam_timelines[x] > 0 {
                next_beam_timelines[x - 1] += beam_timelines[x];
                next_beam_timelines[x + 1] += beam_timelines[x];
                splits += 1;
            }
        }
        b'\n' => {
            x = 0;

            std::mem::swap(&mut beam_timelines, &mut next_beam_timelines);
            next_beam_timelines.iter_mut().for_each(|v| *v = 0);

            continue;
        }
        _ => {
            next_beam_timelines[x] += beam_timelines[x];
        }
    }
    x += 1;
}

let timelines: usize = next_beam_timelines.iter().copied().sum();

let time = start.elapsed();

println!("Part 1: {splits}");
println!("Part 2: {timelines}");
println!("Time: {:.02}us", time.as_secs_f64() * 1000000.0);

}```||

#

i also am not depending on input to be triangle shaped or that it skips every other line

#

but that would be even faster

#

or

#

split by lines and rayon it

#

also could probably be solved a bit backwards, ||for each point on line, find out how many particles will get to it from line above and splitters to the sides||

#

that should be rayon-able

wild otter
#

||rs pub fn part_two(input: &str) -> Option<u64> { let input = input.as_bytes(); let width = input.iter().position(|&c| c == b'\n').unwrap() + 1; let start_index = width / 2 - 1; debug_assert!(input[start_index] == b'S'); let mut beams = vec![0; width]; beams[start_index] = 1; let mut next_beams = vec![0; width]; for layer in input.chunks_exact(width).step_by(2).skip(1) { for (col, &content) in layer[..width].iter().enumerate() { let prev_beam = beams[col]; if content == b'^' { next_beams[col - 1] += prev_beam; next_beams[col + 1] += prev_beam; } else { next_beams[col] += prev_beam; } } swap(&mut beams, &mut next_beams); next_beams.fill(0); } Some(beams.iter().sum()) }||

zealous jungle
#

TIL about [T]::fill

wild otter
#

i noticed that you didnt use it

zealous jungle
#

oh boy

zealous jungle
#

one second faster with some parallelism

#

oh hell yeah

#

rayon 🔥

zealous jungle
#

(that thread was ||merging circuits by connections received from main thread and checked and calculated solutions||)

smoky owl
zealous jungle
#

probably 8x to time

wild otter
#

first "do you know your algorithms" day

zealous jungle
#

since cpu usage jumps to 100%

wild otter
zealous jungle
#

i don't lol

wild otter
#

easy if you know || kruskal's algorithm||

zealous jungle
#

well i thought that took time but apparently that was just 0.1%

wild otter
#

there are 2 well known algorithms for ||minimum spanning tree||
and the order this day asks for limits you to one of them

smoky owl
smoky owl
wild otter
#

||union|| is just the assumed api for ||sets|| in most algorithms

#

i wanna visualize it, it probably draws something

smoky owl
#

(including 3D)

wild otter
#

wdym array drawing

#

i need to draw the writes

smoky owl
wild otter
#

wires

smoky owl
#

i thought you meant the boxes and connections

wild otter
#

yes

#

connections

#

good thing i already wrote a template for ratatui

smoky owl
#

hmm
i'm not sure how that would be written comfortably

wild otter
#

actually, how would i even do that in 3d

#

if it was 2d it would be easy

#

ill just try 2d

zealous jungle
#

maybe

wild otter
#

never used bevy

#

too much work to start now

smoky owl
#

place the camera where it's looking from the vector that crosses (not exactly) the corner of a cube and project everything to it

zealous jungle
#

that would be alot of ram usage though

#

right?

#

funny that i ||later made a Vec<bool> to hold the info about made connections ||

#

||but didn't think to put the coords in it, coords are keys to it||

wild otter
zealous jungle
#

||Vec<(distance, index1, index2)>|| no?

wild otter
#

mhm

#

not bad at all for this scale

#

and you could improve it to linear memory

#

only store the best of each node at any given moment

#

and when you use it up, generate the next best for the node

#

i dont think theres anything here

smoky owl
#

i wonder what it'd look like if the straight line was superimposed, not bound by the grid

wild otter
#

what do you mean?

#

i just noticed its slowing down, but im not sure if slow to draw or its because there are so many already used connections in the heap

#

it was the heap

#

i did 1 frame per pop instead of 1 frame per new valid connection

smoky owl
#

it's jagged

#

because of the braille grid

wild otter
#

well its a terminal

#

i could zoom out

#

i lied

#

i dont know how

#

just zooming out the terminal doesnt work

#

anyway

#

probably still nothing even with fixed lines

smoky owl
#

and the distances become tinee

smoky owl
wild otter
#

not change the physical size on the screen

smoky owl
#

this day is

#

bullying me

#

i don't understand what it's asking

#

you seein this

#

this is the right answer for the sample, yes?

#

why do i get this after 19 pairs

zealous jungle
smoky owl
#

do i have to ignore idempotent joins??

zealous jungle
#

then print the answer

smoky owl
zealous jungle
#

for the first part

smoky owl
#

which is not yet correct

#

it needs nine more joinings

zealous jungle
#

is array on the right junctions vertically in circuits horizontally?

smoky owl
#

each "column" is a circuit

#

so the 862.. and the 984.. box share a circuit

zealous jungle
#

my first 10 joins are
||```
{162, 817, 812} -> {425, 690, 689}
{162, 817, 812} -> {431, 825, 988}
{906, 360, 560} -> {805, 96, 715}
{431, 825, 988} -> {425, 690, 689}
{862, 61, 35} -> {984, 92, 344}
{52, 470, 668} -> {117, 168, 530}
{819, 987, 18} -> {941, 993, 340}
{906, 360, 560} -> {739, 650, 466}
{346, 949, 466} -> {425, 690, 689}
{906, 360, 560} -> {984, 92, 344}

#

for the sample input

#

||```
{162, 817, 812} -> {425, 690, 689} [2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
{162, 817, 812} -> {431, 825, 988} [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
{906, 360, 560} -> {805, 96, 715} [3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
{431, 825, 988} -> {425, 690, 689} [3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
{862, 61, 35} -> {984, 92, 344} [3, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
{52, 470, 668} -> {117, 168, 530} [3, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
{819, 987, 18} -> {941, 993, 340} [3, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1]
{906, 360, 560} -> {739, 650, 466} [3, 3, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1]
{346, 949, 466} -> {425, 690, 689} [4, 3, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1]
{906, 360, 560} -> {984, 92, 344} [5, 4, 2, 2, 1, 1, 1, 1, 1, 1, 1]

#

same but with circuit counts

smoky owl
#

whar

#

oh

#

oh my god

#

it's symmetrical

#

it's 19 because it's double ten

#

😭

#

i hate it here /shitpost

#

exploding

smoky owl
#

P2 was a baby tweak to P1

#

awesome

#

this was a fun day!

zealous jungle
#

how fast is it :3

smoky owl
#

final code

smoky owl
zealous jungle
#

ah, you're sorting

wild otter
#

oops, i was spending most of the runtime on getting the square root of the distances,
not neccasary when all i do is compare them
now most of the time is building the binary heap

wild otter
zealous jungle
#

what is heap for

smoky owl
#

which does both dist(A, B) and dist(B, A)

wild otter
#

but now that i think about it

#

just sort would be fine

#

ill try

wild otter
smoky owl
smoky owl
# wild otter noob

i switched from table to tuples, which not only simplified the code, it made it correct 😌

smoky owl
#

by the way check out "euclidean distance" in uiua
substract, then, under squaring, add up

smoky owl
smoky owl
wild otter
#

the ordering seems weird

#

like

#

the addition is after squaring

#

but the small 2 is to the left

#

and theres also a random /??

smoky owl
#

the purple glyph is under, which is a dyadic ("two") modifier: it takes in two functions (squaring and add up)

#

all of that is one single unit

#

so the order of execution goes

  • subtract
  • what i just described (under square, add up)
#

(add up is /+)

smoky owl
#

(adds up along the main axis, but there's only one dimension here so w/e)

wild otter
#

hate

smoky owl
#

this is "squared euclidean distance"
add up after squaring after subtracting

wild otter
#

that makes a lot more sense

#

why is it so different

smoky owl