Day 3: Gear Ratios


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)
  • Code block support is not fully rolled out yet but likely will be in the middle of the event. Try to share solutions as both code blocks and using something such as https://topaz.github.io/paste/ or pastebin (code blocks to future proof it for when 0.19 comes out and since code blocks currently function in some apps and some instances as well if they are running a 0.19 beta)

FAQ


🔒This post will be unlocked when there is a decent amount of submissions on the leaderboard to avoid cheating for top spots

🔓 Edit: Post has been unlocked after 11 minutes

  • @[email protected]
    link
    fedilink
    2
    edit-2
    2 years ago

    Input parsing AGAIN?

    Lua
    -- SPDX-FileCopyrightText: 2023 Jummit
    --
    -- SPDX-License-Identifier: GPL-3.0-or-later
    
    local lines = {}
    for line in io.open("3.input"):lines() do
    	table.insert(lines, "."..line..".")
    end
    local width = #lines[1]
    local height = #lines
    local function at(x, y, w)
    	if y < 1 or y > height then return nil end
    	return lines[y]:sub(x, x + w - 1)
    end
    local sum = 0
    local gears = {}
    for y, line in ipairs(lines) do
    	local start = 1
    	local outLine = line
    	while true do
    		local newStart, numEnd = line:find("%d+", start)
    		if not newStart then break end
    		local symbol = false
    		local num = tonumber(line:sub(newStart, numEnd))
    		for y = y - 1, y + 1 do
    			local surrounding = at(newStart - 1, y, numEnd - newStart + 3)
    			if surrounding then
    				if surrounding and surrounding:match("[^.%d]") then
    					symbol = true
    				end
    				for i = 1, #surrounding do
    					local gear = surrounding:sub(i, i) == "*"
    					if gear then
    						if not gears[y] then
    							gears[y] = {}
    						end
    						local x = i + newStart - 2
    						if not gears[y][x] then
    							gears[y][i + newStart - 2] = {}
    						end
    						table.insert(gears[y][x], num)
    					end
    				end
    			end
    		end
    		if symbol then
    			sum = sum + num
    		end
    		start = numEnd + 1
    	end
    end
    print(sum)
    local ratio = 0
    for _, line in pairs(gears) do
    	for _, gears in pairs(line) do
    		if #gears == 2 then
    			ratio = ratio + gears[1] * gears[2]
    		end
    	end
    end
    print(ratio)
    
    Hare (Part one only)
    // SPDX-FileCopyrightText: 2023 Jummit
    //
    // SPDX-License-Identifier: GPL-3.0-or-later
    
    use strings;
    use regex;
    use fmt;
    use os;
    use bufio;
    use io;
    use strconv;
    use types;
    
    fn star_in(lines: []str, x: uint, y: uint, w: uint) bool = {
    	let start = y;
    	if (start > 0) start -= 1;
    	let end = y + 1;
    	if (end >= len(lines)) end -= 1;
    	const re = regex::compile(`[^.0-9]`)!;
    	for (let h = start; h <= end; h += 1) {
    		fmt::println(strings::sub(lines[h], x, x + w))!;
    		if (regex::test(&re, strings::sub(lines[h], x, x + w))) {
    			fmt::println("")!;
    			return true;
    		};
    	};
    	fmt::println("")!;
    	return false;
    };
    
    export fn main() void = {
    	const file = os::open("3.input")!;
    	defer io::close(file)!;
    	const buf = bufio::newscanner(file, types::SIZE_MAX);
    	let lines: []str = [];
    	defer strings::freeall(lines);
    	for (true) {
    		match (bufio::scan_line(&buf)!) {
    		case io::EOF =>
    			break;
    		case let line: const str =>
    			append(lines, strings::dup(line));
    		};
    	};
    	const height = len(lines);
    	const width = len(lines[0]);
    	let sum: uint = 0;
    	let gears: [](uint, uint) = [];
    	const num_re = regex::compile(`[0-9]+`)!;
    	for (let y = 0u; y < len(lines); y += 1) {
    		let nums = regex::findall(&num_re, lines[y]);
    		defer regex::result_freeall(nums);
    		for (let i = 0z; i < len(nums); i += 1) {
    			for (let j = 0z; j < len(nums[i]); j += 1) {
    				const find = nums[i][j];
    				const num = strconv::stou(find.content)!;
    				let start = find.start: uint;
    				let w = len(find.content): uint + 2;
    				if (start > 0) {
    					start -= 1;
    				} else {
    					w -= 1;
    				};
    				if (star_in(lines, start, y, w)) {
    					sum += num;
    				};
    			};
    		};
    	};
    	fmt::printfln("{}", sum)!;
    };
    
  • Ananace
    link
    fedilink
    22 years ago

    I get the feeling that I should include some default types for handling 2D maps in my boilerplate, it’s a very recurring problem in AoC after all.

    My solution is reasonably simplistic - and therefore also a bit slow, but the design meant I could do part 2 with just a few extra lines of code on the already processed data, here’s the functional part of it; (I push the previous days solution as part of my workflow for starting with the current day so the full code won’t be up until tomorrow)

    Ruby

    The code has been compressed for brevity.

    Point = Struct.new('Point', :x, :y)
    PartNumber = Struct.new('PartNumber', :number, :adjacent) do
      def adjacent?(to); adjacent.include?(to); end
      def irrelevant?; adjacent.empty?; end
      def to_i; number; end
    end
    
    class Implementation
      def initialize
        @map = []; @dim = { width: 0, height: 0 }; @symbols = []; @numbers = []
      end
    
      def input(line)
        @dim[:width] = line.size; @dim[:height] += 1
        @map += line.chars
      end
    
      def calc
        for y in (0..@dim[:height]-1) do
          for x in (0..@dim[:width]-1) do
            chr = get(x, y); next if chr =~ /\d/ || chr == '.'
            @symbols << Point.new(x, y)
          end
        end
    
        for y in (0..@dim[:height]-1) do
          buf = ""; adj = []
          for x in (0..@dim[:width]) do # Going one over, to fake a non-number as an end char on all lines
            chr = get(x, y)
            if chr =~ /\d/
              buf += chr
              (-1..1).each do |adj_x|
                (-1..1).each do |adj_y|
                  next if adj_x == 0 && adj_y == 0 ||
                    (x + adj_x < 0) || (x + adj_x >= @dim[:width]) ||
                    (y + adj_y < 0) || (y + adj_y >= @dim[:height])
                  sym = Point.new(x + adj_x, y + adj_y)
                  adj << sym if @symbols.any? sym
                end
              end
            elsif !buf.empty?
              @numbers << PartNumber.new(buf.to_i, adj)
              buf = ""; adj = []
            end
          end
        end
      end
    
      def output
        part1 = @numbers.reject(&:irrelevant?).map(&:to_i).sum
        puts "Part 1:", part1
    
        gears = @symbols.select do |sym|
          next unless get(sym) == '*'
          next unless @numbers.select { |num| num.adjacent? sym }.size == 2
          true
        end
        part2 = gears.sum { |gear| @numbers.select { |num| num.adjacent? gear }.map(&:to_i).inject(:*) }
    
        puts "Part 2:", part2
      end
    
      private
    
      def get(x, y = -1)
        y = x.y if x.is_a?(Point)
        x = x.x if x.is_a?(Point)
        return unless (0..@dim[:width]-1).include?(x) && (0..@dim[:height]-1).include?(y)
    
        @map[y * @dim[:width] + x % @dim[:width]]
      end
    end
    
  • bugsmith
    link
    fedilink
    3
    edit-2
    2 years ago

    Edit: Updated now with part 2.

    Managed to have a crack at this a bit earlier today, I’ve only done Part 01 so far. I’ll update with Part 02 later.

    I tackled this with the personal challenge of not loading the entire puzzle input into memory, which would have made this a bit easier.

    Solution in Rust 🦀

    View formatted on GitLab

    use std::{
        env, fs,
        io::{self, BufRead, BufReader, Read},
    };
    
    fn main() -> io::Result<()> {
        let args: Vec = env::args().collect();
        let filename = &args[1];
        let file1 = fs::File::open(filename)?;
        let file2 = fs::File::open(filename)?;
        let reader1 = BufReader::new(file1);
        let reader2 = BufReader::new(file2);
    
        println!("Part one: {}", process_part_one(reader1));
        println!("Part two: {}", process_part_two(reader2));
        Ok(())
    }
    
    fn process_part_one(reader: BufReader) -> u32 {
        let mut lines = reader.lines().peekable();
        let mut prev_line: Option = None;
        let mut sum = 0;
        while let Some(line) = lines.next() {
            let current_line = line.expect("line exists");
            let next_line = match lines.peek() {
                Some(Ok(line)) => Some(line),
                Some(Err(_)) => None,
                None => None,
            };
            match (prev_line, next_line) {
                (None, Some(next)) => {
                    let lines = vec![¤t_line, next];
                    sum += parse_lines(lines, true);
                }
                (Some(prev), Some(next)) => {
                    let lines = vec![&prev, ¤t_line, next];
                    sum += parse_lines(lines, false);
                }
                (Some(prev), None) => {
                    let lines = vec![&prev, ¤t_line];
                    sum += parse_lines(lines, false);
                }
                (None, None) => {}
            }
    
            prev_line = Some(current_line);
        }
        sum
    }
    
    fn process_part_two(reader: BufReader) -> u32 {
        let mut lines = reader.lines().peekable();
        let mut prev_line: Option = None;
        let mut sum = 0;
        while let Some(line) = lines.next() {
            let current_line = line.expect("line exists");
            let next_line = match lines.peek() {
                Some(Ok(line)) => Some(line),
                Some(Err(_)) => None,
                None => None,
            };
            match (prev_line, next_line) {
                (None, Some(next)) => {
                    let lines = vec![¤t_line, next];
                    sum += parse_lines_for_gears(lines, true);
                }
                (Some(prev), Some(next)) => {
                    let lines = vec![&prev, ¤t_line, next];
                    sum += parse_lines_for_gears(lines, false);
                }
                (Some(prev), None) => {
                    let lines = vec![&prev, ¤t_line];
                    sum += parse_lines_for_gears(lines, false);
                }
                (None, None) => {}
            }
    
            prev_line = Some(current_line);
        }
    
        sum
    }
    
    fn parse_lines(lines: Vec<&String>, first_line: bool) -> u32 {
        let mut sum = 0;
        let mut num = 0;
        let mut valid = false;
        let mut char_vec: Vec> = Vec::new();
        for line in lines {
            char_vec.push(line.chars().collect());
        }
        let chars = match first_line {
            true => &char_vec[0],
            false => &char_vec[1],
        };
        for i in 0..chars.len() {
            if chars[i].is_digit(10) {
                // Add the digit to the number
                num = num * 10 + chars[i].to_digit(10).expect("is digit");
    
                // Check the surrounding character for non-period symbols
                for &x in &[-1, 0, 1] {
                    for chars in &char_vec {
                        if (i as isize + x).is_positive() && ((i as isize + x) as usize) < chars.len() {
                            let index = (i as isize + x) as usize;
                            if !chars[index].is_digit(10) && chars[index] != '.' {
                                valid = true;
                            }
                        }
                    }
                }
            } else {
                if valid {
                    sum += num;
                }
                valid = false;
                num = 0;
            }
        }
        if valid {
            sum += num;
        }
        sum
    }
    
    fn parse_lines_for_gears(lines: Vec<&String>, first_line: bool) -> u32 {
        let mut sum = 0;
        let mut char_vec: Vec> = Vec::new();
        for line in &lines {
            char_vec.push(line.chars().collect());
        }
        let chars = match first_line {
            true => &char_vec[0],
            false => &char_vec[1],
        };
        for i in 0..chars.len() {
            if chars[i] == '*' {
                let surrounding_nums = get_surrounding_numbers(&lines, i);
                let product = match surrounding_nums.len() {
                    0 | 1 => 0,
                    _ => surrounding_nums.iter().product(),
                };
                sum += product;
            }
        }
        sum
    }
    
    fn get_surrounding_numbers(lines: &Vec<&String>, gear_pos: usize) -> Vec {
        let mut nums: Vec = Vec::new();
        let mut num: u32 = 0;
        let mut valid = false;
        for line in lines {
            for (i, char) in line.chars().enumerate() {
                if char.is_digit(10) {
                    num = num * 10 + char.to_digit(10).expect("is digit");
                    if [gear_pos - 1, gear_pos, gear_pos + 1].contains(&i) {
                        valid = true;
                    }
                } else if num > 0 && valid {
                    nums.push(num);
                    num = 0;
                    valid = false;
                } else {
                    num = 0;
                    valid = false;
                }
            }
            if num > 0 && valid {
                nums.push(num);
            }
            num = 0;
            valid = false;
        }
        nums
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
    
        const INPUT: &str = "467..114..
    ...*......
    ..35..633.
    ......#...
    617*......
    .....+.58.
    ..592.....
    ......755.
    ...$.*....
    .664.598..";
    
        #[test]
        fn test_process_part_one() {
            let input_bytes = INPUT.as_bytes();
            assert_eq!(4361, process_part_one(BufReader::new(input_bytes)));
        }
    
        #[test]
        fn test_process_part_two() {
            let input_bytes = INPUT.as_bytes();
            assert_eq!(467835, process_part_two(BufReader::new(input_bytes)));
        }
    }
    
  • janAkali
    link
    fedilink
    English
    6
    edit-2
    2 years ago

    That was not fun to solve. Lots of ugly code. This is a less ugly second version. Language: Nim.

    day_03.nim

  • @[email protected]
    cake
    link
    fedilink
    English
    3
    edit-2
    2 years ago

    Language: C#

    I aimed at keeping it as simple and short as reasonably possible this time, no overbuilding here!

    I even used a goto to let me break out of multiple loops at once 🤮 (I had to look up how they worked!) I would totally fail me in a code review!

    One solution for both
    internal class Day3 : IRunnable
        {
            public void Run()
            {
                var input = File.ReadAllLines("Days/Three/Day3Input.txt");
                int sum = 0;
                string numStr = "";
                var starMap = new Dictionary<(int,int),List>();
                for (int i = 0; i < input.Length; i++)           
                    for (int j = 0; j < input[i].Length; j++)
                    {
                        if (char.IsDigit(input[i][j]))                    
                            numStr += input[i][j];                    
                        if (numStr.Length > 0 && (j == input[i].Length - 1 || !char.IsDigit(input[i][j + 1])))
                        {
                            for (int k = Math.Max(0, i - 1); k < Math.Min(i + 2, input.Length); k++)                        
                                for (int l = Math.Max(0, j - numStr.Length); l < Math.Min(j + 2, input[i].Length); l++)                            
                                    if (!char.IsDigit(input[k][l]) && input[k][l] != '.')
                                    {
                                        sum += int.Parse(numStr);
                                        if (input[k][l] == '*')
                                        {
                                            if (starMap.ContainsKey((k, l)))                                        
                                                starMap[(k, l)].Add(int.Parse(numStr));                                        
                                            else
                                                starMap.Add((k,l),new List { int.Parse(numStr) });
                                        }
                                        goto endSymbSearch;
                                    }                           
                        endSymbSearch:
                            numStr = "";
                        }
                    }            
                Console.WriteLine("Result1:"+sum.ToString());
                Console.WriteLine("Result2:" + starMap.Where(sm => sm.Value.Count == 2).Sum(sm => sm.Value[0] * sm.Value[1]));
            }
        }
    
    
  • @[email protected]
    link
    fedilink
    12 years ago

    [LANGUAGE: C#]

    I kept trying to create clever solutions, but ended up falling back on regex when it was taking to long. THE TLDR is we scan the list of strings for a symbol, then parse the three lines above, below and inline with the symbol for digits. Then we try and match the indexes of the match and the area around the symbol. Part 2 was a small modification, and was mostly about getting the existing code to conform the data into a pattern for each of the three lines.

    Part 1

    static char[] Symbols = { '@', '#', '$', '%', '&', '*', '/', '+', '-', '=' };
    string pattern = @"\d+";
    static List? list;
    list = new List((await File.ReadAllLinesAsync(@".\Day 3\PuzzleInput.txt")));
    
    int count = 0;
    for (int row = 0; row < list.Count; row++)
    {
        for (int col = 0; col < list[row].Length; col++)
        {
            var c = list[row][col];
            if (c == '.')
            {
                continue;
            }
    
            if (Symbols.Contains(c))
            {
                var res = Calculate(list[row - 1], col);
                res += Calculate(list[row], col);
                res += Calculate(list[row + 1], col);
                count += res;
            }
    
        }
    }
    Console.WriteLine(count);
    
    private static int Calculate(string line, int col)
    {
        List indexesToCheck = new List { col - 1, col, col + 1 };
        int count = 0;
        MatchCollection matches = Regex.Matches(line, pattern);
    
        foreach (Match match in matches)
        {
            string number = match.Value;
    
            if (AnyIndexInList(indexesToCheck, match.Index, match.Length))
            {
                count += Int32.Parse(number);
            }
        }
        return count;
    }
    
    static bool AnyIndexInList(List list, int startIndex, int length)
    {
        for (int i = startIndex; i < startIndex + length; i++)
        {
            if (list.Contains(i))
            {
                return true;
            }
        }
        return false;
    }
    

    Part 2:

    list = new List((await File.ReadAllLinesAsync(@".\Day 3\PuzzleInput.txt")));
    
    int count = 0;
    for (int row = 0; row < list.Count; row++)
    {
        for (int col = 0; col < list[row].Length; col++)
        {
            var c = list[row][col];
            if (c == '.')
                continue;
            
            if (c == '*')
            {
                var res1 = Calculate2(list[row - 1], col);
                var res2 = Calculate2(list[row], col);
                var res3 = Calculate2(list[row + 1], col);
    
                count += (res1, res2, res3) switch 
                {
                    {res1: not null, res2: null, res3: null } when  res1[1] != null => res1[0].Value * res1[1].Value,
                    {res1:  null, res2: not null, res3: null } when res2[1] != null => res2[0].Value * res2[1].Value,
                    {res1:  null, res2: null, res3: not null } when res3[1] != null => res3[0].Value * res3[1].Value,
    
                    {res1: not null, res2: not null, res3: null } => res1[0].Value * res2[0].Value,
                    {res1: not null, res2: null, res3: not null } => res1[0].Value * res3[0].Value,
                    {res1: null, res2: not null, res3: not null } => res2[0].Value * res3[0].Value,
                    {res1: not null, res2: not null, res3: not null } => res1[0].Value * res2[0].Value * res3[0].Value,
    
                    _ => 0
                } ;
            }
        }
    }
                    
    Console.WriteLine(count);
    
    
    private static int?[]? Calculate2(string line, int col)
    {
        List indexesToCheck = new List { col - 1, col, col + 1 };
        int?[]? count = null;
        MatchCollection matches = Regex.Matches(line, pattern);
    
        foreach (Match match in matches)
        {
            string number = match.Value;
    
            if (AnyIndexInList(indexesToCheck, match.Index, match.Length))
            {
                if (count == null)
                    count = new int?[2] { Int32.Parse(number), null };
                else {
                    count[1] = Int32.Parse(number);
                };
            }
        }
        return count;
    }
    
  • @[email protected]
    link
    fedilink
    22 years ago

    Factor on github:

    USING: arrays io.encodings.utf8 io.files kernel make math
    math.intervals math.parser prettyprint sequences
    sequences.extras splitting unicode ;
    IN: aoc-2023.day03
    
    : symbol-indices ( line -- seq )                        ! "*..$.*..7."
      [ ".0123456789" member? not ] find-all [ first ] map  ! { 0 3 5 }
    ;
    
    : num-spans ( line -- seq )                                ! ".664.598.."
      >array [ over digit? [ nip ] [ 2drop f ] if ] map-index  ! { f 1 2 3 f 5 7 }
      { f } split harvest                                      ! { { 1 2 3 } { 5 7 } }
      [ [ first ] [ last ] bi 2array ] map                     ! { { 1 3 } { 5 7 } }
    ;
    
    : adjacent? ( num-span symbol-indices -- ? )  ! { 1 3 } { 3 5 7 }
      swap [ first 1 - ] [ last 1 + ] bi [a,b]    ! { 3 5 7 } [0,4]
      '[ _ interval-contains? ] any?              ! t
    ;
    
    : part-numbers ( line nearby-symbol-indices -- seq )  ! ".664.598.." { 0 5 6 }
      [ dup num-spans ] dip                               ! ".664.598.." { { 1 3 } { 5 7 } } { 0 5 6 }
      '[ _ adjacent? ] filter                             ! ".664.598.." { { 1 3 } { 5 7 } }
      swap '[ first2 1 + _ subseq string>number ] map     ! { 664 598 }
    ;
    
    : part1 ( -- )
      "vocab:aoc-2023/day03/input.txt" utf8 file-lines             ! lines
      [ [ symbol-indices ] map ] keep                              ! lines-symbol-idxs lines
      [                                                            ! lines-symbol-idxs line line#
        pick swap [ 1 - ?nth-of ] [ nth-of ] [ 1 + ?nth-of ] 2tri  ! lines-symbol-idxs line prev-sym-idxs cur-sym-idxs next-sym-idxs
        3append part-numbers sum                                   ! lines-symbol-idxs line-parts-sum
      ] map-index sum nip .                                        ! total .
    ;
    
    : star-indices ( line -- seq )          ! ".*.$.*...*"
      [ CHAR: * = ] find-all [ first ] map  ! { 1 5 9 }
    ;
    
    : gears ( line prev-line next-line -- seq-of-pairs )        ! ".*.$*.*..." ".........." ".664.598..455"
      pick star-indices                                         ! ".*.$*.*..." ".........." ".664.598..455" { 1 4 6 }
      [ 1array '[ _ part-numbers ] [ 3dup ] dip tri@ 3append ]  ! ".*.$*.*..." ".........." ".664.598..455" { { 664 } { 664 598 } { 598 } }
      [ length 2 = ] map-filter [ 3drop ] dip                   ! { { 664 598 } }
    ;
    
    : part2 ( -- )
      "vocab:aoc-2023/day03/input.txt" utf8 file-lines  ! lines
      dup [                                             ! lines line i
        pick swap [ 1 - ?nth-of ] [ 1 + ?nth-of ] 2bi   ! lines line prev next
        gears [ product ] map-sum                       ! lines line-ratio-sum
      ] map-index sum nip .                             ! total .
    ;
    
  • @[email protected]
    link
    fedilink
    2
    edit-2
    2 years ago

    Language: C

    Part 2 stumped me for a little bit, it wasn’t an obvious extension of part 1. Part 1 was about numbers (with one or more …) while part 2 worked from the symbols (with exactly two …). Going the other way would require more bookkeeping to avoid double counting.

    And for the implementation: if you loop over the grid and check surrounding cells for digits you’d have to account for a bunch of cases, e.g. NW/N or N/NE being part of the same number or NW and NE being part of separate numbers. And you’d have to parse the numbers again. But building a graph or reference list of some sort is both unergonomic with C and not necessarily any simpler.

    I ended up just writing out the cases, and honestly it didn’t turn out too bad.

    GitHub link

    Abridged code
    int main(int argc, char **argv)
    {
    	static char G[GSZ][GSZ];
    	static int N[GSZ][GSZ];
    	int p1=0,p2=0, h=0, x,y, dx,dy, n=0,sym=0,r;
    	
    	for (h=0; fgets(&G[h+1][1], GSZ-1, stdin); h++)
    		assert(h < GSZ);
    
    	/*
    	 * Pass 1: parse numbers and solve part 1. For every digit in
    	 * G, the full number it is part of is stored in N.
    	 */
    	for (y=1; y<=h; y++)
    	for (x=1; G[y][x]; x++)
    		if (isdigit(G[y][x])) {
    			n = n*10 + G[y][x]-'0';
    
    			for (dy=-1; dy<2; dy++)
    			for (dx=-1; dx<2; dx++)
    				sym = sym || (x && y &&
    				    G[y+dy][x+dx] != '.' &&
    				    ispunct(G[y+dy][x+dx]));
    		} else {
    			for (dx=-1; isdigit(G[y][x+dx]); dx--)
    				N[y][x+dx] = n;
    			if (sym)
    				p1 += n;
    			n = sym = 0;
    		}
    
    	/*
    	 * Pass 2: solve part 2 by finding all '*', then counting and
    	 * multiplying adjecent numbers.
    	 *
    	 * Horizontal adjecency is trivial but vertical/diagonal has
    	 * two situations: if there's a digit directly North of the '+',
    	 * it must be a single number: NW and NE would connect to it.
    	 * If N isn't a digit, digits in NW and NE belong to separate
    	 * numbers.
    	 */
    	for (y=1; y<=h; y++)
    	for (x=1; G[y][x]; x++) {
    		if (G[y][x] != '*')
    			continue;
    
    		n = 0; r = 1;
    
    		if (N[y][x-1]) { n++; r *= N[y][x-1]; }
    		if (N[y][x+1]) { n++; r *= N[y][x+1]; }
    
    		if (N[y-1][x]) { n++; r *= N[y-1][x]; } else {
    			if (N[y-1][x-1]) { n++; r *= N[y-1][x-1]; }
    			if (N[y-1][x+1]) { n++; r *= N[y-1][x+1]; }
    		}
    
    		if (N[y+1][x]) { n++; r *= N[y+1][x]; } else {
    			if (N[y+1][x-1]) { n++; r *= N[y+1][x-1]; }
    			if (N[y+1][x+1]) { n++; r *= N[y+1][x+1]; }
    		}
    
    		if (n == 2)
    			p2 += r;
    	}
    
    	printf("%d %d\n", p1, p2);
    	return 0;
    }
    
  • @[email protected]
    link
    fedilink
    2
    edit-2
    2 years ago

    Dart Solution

    Holy moley, if this year is intended to be impervious to AI solution, it’s also working pretty well as a filter to this paltry Human Intelligence.

    Find interesting symbols, look around for digits, and expand these into numbers. A dirty hacky solution leaning on a re-used Grid class. Not recommended.

    late ListGrid grid;
    
    Index look(Index ix, Index dir) {
      var here = ix;
      var next = here + dir;
      while (grid.isInBounds(next) &amp;&amp; '1234567890'.contains(grid.at(next))) {
        here = next;
        next = here + dir;
      }
      return here;
    }
    
    /// Return start and end indices of a number at a point.
    (Index, Index) expandNumber(Index ix) =>
        (look(ix, Grid.dirs['L']!), look(ix, Grid.dirs['R']!));
    
    int parseNumber((Index, Index) e) => int.parse([
          for (var i = e.$1; i != e.$2 + Grid.dirs['R']!; i += Grid.dirs['R']!)
            grid.at(i)
        ].join());
    
    /// Return de-duplicated positions of all numbers near the given symbols.
    nearSymbols(Set syms) => [
          for (var ix in grid.indexes.where((i) => syms.contains(grid.at(i))))
            {
              for (var n in grid
                  .near8(ix)
                  .where((n) => ('1234567890'.contains(grid.at(n)))))
                expandNumber(n)
            }.toList()
        ];
    
    part1(List lines) {
      grid = ListGrid([for (var e in lines) e.split('')]);
      var syms = lines
          .join('')
          .split('')
          .toSet()
          .difference('0123456789.'.split('').toSet());
      // Find distinct number locations near symbols and sum them.
      return {
        for (var ns in nearSymbols(syms))
          for (var n in ns) n
      }.map(parseNumber).sum;
    }
    
    part2(List lines) {
      grid = ListGrid([for (var e in lines) e.split('')]);
      // Look for _pairs_ of numbers near '*' and multiply them.
      var products = [
        for (var ns in nearSymbols({'*'}).where((e) => e.length == 2))
          ns.map(parseNumber).reduce((s, t) => s * t)
      ];
      return products.sum;
    }
    
    
  • AtegonOPM
    link
    fedilink
    2
    edit-2
    2 years ago

    [Rust] Harder one today, for part 1 I ended up getting stuck for a bit since I wasnt taking numbers at the end of lines into account and in part 2 I defined my gears vector in the wrong spot and spent a bit debugging that

    Code

    (lemmy removes some chars, all chars are in code link)

    use std::fs;
    
    fn part1(input: String) -> u32 {
        let lines = input.lines().collect::>();
        let mut sum = 0;
    
        for i in 0..lines.len() {
            let mut num = 0;
            let mut valid = false;
            let chars = lines[i].chars().collect::>();
    
            for j in 0..chars.len() {
                let character = chars[j];
                let parts = ['*', '#', '+', '$', '/', '%', '=', '-', '&amp;', '@'];
    
                if character.is_digit(10) {
                    num = num * 10 + character.to_digit(10).unwrap();
    
                    if i > 0 {
                        if parts.contains(&amp;lines[i - 1].chars().collect::>()[j]) {
                            valid = true;
                        }
    
                        if j > 0 {
                            if parts.contains(&amp;lines[i - 1].chars().collect::>()[j - 1]) {
                                valid = true;
                            }
                        }
    
                        if j &lt; chars.len() - 1 {
                            if parts.contains(&amp;lines[i - 1].chars().collect::>()[j + 1]) {
                                valid = true;
                            }
                        }
                    }
    
                    if i &lt; lines.len() - 1 {
                        if parts.contains(&amp;lines[i + 1].chars().collect::>()[j]) {
                            valid = true;
                        }
    
                        if j > 0 {
                            if parts.contains(&amp;lines[i + 1].chars().collect::>()[j - 1]) {
                                valid = true;
                            }
                        }
    
                        if j &lt; chars.len() - 1 {
                            if parts.contains(&amp;lines[i + 1].chars().collect::>()[j + 1]) {
                                valid = true;
                            }
                        }
                    }
    
                    if j > 0 {
                        if parts.contains(&amp;lines[i].chars().collect::>()[j - 1]) {
                            valid = true;
                        }
                    }
    
                    if j &lt; chars.len() - 1 {
                        if parts.contains(&amp;lines[i].chars().collect::>()[j + 1]) {
                            valid = true;
                        }
                    }
                }
                else {
                    if valid == true {
                        sum += num;
                    }
    
                    num = 0;
                    valid = false;
                }
    
                if j == chars.len() - 1 {
                    if valid == true {
                        sum += num;
                    }
    
                    num = 0;
                    valid = false;
                }
            }
        }
    
        return sum;
    }
    
    fn part2(input: String) -> u32 {
        let lines = input.lines().collect::>();
        let mut gears: Vec&lt;(usize, usize, u32)> = Vec::new();
        let mut sum = 0;
    
        for i in 0..lines.len() {
            let mut num = 0;
            let chars = lines[i].chars().collect::>();
            let mut pos: (usize, usize) = (0, 0);
            let mut valid = false;
    
            for j in 0..chars.len() {
                let character = chars[j];
                let parts = ['*'];
    
                if character.is_digit(10) {
                    num = num * 10 + character.to_digit(10).unwrap();
    
                    if i > 0 {
                        if parts.contains(&amp;lines[i - 1].chars().collect::>()[j]) {
                            valid = true;
                            pos = (i - 1, j);
                        }
    
                        if j > 0 {
                            if parts.contains(&amp;lines[i - 1].chars().collect::>()[j - 1]) {
                                valid = true;
                                pos = (i - 1, j - 1);
                            }
                        }
    
                        if j &lt; chars.len() - 1 {
                            if parts.contains(&amp;lines[i - 1].chars().collect::>()[j + 1]) {
                                valid = true;
                                pos = (i - 1, j + 1);
                            }
                        }
                    }
    
                    if i &lt; lines.len() - 1 {
                        if parts.contains(&amp;lines[i + 1].chars().collect::>()[j]) {
                            valid = true;
                            pos = (i + 1, j);
                        }
    
                        if j > 0 {
                            if parts.contains(&amp;lines[i + 1].chars().collect::>()[j - 1]) {
                                valid = true;
                                pos = (i + 1, j - 1);
                            }
                        }
    
                        if j &lt; chars.len() - 1 {
                            if parts.contains(&amp;lines[i + 1].chars().collect::>()[j + 1]) {
                                valid = true;
                                pos = (i + 1, j + 1);
                            }
                        }
                    }
    
                    if j > 0 {
                        if parts.contains(&amp;lines[i].chars().collect::>()[j - 1]) {
                            valid = true;
                            pos = (i, j - 1);
                        }
                    }
    
                    if j &lt; chars.len() - 1 {
                        if parts.contains(&amp;lines[i].chars().collect::>()[j + 1]) {
                            valid = true;
                            pos = (i, j + 1);
                        }
                    }
                }
                else {
                    if valid == true {
                        let mut current_gear = false;
                        
                        for gear in &amp;gears {
                            if gear.0 == pos.0 &amp;&amp; gear.1 == pos.1 {
                                sum += num * gear.2;
                                current_gear = true;
                                break;
                            }
                        }
                        
                        if !current_gear {
                            let tuple_to_push = (pos.0.clone(), pos.1.clone(), num.clone());
                            gears.push((pos.0.clone(), pos.1.clone(), num.clone()));
                        }
                    }
    
                    num = 0;
                    valid = false;
                }
    
                if j == chars.len() - 1 {
                    if valid == true {
                        let mut current_gear = false;
                        
                        for gear in &amp;gears {
                            if gear.0 == pos.0 &amp;&amp; gear.1 == pos.1 {
                                sum += num * gear.2;
                                current_gear = true;
                                break;
                            }
                        }
                        
                        if !current_gear {
                            let tuple_to_push = (pos.0.clone(), pos.1.clone(), num.clone());
                            gears.push((pos.0.clone(), pos.1.clone(), num.clone()));
                        }
                    }
    
                    num = 0;
                    valid = false;
                }
            }
        }
    
        return sum;
    }
    
    fn main() {
        let input = fs::read_to_string("data/input.txt").unwrap();
    
        println!("{}", part1(input.clone()));
        println!("{}", part2(input.clone()));
    }
    

    Code Link

  • @[email protected]
    link
    fedilink
    1
    edit-2
    2 years ago

    Did this in Odin

    Here’s a tip: if you are using a language / standard library that doesn’t have a set, you can mimic it with a map from your key to a nullary (in this case an empty struct)

    formatted code

    package day3
    
    import "core:fmt"
    import "core:strings"
    import "core:unicode"
    import "core:strconv"
    
    flood_get_num :: proc(s: string, i: int) -> (parsed: int, pos: int) {
        if !unicode.is_digit(rune(s[i])) do return -99999, -1
    
        pos = strings.last_index_proc(s[:i+1], proc(r:rune)->bool{return !unicode.is_digit(r)})
        pos += 1
    
        ok: bool
        parsed, ok = strconv.parse_int(s[pos:])
    
        return parsed, pos
    }
    
    p1 :: proc(input: []string) {
        // wow what a gnarly type
        foundNumSet := make(map[[2]int]struct{})
        defer delete(foundNumSet)
    
        total := 0
    
        for y in 0..
    • @[email protected]
      link
      fedilink
      32 years ago

      Here’s a tip: if you are using a language / standard library that doesn’t have a map,

      Probably meant to write ‘a set’? Good trick though.

    • @[email protected]
      link
      fedilink
      12 years ago

      hmm, my code keeps getting truncated at for y in .., anyone have any idea why? Maybe the “<” right after that confuses a parser somewhere?

      • AtegonOPM
        link
        fedilink
        22 years ago

        Lemmy doesn’t handle certain characters well currently such as left angle brackets and ampersands due to some sanitization in the back end to stop scripting attacks