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
- What is this?: Here is a post with a large amount of details: https://programming.dev/post/6637268
- Where do I participate?: https://adventofcode.com/
- Is there a leaderboard for the community?: We have a programming.dev leaderboard with the info on how to join in this post: https://programming.dev/post/6631465
Haskell
I was late to the part on this one and forgot to post my solution :3
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 inputC# ( c sharp )
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(); } } }A straightforward day 1 for a Javascript / NodeJS solution.
Poorly Optimized JS Solution
let position = 50; let answer1 = 0; let answer2 = 0; const sequence = require('fs').readFileSync('input-day1.txt', 'utf-8'); sequence.split('\n').forEach(instruction => { const turn = instruction.charAt(0); let distance = parseInt(instruction.slice(1), 10); while (distance > 0) { distance -= 1; if (turn === 'L') { position -= 1; } else if (turn === 'R') { position += 1; } if (position >= 100) { position -= 100; } else if (position < 0) { position += 100; } if (position === 0) { answer2 += 1; } } if (position === 0) { answer1 += 1; } }); console.log(`Part 1 Answer: ${answer1}`); console.log(`Part 2 Answer: ${answer2}`);Brute forcing it like this was my first thought - like they say: if it’s stupid and it works, it’s not stupid.
So, obviously the bot didnt work, thanks Ategon for covering :D
Here is the dumb solution for pt2, everyone can share in my shame:
#[test] fn test_2025_1_part2() { let input = std::fs::read_to_string("input/2025/day_1.txt").unwrap(); let turns = input .lines() .map(|line| { let value = line[1..].parse::<i32>().unwrap(); if line[0..1] == *"L" { -value } else { value } }) .collect::<Vec<i32>>(); let mut zero_ctr = 0; let mut pos = 50; for turn in turns { print!("{pos}->"); let mut zero_passes = 0; if turn > 0 { for _ in 0..turn { pos += 1; if pos == 100 { pos = 0; zero_passes += 1; } } } else { for _ in 0..-turn { pos -= 1; if pos == 0 { zero_passes += 1; } if pos == -1 { pos = 99; } } }; println!("{turn}->{pos}: {zero_passes}"); zero_ctr += zero_passes; } println!("zero ctr: {}", zero_ctr); }Rust
#[derive(Default)] pub struct Day1Solver { input: Vec<i64>, } impl Solver for Day1Solver { fn presolve(&mut self, input: &str) { self.input = input .trim() .split("\n") .map(|line| { if let Some(n) = line.strip_prefix('L') { -n.parse::<i64>().unwrap() } else if let Some(n) = line.strip_prefix('R') { n.parse().unwrap() } else { panic!("what: {line}"); } }) .collect(); } fn solve_part_one(&mut self) -> String { let mut p = 50; let mut count = 0; for n in self.input.clone() { p += n; if p % 100 == 0 { count += 1; } } count.to_string() } fn solve_part_two(&mut self) -> String { let mut count = 0; let mut p = 1000000000050; for i in self.input.clone() { if p % 100 == 0 { count += (i / 100).abs(); } else { count += ((p + i) / 100 - p / 100).abs(); if i < 0 && (p + i) % 100 == 0 { count += 1; } } p += i; } count.to_string() } }Nice solution. Just a little Rust tip if you don’t mind: In this case you can avoid cloning the input Vec in the loops by instead looping over references into the list with
for n in self.input.iter()or simplerfor n in &self.input. The only difference is thatnwill be of type&i64instead ofi64.Thanks! I don’t mind tips at all.
I’m far from sure I understand what you’re doing in part 2, but I think maybe you hit the same logic bug I did (I solved it with a shameful if statement that I will have to fix later).
the idea is that crossing the zero is easy to detect by dividing the total dial movement by 100. I.e. if you cross from 120 to 90 you will detect that 120/100=1 changed to 90/100=0. The only case when this doesn’t work is when you stop at zero going in the negative direction, hence the extra if
The thing I couldn’t comprehend was where 1000000000050 had come from.
ah that’s just because i needed rounding towards infinity and not towards zero. In other words i wanted -10/100 to be -1 and not zero. But i couldn’t figure it out on the spot, so i just made it never negative :)
How could I run this code? Do you use some kind of framework for AoC?
you could say that, yeah, it downloads the inputs, submits the answers, and keeps track of wrong answers. You can grab my entire repository here: https://github.com/hades/aoc25/tree/master
Rust
Almost missed, that “the dial starts by pointing at 50”.
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!();The struggled with a counting solution for a long time. I submitted with a simple enumerative solution in the end but managed to get it right after some pause time:
Haskell
Fast to Run, Stepwise Solution
{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OrPatterns #-} module Main (main) where import Control.Monad ( (<$!>) ) import qualified Data.List as List main :: IO () main = do rotations <- (fmap parseRotation . init . lines) <$!> getContents print $ part1 rotations print $ part2 rotations part2 :: [Either Int Int] -> Int part2 rotations = let foldRotation (position, zeroCount) operation = case operation of Left y -> let (zeroPasses, y') = y `divMod` 100 position' = (position - y') `mod` 100 zeroCount' = zeroPasses + zeroCount + if position <= y' then fromEnum $ position /= 0 else 0 in (position', zeroCount') Right y -> let (zeroPasses, y') = y `divMod` 100 position' = (position + y') `mod` 100 zeroCount' = zeroPasses + zeroCount + if y' + position >= 100 then 1 else 0 in (position', zeroCount') in snd $ List.foldl' foldRotation (50, 0) rotations part1 :: [Either Int Int] -> Int part1 rotations = let positions = List.scanl applyRotation 50 rotations in List.length . filter (== 0) $ positions applyRotation :: Int -> Either Int Int -> Int applyRotation x = \case Left y -> (x - y) `mod` 100 Right y -> (x + y) `mod` 100 parseRotation :: String -> Either Int Int parseRotation = \case 'R':rest -> Right $ read rest 'L':rest -> Left $ read rest bad -> error $ "invalid rotation operation: " ++ badFast to Code, Exhaustively Enumerating Solution
-- | Old solution enumerating all the numbers part2' :: [Either Int Int] -> Int part2' rotations = let intermediatePositions _ [] = [] intermediatePositions x (op:ops) = case op of Left 0; Right 0 -> intermediatePositions x ops Left y -> let x' = pred x `mod` 100 in x' : intermediatePositions x' (Left (pred y) : ops) Right y -> let x' = succ x `mod` 100 in x' : intermediatePositions x' (Right (pred y) : ops) in List.length . List.filter (== 0) . intermediatePositions 50 $ rotationsWhy are you preferring lambda-case over plain old pattern matching as in the following snippet? I didn’t know this language feature existed and I am now curious :)
applyRotation :: Int -> Either Int Int -> Int applyRotation x (Left y) = (x - y) `mod` 100 applyRotation x (Right y) = (x + y) `mod` 100Thank you for the excellent question. This made me reflect on my coding style and why I actually chose this. Maybe you have noticed, my usage of
LambdaCaseis inconsistent: I didn’t use it in the definition offoldRotation. Which happened with some refactorings (You couldn’t know that, I didn’t tell anywhere), but still.After going through some ‘old’ code I found that I didn’t start using it until early this year. (For context: I started doing Haskell in September 2024) But that may just coincide with me installing HLS.
Anyway, back to the topic: I actually think it’s very elegant because it saves re-typing the function name and/or other parameters. It also easily allows me to add further arguments to the function (but only before the last one). In my mind, this is where
LambdaCaseshines.Sometimes I end up refactoring functions because it’s very hard to match on multiple arguments using
LambdaCase. I also try to avoid adding arguments in the back, which might bite me later and limits flexibility a lot.Moaaar Backstory
I picked it up in some forum discussion I read where somebody argued that using explicit matches litters the Codebase with re-definitions of the same functions. It makes
grep-ing the source hard. I was easily influenced by this and adopted it.I think this is not the way I like to go about it. I would rather use Hoogle, Haddock or HLS to search in my source.
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.
"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…
"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.
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.)
(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))))Kotlin
already rather scuffed for day1, but works:
https://codeberg.org/Eco-Gaming/AdventOfCode/src/branch/main/src/main/kotlin/a2025/puzzles/Day01.kt
Here’s my Kotlin version for Part 2.
fun main() { var currentDialPosition = 50 var password = 0 val input = getInput(1) val turns = parseInput2(input) turns.forEach { if (it.second > 0) { password += it.second } var newDialPosition = currentDialPosition + it.first if (newDialPosition < 0) { newDialPosition += 100 if (currentDialPosition != 0) { password++ } } else if (newDialPosition > 99) { newDialPosition %= 100 password++ } else if (newDialPosition == 0) { password++ } currentDialPosition = newDialPosition } println(password) } fun parseInput2(input: String): List<Pair<Int, Int>> = input .split("\n") .filter { it.isNotBlank() } .map { val direction = it.first() val number = it.slice(1..<it.length).toInt() val clicks = number % 100 val fullTurns = number / 100 if (direction == 'L') { clicks * -1 to fullTurns } else { clicks to fullTurns } }
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.
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.part1Full solution at Codeberg: solution.nim
Golang
func part1() { // file, _ := os.Open("sample.txt") file, _ := os.Open("input.txt") defer file.Close() scanner := bufio.NewScanner(file) n := 100 current := 50 pointingAt0 := 0 for scanner.Scan() { line := scanner.Text() num, _ := strconv.Atoi(line[1:]) if line[0] == 'L' { current = ((current-num)%n + n) % n } else { current = (current + num) % n } if current == 0 { pointingAt0++ } } fmt.Println(pointingAt0) } func part2() { // file, _ := os.Open("sample.txt") file, _ := os.Open("input.txt") defer file.Close() scanner := bufio.NewScanner(file) n := 100 current := 50 pointingAt0 := 0 for scanner.Scan() { line := scanner.Text() num, _ := strconv.Atoi(line[1:]) rounds := num / n pointingAt0 += rounds num = num % n new := -1 if line[0] == 'L' { new = ((current-num)%n + n) % n if current != 0 && (new > current || new == 0) { pointingAt0++ } } else { new = (current + num) % n if current != 0 && (new < current || new == 0) { pointingAt0++ } } current = new } fmt.Println(pointingAt0) }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.
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
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) )))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.











