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

  • Strlcpy@1 ( strlcpy@lemmy.sdf.org ) 
    link
    fedilink
    arrow-up
    12
    ·
    edit-2
    11 months ago

    DOS + BIOS boot (hybrid binary)

    Repo | day01.asm | day01.c (prototype) | .COM download

    Written in x86-16 assembly. Works as a DOS program but also as disk image for older PCs with BIOS support (or older VMs). Getting this setup to work was tricky, especially since when I started this, I had only basic x86-16 experience! Needless to say I’ve spent much time staring at hex numbers.

    The very start of the file determines if it’s running in DOS or as a bootloader, in which case it’ll have to load the remainder of the file from disk and rearrange the memory layout to emulate the DOS situation.

    Right now this is using just one segment of memory (64K). Input data is compressed using a custom run-length encoding scheme. The compressed background image is directly decoded into the VGA framebuffer, after which it is overwritten with the decompressed input file. Space is tight!

    My main goal is to finish a few days at least with one or more game consoles supported (GameBoy Advance would be cool) and to do some cool palette tricks, like a day/night transition or animated water and stars.

  • ystael ( ystael@beehaw.org ) 
    link
    fedilink
    arrow-up
    4
    ·
    11 months ago

    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))))
    
  • reboot6675 ( reboot6675@sopuli.xyz ) 
    link
    fedilink
    arrow-up
    4
    ·
    11 months ago

    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)
    }
    
  • hades ( hades@programming.dev ) 
    link
    fedilink
    arrow-up
    4
    ·
    11 months ago

    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 simpler for n in &self.input. The only difference is that n will be of type &i64 instead of i64.

    • Deebster ( Deebster@programming.dev ) 
      link
      fedilink
      English
      arrow-up
      1
      ·
      11 months ago

      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).

      • hades ( hades@programming.dev ) 
        link
        fedilink
        arrow-up
        2
        ·
        11 months ago

        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

  • Zikeji ( Zikeji@programming.dev ) 
    link
    fedilink
    English
    arrow-up
    4
    ·
    11 months ago

    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}`);
    
  • h4x0r ( h4x0r@lemmy.dbzer0.com ) 
    link
    fedilink
    English
    arrow-up
    3
    ·
    edit-2
    11 months ago

    c

    #include "aoc.h"
    #include <stdio.h>
    #include <string.h>
    
    constexpr usize LINE_BUFSZ = (1 << 3);
    constexpr i32 START = 50;
    constexpr i32 TOP = 100;
    
    static void
    solve(Mode mode) {
      FILE* input = fopen("input", "r");
      c8 line[LINE_BUFSZ] = {};
      u32 zs = 0;
      i32 idx = START;
      while (fgets(line, sizeof(line), input)) {
        line[strcspn(line, "\n")] = 0;
        i32 val = 0;
        sscanf(&line[1], "%d", &val);
        if (mode == MODE_ONE) {
          i32 d = line[0] == 'L' ? -val : val;
          idx = (idx + d) % TOP;
          idx += idx < 0 ? TOP : 0;
          idx == 0 ? zs++ : 0;
        } else {
          for (i32 i = 0; i < val; i++) {
            idx = line[0] == 'L' ? idx - 1 : idx + 1;
            idx = idx == -1 ? TOP - 1 : idx;
            idx = idx == TOP ? 0 : idx;
            idx == 0 ? zs++ : 0;
          }
        }
      }
      fclose(input);
      printf("%u\n", zs);
    }
    
    i32
    main(void) {
      solve(MODE_ONE);
      solve(MODE_TWO);
    }
    
  • 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

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

    View on github

    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!();
    
  • janAkali ( janAkali@lemmy.sdf.org ) 
    link
    fedilink
    arrow-up
    3
    ·
    11 months ago

    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.part1
    

    Full solution at Codeberg: solution.nim

  • Amy ( lwhjp@piefed.blahaj.zone ) 
    link
    fedilink
    English
    arrow-up
    2
    ·
    11 months ago

    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 input  
    
  • Deebster ( Deebster@programming.dev ) 
    link
    fedilink
    English
    arrow-up
    2
    ·
    edit-2
    11 months ago

    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.

  • eta ( eta@feddit.org ) 
    link
    fedilink
    English
    arrow-up
    2
    ·
    edit-2
    11 months ago

    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.

    #!/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

  • ghodawalaaman ( ghodawalaaman@programming.dev ) deleted by creator
    link
    fedilink
    arrow-up
    1
    ·
    11 months ago

    C# ( 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();
            }
        }
    }
    
    • chunkystyles ( chunkystyles@sopuli.xyz ) 
      link
      fedilink
      English
      arrow-up
      1
      ·
      11 months ago

      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
              }
          }