Skip Navigation

πŸ”’ - 2025 DAY 1 SOLUTIONS -πŸ”’

Day 1: Secret Entrance

Megathread guidelines

  • Keep top level comments as only solutions, if you want to say something other than a solution put it in a new post. (replies to comments can be whatever)
  • You can send code in code blocks by using three backticks, the code, and then three backticks or use something such as https://topaz.github.io/paste/ if you prefer sending it through a URL

FAQ

34 comments
  • Uiua

    Today's lesson: Never think that scanning the first 100 lines of input will give you a good understanding of it.

    Part 2 is really messy and could probably be much simpler, but I couldn't get the logic straight in my head otherwise.

     undefined
        
    "L68 L30 R48 L5 R60 L55 L1 L99 R14 L82"
    βŠœβ‹•βŠΈβ‰ @\sβˆ§βœβŠ‘β‹…@Β―βŠšβŠΈβŒ•"L"βˆ§βœβŠ‘β‹…@+βŠšβŠΈβŒ•"R"
    P₁ ← ⧻⊚=0\(β—Ώ100+)βŠ‚50
    P ← (
      βŠƒ(-Γ—100Γ—|/+)⌊÷100⌡⟜(⊸±) # Count and remove all over-rotations
      β‰βŠŸβ†˜Β―1⊸(\(β—Ώ100+)βŠ‚50)     # Take all positions and next moves.
      β–½βŠΈβ‰‘(β‰ 0⊒)                # Ignore any starting from zero
      +/+β†₯βŠƒ(<0|>100)≑/+       # Sum the pairs, check for passing zero.
    )
    Pβ‚‚ ← +βŠƒP P₁
    βŠƒ(P₁|Pβ‚‚)
    
      
    • Just had a look at the Uiua Discord, and Part 2 can be simplified a little...

       undefined
          
      "L68 L30 R48 L5 R60 L55 L1 L99 R14 L82"
      βŠœβ‹•βŠΈβ‰ @\sβˆ§βœβŠ‘β‹…@Β―βŠšβŠΈβŒ•"L"βˆ§βœβŠ‘β‹…@+βŠšβŠΈβŒ•"R"
      P₁ ← ⧻⊚=0\(β—Ώ100+)βŠ‚50
      Pβ‚‚ ← /+=0β—Ώ100\+β–½βŒ΅βŸœΒ±βŠ‚50
      βŠƒP₁ Pβ‚‚
      
        

      Sometimes I could just cry.

  • A straightforward day 1 for a Javascript / NodeJS solution.

    • Brute forcing it like this was my first thought - like they say: if it's stupid and it works, it's not stupid.

  • Rust

    Almost missed, that "the dial starts by pointing at 50".

    View on github

     rust
        
    const N: i32 = 100;
    
    fn parse_line(l: &str) -> (i32, i32) {
        let dir = match l.chars().next().unwrap() {
            'L' => -1,
            'R' => 1,
            _ => panic!(),
        };
        let dist = l[1..].parse::<i32>().unwrap();
        (dir, dist)
    }
    
    fn part1(input: String) {
        let mut pos = 50;
        let mut count0 = 0;
        for l in input.lines() {
            let (dir, dist) = parse_line(l);
            pos = (pos + dir * dist) % N;
            if pos == 0 {
                count0 += 1;
            }
        }
        println!("{count0}");
    }
    
    fn part2(input: String) {
        let mut pos = 50;
        let mut count0 = 0;
        for l in input.lines() {
            let (dir, dist) = parse_line(l);
            if dir == 1 {
                count0 += (pos + dist) / N;
            } else {
                count0 += ((N - pos) % N + dist) / N;
            }
            pos = (pos + dir * dist).rem_euclid(N);
        }
        println!("{count0}");
    }
    
    util::aoc_main!();
    
      
  • I agree with strlcpy -- computing was better in the 1980s. Let's try the version of the 1980s where Lisp Machines were going to power the AI future. It can't be any worse than the AI future we've got right now.

    (This is SBCL, not a Lisp Machine emulator, because I'm not that hardcore.)

     lisp
        
    (defun parse-line (line)
      (let ((sign (if (eql (char line 0) #\R) 1 -1))
            (number (parse-integer (subseq line 1))))
        (* sign number)))
    
    (defun read-inputs (filename)
      (let ((input-lines (uiop:read-file-lines filename)))
        (mapcar #'parse-line input-lines)))
    
    (defun rotate (pos rotation)
      (mod (+ pos rotation) 100))
    
    (defun main-1 (filename)
      (let ((rotations (read-inputs filename))
            (pos 50))
        (loop for rotation in rotations
              do (setf pos (rotate pos rotation))
              sum (if (= pos 0) 1 0))))
    
    (defun zero-crossings (pos rotation)
      (if (> rotation 0)
          (floor (+ rotation pos) 100)
          (let ((neg-pos (if (zerop pos) pos (- pos 100))))
            (- (ceiling (+ rotation neg-pos) 100)))))
    
    (defun main-2 (filename)
      (let ((rotations (read-inputs filename))
            (pos 50))
        (loop for rotation in rotations
              sum (zero-crossings pos rotation) into crossings
              do (setf pos (rotate pos rotation))
              finally (return crossings))))
    
      
  • Nim

    That was the rough first day for me. Part 1 was ok. For part 2 I didn't want to go the easy route, so I was trying to find simple formulaic solution, but my answer was always off by some amount. And debugging was hard, because I I was getting the right answer for example input.
    After 40 minutes I wiped everything clean and wrote a bruteforce.

    Later that day I returned and solved this one properly. I had to draw many schemes and consider all the edge cases carefully to come up with code below.

     nim
        
    type
      AOCSolution[T,U] = tuple[part1: T, part2: U]
    
    proc solve(input: string): AOCSolution[int, int] =
      var dial = 50
      for line in input.splitLines():
        let value = parseInt(line[1..^1])
        let sign = if line[0] == 'L': -1 else: 1
        let offset = value mod 100
        result.part2 += value div 100
    
        if dial != 0:
          if sign < 0 and offset >= dial or
             sign > 0 and offset >= (100-dial): inc result.part2
    
        dial = (dial + offset * sign).euclmod(100)
        if dial == 0: inc result.part1
    
      

    Full solution at Codeberg: solution.nim

  • Elixir

    Oh wow I'm late but that's because I decided to do this year in Elixir which I am not really experienced enough yet as it seems. Part 1 was ok but I didn't really put enough thought into Part 2 and got stuck for a while. However I got it done after a few failed tries and a few questions to AI on how to do things in Elixir.

     undefined
        
    #!/usr/bin/elixir
    
    defmodule SafeDial do
    
    def readInstructions(filename) do
        filename
        |> File.stream!()
        |> Stream.map(&String.trim/1)
        |> Stream.reject(&(&1 == ""))
        |> Stream.map(fn line ->
            case Regex.run(~r/^([A-Za-z])(\d+)$/, line) do
                [_, letter, number] -> {String.to_atom(letter), String.to_integer(number)}
                _ -> nil
            end
        end)
        |> Enum.reject(&(&1 == nil))
    end
    
    def evalInstructions(instructions) do
        initialPos = 50
        zeroCount = 0
        checkInstruction(instructions, initialPos, zeroCount)
    end
    
    defp checkInstruction(instructions, currPos, zeroCount) do
        case instructions do
            [] -> {currPos, zeroCount}
            [instruct | rest] ->
                nextPos =
                    case instruct do
                        {dir, step} when dir == :R and currPos + rem(step,100) > 99 ->
                            currPos + rem(step,100) - 100
                        {dir, step} when dir == :R and currPos + rem(step,100) > 99 ->
                            currPos + rem(step,100) - 100
                        {dir, step} when dir == :R ->
                            currPos + rem(step,100)
                        {dir, step} when dir == :L and currPos - rem(step,100) < 0 ->
                            currPos - rem(step,100) + 100
                        {dir, step} when dir == :L ->
                            currPos - rem(step,100)
                        _ -> raise "[ ERROR ] unkown instruction: #{inspect(instruct)}"
                    end
                newZeroCount = if nextPos == 0, do: zeroCount + 1, else: zeroCount
                checkInstruction(rest, nextPos, newZeroCount)
            other -> raise "[ ERROR ] unknown instruction: #{inspect(other)}"
        end
    end
    
    def evalInstructionsPart2(instructions) do
        initialPos = 50
        zeroCount = 0
        checkInstructionPart2(instructions, initialPos, zeroCount)
    end
    
    defp checkInstructionPart2(instructions, currPos, zeroCount) do
        case instructions do
            [] -> {currPos, zeroCount}
            [instruct | rest] ->
                {nextPos, zeroCount1} =
                    case instruct do
                        {dir, step} when dir == :R and currPos + rem(step,100) == 100 ->
                            {currPos + rem(step,100) - 100, zeroCount+floor(step/100)}
                        {dir, step} when dir == :R and currPos + rem(step,100) > 99 ->
                            {currPos + rem(step,100) - 100, zeroCount+floor(step/100)+1}
                        {dir, step} when dir == :R ->
                            {currPos + rem(step,100), zeroCount+floor(step/100)}
                        {dir, step} when dir == :L and currPos == 0 and currPos - rem(step,100) < 0 ->
                            {currPos - rem(step,100) + 100, zeroCount+floor(step/100)}
                        {dir, step} when dir == :L and currPos - rem(step,100) < 0 ->
                            {currPos - rem(step,100) + 100, zeroCount+floor(step/100)+1}
                        {dir, step} when dir == :L ->
                            {currPos - rem(step,100), zeroCount+floor(step/100)}
                        _ -> raise "[ ERROR ] unkown instruction: #{inspect(instruct)}"
                    end
                newZeroCount = if nextPos == 0, do: zeroCount1 + 1, else: zeroCount1
                checkInstructionPart2(rest, nextPos, newZeroCount)
            other -> raise "[ ERROR ] unknown instruction: #{inspect(other)}"
        end
    end
    
    end #module
    
    IO.puts "### PART 1 ###"
    instructions = SafeDial.readInstructions("input/day01Input.txt")
    {finalPos, zeroCount} = SafeDial.evalInstructions(instructions)
    IO.puts "final position:#{finalPos} zero count:#{zeroCount}"
    
    IO.puts "### PART 2 ###"
    instructions = SafeDial.readInstructions("input/day01Input.txt")
    {finalPos, zeroCount} = SafeDial.evalInstructionsPart2(instructions)
    IO.puts "final position:#{finalPos} zero count:#{zeroCount}"
    
      

    Edit: removed debug output

  • Haskell

    I was late to the part on this one and forgot to post my solution :3

     Haskell  
        
    import Data.List  
    
    readInput = map readMove . lines  
      where  
        readMove (d : ds) =  
          let n = read ds :: Int  
           in case d of  
                'L' -> -n  
                'R' -> n  
    
    part1 = length . filter ((== 0) . (`mod` 100)) . scanl' (+) 50  
    
    part2 = fst . foldl' count (0, 50)  
      where  
        count (z, p) d =  
          let (q, r) = (p + d) `divMod` 100  
              a = if p == 0 && d < 0 then -1 else 0  
              b = if r == 0 && d < 0 then 1 else 0  
           in (z + abs q + a + b, r)  
    
    main = do  
      input <- readInput <$> readFile "input01"  
      print $ part1 input  
      print $ part2 input  
    
      
  • Rust

     rust
        
    use std::{cmp::Ordering, fs, str::FromStr};
    
    use color_eyre::eyre::{Result, bail};
    
    const DIAL_NUMBERS: isize = 100;
    
    #[derive(Clone, Copy)]
    struct Dial(usize);
    
    impl Dial {
        fn new() -> Self {
            Self(50)
        }
    
        fn rotate(&mut self, rot: isize) -> usize {
            let pass_0s;
    
            let total = rot.wrapping_add_unsigned(self.0);
            match total.cmp(&0) {
                Ordering::Equal => {
                    pass_0s = 1;
                    self.0 = 0;
                }
                Ordering::Less => {
                    // Starting on 0 means we don't cross it
                    let started_0 = if self.0 == 0 { 1 } else { 0 };
                    pass_0s = 1 + (-total / DIAL_NUMBERS) as usize - started_0;
                    self.0 = (self.0 as isize + rot).rem_euclid(DIAL_NUMBERS) as usize;
                }
                Ordering::Greater => {
                    let full_turns = total / DIAL_NUMBERS;
                    pass_0s = full_turns as usize;
                    self.0 = (total - DIAL_NUMBERS * full_turns) as usize;
                }
            };
    
            pass_0s
        }
    
        fn sequence(&mut self, s: &str) -> Result<(usize, usize)> {
            let mut end_0s = 0;
            let mut pass_0s = 0;
    
            for l in s.lines() {
                let num = isize::from_str(&l[1..]).unwrap();
                let dir = l.bytes().next().unwrap();
                pass_0s += match dir {
                    b'L' => self.rotate(-num),
                    b'R' => self.rotate(num),
                    _ => bail!("b{dir} is an invalid rotation direction"),
                };
                if self.0 == 0 {
                    end_0s += 1;
                }
            }
    
            Ok((end_0s, pass_0s))
        }
    }
    
    fn parts(filepath: &str) -> Result<(usize, usize)> {
        let input = fs::read_to_string(filepath)?;
        let mut dial = Dial::new();
        let res = dial.sequence(&input)?;
        Ok(res)
    }
    
    fn main() -> Result<()> {
        color_eyre::install()?;
    
        let (p1, p2) = parts("d01/input.txt")?;
        println!("Part 1: {p1}");
        println!("Part 2: {p2}");
        Ok(())
    }
    
    
      

    I lost a lot of time struggling with edge cases in part two since I thought it was wasteful to run rem_euclid() (i.e. modulus) when I'd already got the information to do it more efficiently. While I got there in the end my final code was a bit ugly. This prettier, "unoptimised" version runs in 1.6ms, so I was being an idiot.

  • Python

    Solved part 1 in a matter of minutes, spent literal days tinkering with part 2 before resorting to brute force, just to finally get a correct result, which allowed me to fix the more optimised approach. Brute force still turned out surprisingly performant, scaling linearly in time with the rotation distance and sub-linearly in memory.

     python
        
    from itertools import accumulate
    from math import copysign
    from pathlib import Path
    from typing import List
    
    
    def parse_input(input: str) -> List[int]:
        return list(map(lambda s: int(f"{'-' if s[0] == 'L' else ''}{s[1:]}"), input.splitlines()))
    
    
    def part_one(input: str) -> int:
        return len(list(filter(lambda x: x == 0, accumulate(parse_input(input), lambda v, i: (i + v) % 100, initial=50))))
    
    
    def part_two(input: str) -> int:
        v, c = 50, 0
        for i in parse_input(input):
            c += i // (d := int(copysign(100, i)))  # full rotations
            count_underflow = v != 0  # was counted in previous iteration
            v += (i % d)  # remainder
            if count_underflow:
                c += abs(v // 100)  # under-/overflows
            if v == 0:
                c += 1
            v %= 100
        return c
    
    
    if __name__ == "__main__":
        input = Path("_2025/_1/input").read_text("utf-8")
        print(part_one(input))
        print(part_two(input))
    
    
      

    Brute force part 2

     python
        
    def part_two(input: str) -> int:
        from itertools import chain
        return len(list(filter(
            lambda x: x == 0,
            accumulate(chain(*[[int(copysign(1, i))] * abs(i) for i in parse_input(input)]), lambda v, i: (i + v) % 100, initial = 50)
        )))
    
      
  • C# ( c sharp )

     undefined
        
    using System.Collections;
    using System.Collections.Generic;
    
    namespace ConsoleApp1
    {
        public static class Program
        {
            public static void Part1()
            {
                var lines = File.ReadAllLines("C:\\Users\\aman\\RiderProjects\\ConsoleApp1\\ConsoleApp1\\input.txt");
                var dialReading = 50;
                int result = 0;
                foreach (var line in lines)
                {
                    if (dialReading == 0)
                    {
                        result += 1;
                    }
                    char dir = line[0];
                    int rotation =  int.Parse(line.Substring(1));
                    if (dir == 'R')
                    {
                        dialReading += rotation;
                        dialReading %= 100;
                    }
                    else
                    {
                        int diff = dialReading - rotation;
                        if (diff > 0)
                        {
                            dialReading -= rotation;
                            dialReading %= 100;
                        }
                        else
                        {
                            dialReading = dialReading + 100 - rotation;
                            dialReading %= 100;
                        }
                    }
                }
    
                Console.WriteLine(result);
            }
    
            public static void Part2()
            {
                var lines = File.ReadAllLines("C:\\Users\\aman\\RiderProjects\\ConsoleApp1\\ConsoleApp1\\input.txt");
                var dialReading = 50;
                int result = 0;
                foreach (var line in lines)
                {
                    char dir = line[0];
                    int rotation =  int.Parse(line.Substring(1));
                    if (dir == 'R')
                    {
                        while (rotation > 0)
                        {
                            if (dialReading == 0)
                                result += 1;
                            dialReading += 1;
                            dialReading %= 100;
                            rotation -= 1;
                        }
                    }
                    else
                    {
                        while (rotation > 0)
                        {
                            if (dialReading == 0)
                                result += 1;
                            dialReading -= 1;
                            if ( dialReading < 0)
                                dialReading += 100;
                            dialReading %= 100;
                            rotation -= 1;
                        }
                    }
                }
    
                Console.WriteLine(result);
            }
            public static void Main(string[] args)
            {
                Part1();
                Part2();
            }
        }
    }
    
      
34 comments