14 votes

Day 1: Secret Entrance

Today's problem description: https://adventofcode.com/2025/day/1

Please post your solutions in your own top-level comment. Here's a template you can copy-paste into your comment to format it nicely, with the code collapsed by default inside an expandable section with syntax highlighting (you can replace python with any of the "short names" listed in this page of supported languages):

<details>
<summary>Part 1</summary>

```python
Your code here.
```

</details>

19 comments

  1. [3]
    lily
    (edited )
    Link
    Doing this year in a little-known scripting language I've been writing a game with in my spare time (https://lily-lang.org). It's an embeddable scripting language like Lua, but with static typing...

    Doing this year in a little-known scripting language I've been writing a game with in my spare time (https://lily-lang.org). It's an embeddable scripting language like Lua, but with static typing - an interesting niche I haven't really seen covered elsewhere.

    I agree with others that this was harder than the typical Day 1 puzzle, but it still wasn't too bad. I took some time after initially solving it to try to figure out a cleaner solution, but eventually just gave up and committed my original code. The input was small enough it didn't really matter.

    Solution (Lily)
    var dial = 50
    
    var times_zero_was_reached = 0
    var times_zero_was_reached_or_passed = 0
    
    for line in File.read_to_string("inputs/day_01.txt").split("\n"): {
        var amount = line.slice(1).parse_i().unwrap()
    
        if line[0] == 'R': {
            dial += amount
            while dial >= 100: {
                dial -= 100
                times_zero_was_reached_or_passed += 1
            }
        else:
            if dial == 0: {
                dial = 100 - amount
            else:
                dial -= amount
            }
    
            while dial < 0: {
                dial += 100
                times_zero_was_reached_or_passed += 1
            }
    
            if dial == 0: {
                times_zero_was_reached_or_passed += 1
            }
        }
    
        if dial == 0: {
            times_zero_was_reached += 1
        }
    }
    
    print("Part 1: {}\nPart 2: {}".format(
        times_zero_was_reached, times_zero_was_reached_or_passed
    ))
    
    4 votes
    1. [2]
      scarecrw
      Link Parent
      The language looks cool, but that website is phenomenal! I want every site for a language to be as simple and organized as this. Exactly the information I want without any extras in the way. I...

      The language looks cool, but that website is phenomenal! I want every site for a language to be as simple and organized as this. Exactly the information I want without any extras in the way.

      I have to ask based on the username, is this a language you're connected to or just a happy coincidence?

      4 votes
      1. lily
        Link Parent
        Just a coincidence! I found the language earlier this year when looking through a list on GitHub of embeddable scripting languages, and thought it looked interesting. I have contributed some...

        Just a coincidence! I found the language earlier this year when looking through a list on GitHub of embeddable scripting languages, and thought it looked interesting. I have contributed some functionality to the standard library (mainly a sort method for lists and a small UTF-8 module), but I'm not the developer of the language itself.

        3 votes
  2. [3]
    tomf
    Link
    I'm not sure how much I'll do this year, but here's P1 and P2 in Google Sheets The Formula =INDEX( LET( in,TOCOL(A:A,3), m,IF(LEFT(in)="R",1,-1)*REGEXEXTRACT(in,"\d+"),...

    I'm not sure how much I'll do this year, but here's P1 and P2 in Google Sheets

    The Formula
    =INDEX(
      LET(
       in,TOCOL(A:A,3),
       m,IF(LEFT(in)="R",1,-1)*REGEXEXTRACT(in,"\d+"),
       r,SCAN(50,m,LAMBDA(x,y,MOD(x+y,100))),
       t,MOD(r-m,100),
       VSTACK(
        SUM(N(r=0)),
        SUM(1+INT((ABS(m)-IF(t=0,100,IF(m>0,100-t,t)))/100)))))
    
    4 votes
    1. [2]
      cfabbro
      Link Parent
      My topic about the Excel World Championships inspire you to give spreadsheets a try again for AoC? ;)

      My topic about the Excel World Championships inspire you to give spreadsheets a try again for AoC? ;)

      1 vote
      1. tomf
        Link Parent
        haha that and its really all I'm good at in this space. Last year I only did a few days -- but I usually get about 3/4 done. Its once we get into the 'build a computer...' that I tap out

        haha that and its really all I'm good at in this space. Last year I only did a few days -- but I usually get about 3/4 done. Its once we get into the 'build a computer...' that I tap out

        1 vote
  3. [2]
    jzimbel
    (edited )
    Link
    Elixir If anyone is interested, I have a template repository that helps you set up an elixir project for Advent of Code puzzles with some nice conveniences. That’s what I’ll be using in all of my...

    Elixir

    If anyone is interested, I have a template repository that helps you set up an elixir project for Advent of Code puzzles with some nice conveniences. That’s what I’ll be using in all of my solutions.

    I am so glad he’s shortened the event to 12 days, I always spend way too much time on these puzzles and end up neglecting more important things during an already busy holiday month…

    Both parts

    I wanted to find a cleaner (i.e. more math-based, less branching-logic-based) approach for counting the number of times a given rotation visited position 0, but this was the best I could come up with.

    Summary of my approach: Build a list where each element is a map with :position and :zero_visits keys. Each map gives the position of the dial after one rotation specified by the input, as well as the number of times it moved to 0 on its way to that ending position. Use these computed states of the dial after each rotation to get the answers for both part 1 and part 2.

    defmodule AdventOfCode.Solution.Year2025.Day01 do
      use AdventOfCode.Solution.SharedParse
    
      @start_position 50
    
      @impl true
      def parse(input) do
        input
        |> String.split()
        |> Enum.map(fn
          "L" <> digits -> -String.to_integer(digits)
          "R" <> digits -> String.to_integer(digits)
        end)
        |> Stream.scan(%{position: @start_position, zero_visits: 0}, &move/2)
        |> Enum.to_list()
      end
    
      def part1(movements), do: Enum.count(movements, &(&1.position == 0))
      def part2(movements), do: Enum.sum_by(movements, & &1.zero_visits)
    
      defp move(rotation, %{position: position}) do
        %{
          position: Integer.mod(position + rotation, 100),
          zero_visits: count_zero_visits(position, rotation)
        }
      end
    
      # Note: Input never contains "L0" or "R0"--magnitude of a rotation is always nonzero.
      defp count_zero_visits(position, rotation) when rotation > 0, do: zv(rotation, 100 - position)
      defp count_zero_visits(position, rotation) when rotation < 0, do: zv(abs(rotation), position)
    
      defp zv(rot_mag, _zero_dist = 0), do: div(rot_mag, 100)
      defp zv(rot_mag, zero_dist) when rot_mag >= zero_dist, do: 1 + zv(rot_mag - zero_dist, 0)
      defp zv(_rot_mag, _zero_dist), do: 0
    end
    
    Benchmarks

    Since I put basically all the work in the shared parse function, "part 1" and "part 2" are simply the logic that sums up the results with one final pass through the list.

    Name             ips        average  deviation         median         99th %
    Part 2       93.12 K       10.74 μs    ±37.65%       10.63 μs       12.08 μs
    Part 1       29.50 K       33.90 μs     ±4.94%       33.71 μs       36.13 μs
    Parse         2.52 K      396.37 μs    ±30.97%      367.54 μs      546.53 μs
    
    3 votes
    1. hpr
      Link Parent
      Huh, didn't think I'd find someone else here doing it in Elixir. I might be having a closer look at your code, since I am using the puzzles to learn the language.

      Huh, didn't think I'd find someone else here doing it in Elixir.
      I might be having a closer look at your code, since I am using the puzzles to learn the language.

      1 vote
  4. [2]
    thecakeisalime
    Link
    I originally solved part 2 with brute force, because I kept getting hung up on corner cases. But I eventually figured it out. Looking at my solution, the cases aren't that bad overall, but it was...

    I originally solved part 2 with brute force, because I kept getting hung up on corner cases. But I eventually figured it out. Looking at my solution, the cases aren't that bad overall, but it was hard to make sure the cases were mutually exclusive. I was missing the second elif case for the longest time.

    Also, I'm new to python, so if there's anything I'm doing that looks weird or unnecessary or could be done better, please feel free to let me know.

    Part 1
    file = open("20250101.in", "r")
    
    dial = 50
    count = 0
    
    for line in file:
      if line[0] == 'L':
        dial -= int(line[1:])
      else:
        dial += int(line[1:])
      
      dial %= 100
      if dial == 0:
        count += 1
    
    file.close()
    
    print(count)
    
    Part 2
    file = open("20250101.in", "r")
    
    dial = 50
    count = 0
    
    for line in file:
      rot = int(line[1:])
    
      previousDial = dial
      if line[0] == 'L':
        dial -= rot
      else:
        dial += rot
    
      dial %= 100
    
      count += rot // 100
      if dial == 0 and previousDial == 0:
        count += 0
      elif dial == 0:
        count += 1
      elif previousDial == 0:
        count += 0
      elif line[0] == 'L' and previousDial - (rot % 100) < 0:
        count += 1
      elif line[0] == 'R' and previousDial + (rot % 100) > 100:
        count += 1
    
    file.close()
    
    print(count)
    
    2 votes
    1. scarecrw
      Link Parent
      Looks great! If you want a tip, it's often preferred to use the with syntax for file access, which ensures file closing even with exceptions. So instead of: file = open(filename, 'r') # ...code......

      Looks great!

      If you want a tip, it's often preferred to use the with syntax for file access, which ensures file closing even with exceptions.

      So instead of:

      file = open(filename, 'r')
      # ...code...
      file.close()
      

      you can use:

      with open(filename, 'r') as file:
          # ...code...
      
      2 votes
  5. [2]
    creamsnail
    Link
    I probably did not follow the prescribed way to accomplish these. But I did them, and that's all that really matters. I hope to see other versions for those who are doing it in Rust. I think I saw...

    I probably did not follow the prescribed way to accomplish these. But I did them, and that's all that really matters.

    I hope to see other versions for those who are doing it in Rust. I think I saw two other folks on here mention trying it this year, hopefully it'll inspire them to post their solutions when or if they can (no pressure).

    Parts 1 and 2 (with tests) Also I thought it was kind of cute that the second part's hex `0x434C49434B` decodes to `CLICK`
    use std::fs::read_to_string;
    
    const test_file: &str = "example_input.txt";
    const puzzle_input: &str = "puzzle_input.txt";
    
    fn main() {
        // PART ONE
        let part_one_start = 50;
        let part_one_answer = part_one(read_puzzle_input(puzzle_input), part_one_start);
        println!("The answer to part one is: {}", part_one_answer);
    
        let part_two_start: usize = 50;
        let part_two_answer = part_two(read_puzzle_input(puzzle_input), part_two_start);
        println!("The answer to part two is: {}", part_two_answer)
    }
    
    // Reads in the puzzle input text file which contains the D##
    fn read_puzzle_input(file_name: &str) -> Vec<String> {
        return read_to_string(file_name)
            .unwrap()
            .lines()
            .map(String::from)
            .collect();
    }
    
    fn part_one(instruction: Vec<String>, start_point: usize) -> i32 {
        let mut dial = vec![
            "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16",
            "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31",
            "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46",
            "47", "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61",
            "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "74", "75", "76",
            "77", "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "90", "91",
            "92", "93", "94", "95", "96", "97", "98", "99",
        ];
        dial.rotate_left(start_point);
    
        // Keep track of how many times we hit 0
        let mut count = 0;
    
        // Iterate over the instructions
        for line in instruction {
            let (direction, number) = line.split_at(1);
            // Set the number to a digit so we can rotate the dial
            let num: usize = number.parse::<usize>().unwrap();
            if direction == "R" {
                for _ in 0..num {
                    dial.rotate_left(1)
                }
            } else if direction == "L" {
                for _ in 0..num {
                    dial.rotate_right(1)
                }
            }
            if dial[0] == "0" {
                count += 1;
            }
        }
        return count;
    }
    
    fn part_two(instruction: Vec<String>, start_point: usize) -> i32 {
        let mut dial = vec![
            "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16",
            "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31",
            "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46",
            "47", "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61",
            "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "74", "75", "76",
            "77", "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "90", "91",
            "92", "93", "94", "95", "96", "97", "98", "99",
        ];
        dial.rotate_left(start_point);
    
        // Keep track of how many times we hit 0
        let mut count = 0;
    
        // Iterate over the instructions
        for line in instruction {
            let (direction, number) = line.split_at(1);
            // Set the number to a digit so we can rotate the dial
            let num: usize = number.parse::<usize>().unwrap();
            if direction == "R" {
                for _ in 0..num {
                    dial.rotate_left(1);
                    if dial[0] == "0" {
                        count += 1;
                    }
                }
            } else if direction == "L" {
                for _ in 0..num {
                    dial.rotate_right(1);
                    if dial[0] == "0" {
                        count += 1;
                    }
                }
            }
        }
        return count;
    }
    
    #[test]
    fn part_one_example() {
        let instruction = read_puzzle_input(test_file);
        let start_point: usize = 50;
        assert_eq!(part_one(instruction, start_point), 3);
    }
    
    #[test]
    fn part_two_example() {
        let instruction = read_puzzle_input(test_file);
        let start_point: usize = 50;
        assert_eq!(part_two(instruction, start_point), 6);
    }
    
    2 votes
    1. DeaconBlue
      Link Parent
      I am posting this to remind me to send my solution after work, also doing it in Rust!

      I am posting this to remind me to send my solution after work, also doing it in Rust!

      2 votes
  6. fidwell
    Link
    Welcome back to AoC! Part 1 was pretty easy as usual, although part 2 took me a little longer than I would have liked, due to a couple finicky edge cases. Overall, a pretty typical day 1 puzzle...

    Welcome back to AoC!

    Part 1 was pretty easy as usual, although part 2 took me a little longer than I would have liked, due to a couple finicky edge cases. Overall, a pretty typical day 1 puzzle and a nice warmup back into it.

    Minor general spoilers

    There were some fun micro-optimizations to make in this puzzle as well, such as parsing all the input up-front instead of one line at a time. Speaking of, there is also the trick of replacing L in the input with - and R with nothing, to improve numeric parsing.

    Full code (C#) — As always I'm sure there are ways to make this more concise and maybe even faster, but as-is my benchmarks put both parts' runtime under 500 µs, which is well within my threshold.

    1 vote
  7. scarecrw
    Link
    Opted to try Prolog for this year. I don't have a clue what I'm doing, and may have bitten off more than I can chew, but I'm loving it so far! Day 1 Prolog Solution Spoilers Originally tried to do...

    Opted to try Prolog for this year. I don't have a clue what I'm doing, and may have bitten off more than I can chew, but I'm loving it so far!

    Day 1 Prolog Solution

    Spoilers

    Originally tried to do part 2 without the full expansion, as it worked almost perfectly with divmod, but gave up after a few frustrating issues and just went the naive route. The style is still pretty imperative, so I'm hoping on future days to try and understand more of the idiomatic approaches.

    1 vote
  8. [2]
    balooga
    Link
    TypeScript I'll echo what others have said, this was a markedly harder Day 1 than in years past. I guess that's to be expected for the new compressed 12-day run. Makes me nervous about tomorrow...

    TypeScript

    I'll echo what others have said, this was a markedly harder Day 1 than in years past. I guess that's to be expected for the new compressed 12-day run. Makes me nervous about tomorrow though! Like others, I found Part 1 pretty simple but got hung up on corner cases in Part 2. Ended up just brute-forcing it, which was ironically much more readable than the monstrosity I'd been trying to make work. And it still runs in 5ms on my machine, so no sense in trying to optimize further.

    Parts 1 and 2
    type Direction = 'L' | 'R';
    type InputData = [Direction, number][];
    
    function formatInput(input: string): InputData {
      const inputArr = input.trim().split('\n');
      return inputArr.map(rotation => [rotation[0] as Direction, parseInt(rotation.slice(1), 10)]);
    }
    
    export function run(input: string): string[] {
      const data = formatInput(input);
    
      const countZeroStops = (rotations: InputData): number => {
        let pointer = 50;
        let count = 0;
        for (const [direction, distance] of rotations) {
          const multiplier = direction === 'L' ? -1 : 1;
          pointer = (((pointer + distance * multiplier) % 100) + 100) % 100;
          if (pointer === 0) {
            count++;
          }
        }
        return count;
      };
    
      const countZeroClicks = (rotations: InputData): number => {
        let pointer = 50;
        let count = 0;
        for (const [direction, distance] of rotations) {
          const clickAmount = direction === 'L' ? -1 : 1;
          for (let i = 0; i < distance; i++) {
            pointer += clickAmount;
            if (pointer === 100) {
              pointer = 0;
            }
            if (pointer === -1) {
              pointer = 99;
            }
            if (pointer === 0) {
              count++;
            }
          }
        }
        return count;
      };
    
      return [`${countZeroStops(data)}`, `${countZeroClicks(data)}`];
    }
    
    1 vote
    1. hpr
      Link Parent
      The step up in difficulty really messed me up. I spent way more time than I usually would being certain there was just a very simple formula for counting the number of times one would pass over 0...

      The step up in difficulty really messed me up.

      I spent way more time than I usually would being certain there was just a very simple formula for counting the number of times one would pass over 0 without special cases. It's day 1 after all!

      But alas, handling the special cases for the negatives seems to have been necessary all along.
      I could have saved myself a lot of time thinking I was missing the obvious.

      2 votes
  9. Berdes
    Link
    I'm doing it in Rust this year to get some experience using the language. I'm very familiar with C++ and have used Haskell a long time ago, so I'm not (yet?) struggling with new concepts. For now,...

    I'm doing it in Rust this year to get some experience using the language. I'm very familiar with C++ and have used Haskell a long time ago, so I'm not (yet?) struggling with new concepts. For now, I'm mostly learning the details of the syntax and figuring out what's available in the standard library.

    Parts 1 and 2
    use std::io;
    use std::str::FromStr;
    
    enum Rotation {
      L,
      R,
    }
    
    struct ParseRotationError {
      message: String,
    }
    
    impl Rotation {
      fn from_char(c: char) -> Result<Self, ParseRotationError> {
        match c {
          'L' => Ok(Rotation::L),
          'R' => Ok(Rotation::R),
          _ => Err(ParseRotationError {
            message: format!("Cannot parse {} as Rotation", c),
          }),
        }
      }
    }
    
    struct Move {
      r: Rotation,
      c: i32,
    }
    
    impl Move {
      fn signed_move(&self) -> i32 {
        match self.r {
          Rotation::L => -self.c,
          Rotation::R => self.c,
        }
      }
    }
    
    struct ParseMoveError {
      message: String,
    }
    
    impl FromStr for Move {
      type Err = ParseMoveError;
    
      fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s.len() < 2 {
          return Err(ParseMoveError {
            message: format!(
              "Move should contain at least 2 characters: len is {}",
              s.len()
            ),
          });
        }
        let mut chars = s.chars();
        let r = Rotation::from_char(chars.next().unwrap()).map_err(|e| ParseMoveError {
          message: format!("Failed to parse rotation: {}", e.message),
        })?;
        let c = chars.as_str().parse::<i32>().map_err(|e| ParseMoveError {
          message: format!("Failed to parse count: {}", e),
        })?;
        Ok(Move { r, c })
      }
    }
    
    fn main() {
      let mut part1 = 0;
      let mut part2 = 0;
      let mut position = 50;
      for line in io::stdin().lines() {
        match line {
          Ok(l) => match l.parse::<Move>() {
            Ok(m) => {
              let prev_pos = position;
              position += m.signed_move();
              if position <= 0 {
                part2 += (-position) / 100 + (if prev_pos == 0 { 0 } else { 1 });
              } else if position >= 100 {
                part2 += position / 100;
              }
              position = (position % 100 + 100) % 100;
              part1 += if position == 0 { 1 } else { 0 };
            }
            Err(e) => println!("Failed to parse '{}': {}", l, e.message),
          },
          Err(e) => println!("Error reading line: {}", e),
        }
      }
      println!("Part 1: {}", part1);
      println!("Part 2: {}", part2);
    }
    
    1 vote
  10. netsplit
    Link
    Here are my slightly brute-forced day 1 solutions in Perl. Spoiler I was initially tripped up by the counter resets at 0 and 100, hence the slightly clumsy workaround there. Part 1 (Perl)...

    Here are my slightly brute-forced day 1 solutions in Perl.

    Spoiler

    I was initially tripped up by the counter resets at 0 and 100, hence the slightly clumsy workaround there.

    Part 1 (Perl)
    #!/usr/bin/env perl
    
    use strict;
    use warnings;
    
    use v5.42.0;
    
    use File::Slurper 'read_lines';
    
    my @content = read_lines 'input.txt';
    
    my $password = 0;
    my $start    = 50;
    
    foreach my $instruction (@content) {
        my ($dir, $steps) = $instruction =~ /^(.)(.+)/;
    
        for (1..$steps) {
            if ($dir eq 'L') {
                $start -= 1;
            } else {
                $start += 1;
            }
    
            if ($start == -1) {
                $start = 99;
            } elsif ($start == 100) {
                $start = 0;
            }
        }
    
        $password++ if $start == 0;
    }
    
    print "The password is: ${password}\n";
    
    Part 2 (Perl)
    #!/usr/bin/env perl
    
    use strict;
    use warnings;
    
    use v5.42.0;
    
    use File::Slurper 'read_lines';
    
    my @content = read_lines 'input.txt';
    
    my $password = 0;
    my $start    = 50;
    
    foreach my $instruction (@content) {
        my ($dir, $steps) = $instruction =~ /^(.)(.+)/;
    
        for (1..$steps) {
            if ($dir eq 'L') {
                $start -= 1;
            } else {
                $start += 1;
            }
    
            if ($start == -1) {
                $start = 99;
            } elsif ($start == 100) {
                $start = 0;
            }
    
            $password++ if $start == 0;
        }
    }
    
    print "The password is: ${password}\n";
    
    1 vote
  11. cfabbro
    (edited )
    Link
    Normally these topics are automated, but @Deimos is currently on vacation and probably won't be able to set that up very easily where he's at, so I decided to manually post them until he returns....

    Normally these topics are automated, but @Deimos is currently on vacation and probably won't be able to set that up very easily where he's at, so I decided to manually post them until he returns.

    /offtopic

    4 votes