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

    • I like it, it’s simple and to the point. I’ve learned that one of the most helpful things to do when solving these puzzles is to not make it more complicated than it needs to be, and you certainly succeeded better at that today than I did.

      • cacheson ( cacheson@kbin.social ) 
        link
        fedilink
        arrow-up
        1
        ·
        3 years ago

        My solution for day 1 part 1 was simple and to the point. The other ones are getting increasingly less so. You’re right that sometimes it’s best not to get too fancy, but I think soon I may have to break out such advanced programming techniques as “functions” and maybe “objects”, instead of writing increasingly convoluted piles of nested loops. xD

  • Rust

    I’ve been using Regexes for every day so far, this time it helped in finding numbers along with their start and end position in a line. For the second part I mostly went with the approach of part 1 which was to look at all numbers and then figure out if it has a part symbol around it. Only in part 2 I saved all numbers next to a gear * in a hash table that maps each gear position to a list of adjacent numbers. Then in the end I can just look at all gears with exactly 2 numbers attached.

    Also it has to be said, multiplying two numbers is the exact opposite of getting their ratio!

  • StreetKid ( StreetKid@reddthat.com ) 
    link
    fedilink
    English
    arrow-up
    3
    ·
    3 years ago

    My Python solution for part 1 and part 2. I really practice my regex skills.

    spoiler
    #!/usr/bin/python3
    
    import re
    
    value_re = '(\d+)'
    symbol_re = '[^\d.]'
    gear_re = '(\*)'
    
    def main():
        input = list()
        with open("input.txt", 'r') as in_file:
            for line in in_file:
                input.append(line.strip('\n'))
        length = len(input)
        width = len(input[0])
        value_sum = 0
        for idx, line in enumerate(input):
            for match in re.finditer(value_re, line):
                for line_mask in input[max(idx - 1, 0):min(idx + 2, length)]:
                    valid_chars = line_mask[max(match.span()[0] - 1, 0):min(match.span()[1] + 1, width)]
                    if re.search(symbol_re, valid_chars):
                        value_sum += int(match[0])
                        break
        print(f"Value sum = {value_sum}")
    
        gear_ratio = 0
        for idx, line in enumerate(input):
            for match in re.finditer(gear_re, line):
                valid_lines = input[max(idx - 1, 0):min(idx + 2, length)]
                min_range = max(match.span()[0] - 1, 0)
                max_range = min(match.span()[1], width)
                num_of_adjacent = 0
                temp_gear_ratio = 1
                for valid_line in valid_lines:
                    for match in re.finditer(value_re, valid_line):
                        if match.span()[0] in range(min_range,max_range + 1) or match.span()[1] - 1 in range(min_range,max_range + 1):
                            num_of_adjacent += 1
                            temp_gear_ratio *= int(match[0])
                if num_of_adjacent == 2:
                    gear_ratio += temp_gear_ratio
        print(f"Gear ratio = {gear_ratio}")
    
    if __name__ == '__main__':
        main()
    
  • Andy ( Andy@programming.dev ) 
    link
    fedilink
    arrow-up
    2
    ·
    edit-2
    3 years ago

    Factor on github (with comments and imports):

    : symbol-indices ( line -- seq )
      [ ".0123456789" member? not ] find-all [ first ] map
    ;
    
    : num-spans ( line -- seq )
      >array [ over digit? [ nip ] [ 2drop f ] if ] map-index
      { f } split harvest
      [ [ first ] [ last ] bi 2array ] map
    ;
    
    : adjacent? ( num-span symbol-indices -- ? )
      swap [ first 1 - ] [ last 1 + ] bi [a,b]
      '[ _ interval-contains? ] any?
    ;
    
    : part-numbers ( line nearby-symbol-indices -- seq )
      [ dup num-spans ] dip
      '[ _ adjacent? ] filter
      swap '[ first2 1 + _ subseq string>number ] map
    ;
    
    : part1 ( -- )
      "vocab:aoc-2023/day03/input.txt" utf8 file-lines
      [ [ symbol-indices ] map ] keep
      [
        pick swap [ 1 - ?nth-of ] [ nth-of ] [ 1 + ?nth-of ] 2tri
        3append part-numbers sum
      ] map-index sum nip .
    ;
    
    : star-indices ( line -- seq )
      [ CHAR: * = ] find-all [ first ] map
    ;
    
    : gears ( line prev-line next-line -- seq-of-pairs )
      pick star-indices
      [ 1array '[ _ part-numbers ] [ 3dup ] dip tri@ 3append ]
      [ length 2 = ] map-filter [ 3drop ] dip
    ;
    
    : part2 ( -- )
      "vocab:aoc-2023/day03/input.txt" utf8 file-lines
      dup [
        pick swap [ 1 - ?nth-of ] [ 1 + ?nth-of ] 2bi
        gears [ product ] map-sum
      ] map-index sum nip .
    ;
    
  • Ategon ( Ategon@programming.dev ) OP
    link
    fedilink
    arrow-up
    2
    ·
    edit-2
    3 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 = ['*', '#', '+', '$', '/', '%', '=', '-', '&', '@'];
    
                if character.is_digit(10) {
                    num = num * 10 + character.to_digit(10).unwrap();
    
                    if i > 0 {
                        if parts.contains(&lines[i - 1].chars().collect::>()[j]) {
                            valid = true;
                        }
    
                        if j > 0 {
                            if parts.contains(&lines[i - 1].chars().collect::>()[j - 1]) {
                                valid = true;
                            }
                        }
    
                        if j < chars.len() - 1 {
                            if parts.contains(&lines[i - 1].chars().collect::>()[j + 1]) {
                                valid = true;
                            }
                        }
                    }
    
                    if i < lines.len() - 1 {
                        if parts.contains(&lines[i + 1].chars().collect::>()[j]) {
                            valid = true;
                        }
    
                        if j > 0 {
                            if parts.contains(&lines[i + 1].chars().collect::>()[j - 1]) {
                                valid = true;
                            }
                        }
    
                        if j < chars.len() - 1 {
                            if parts.contains(&lines[i + 1].chars().collect::>()[j + 1]) {
                                valid = true;
                            }
                        }
                    }
    
                    if j > 0 {
                        if parts.contains(&lines[i].chars().collect::>()[j - 1]) {
                            valid = true;
                        }
                    }
    
                    if j < chars.len() - 1 {
                        if parts.contains(&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<(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(&lines[i - 1].chars().collect::>()[j]) {
                            valid = true;
                            pos = (i - 1, j);
                        }
    
                        if j > 0 {
                            if parts.contains(&lines[i - 1].chars().collect::>()[j - 1]) {
                                valid = true;
                                pos = (i - 1, j - 1);
                            }
                        }
    
                        if j < chars.len() - 1 {
                            if parts.contains(&lines[i - 1].chars().collect::>()[j + 1]) {
                                valid = true;
                                pos = (i - 1, j + 1);
                            }
                        }
                    }
    
                    if i < lines.len() - 1 {
                        if parts.contains(&lines[i + 1].chars().collect::>()[j]) {
                            valid = true;
                            pos = (i + 1, j);
                        }
    
                        if j > 0 {
                            if parts.contains(&lines[i + 1].chars().collect::>()[j - 1]) {
                                valid = true;
                                pos = (i + 1, j - 1);
                            }
                        }
    
                        if j < chars.len() - 1 {
                            if parts.contains(&lines[i + 1].chars().collect::>()[j + 1]) {
                                valid = true;
                                pos = (i + 1, j + 1);
                            }
                        }
                    }
    
                    if j > 0 {
                        if parts.contains(&lines[i].chars().collect::>()[j - 1]) {
                            valid = true;
                            pos = (i, j - 1);
                        }
                    }
    
                    if j < chars.len() - 1 {
                        if parts.contains(&lines[i].chars().collect::>()[j + 1]) {
                            valid = true;
                            pos = (i, j + 1);
                        }
                    }
                }
                else {
                    if valid == true {
                        let mut current_gear = false;
                        
                        for gear in &gears {
                            if gear.0 == pos.0 && 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 &gears {
                            if gear.0 == pos.0 && 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

  • morrowind ( morrowind@lemmy.ml ) 
    link
    fedilink
    arrow-up
    2
    ·
    3 years ago

    Crystal

    My computer crashed right most of the way through and I lost everything, so this was even more frustrating than it should have been
    Also damn, lemmy’s tabs are massive

    will post part 2 when I get to it

    input = File.read("input.txt")
    
    lines = input.lines.map(&.chars)
    
    sum = 0
    num_marker = nil
    lines.each_with_index do |line, y|
    	line.each_with_index do |char, x|
    		num_marker ||= x if char.number?
    	
    		if (!char.number? || x == line.size-1) && num_marker
    			if check_symbol(y, (num_marker-1)..x, lines)
    				sum += lines[y][(char.number? ? num_marker..x : num_marker...x)].join.to_i 
    			end
    			num_marker = nil
    end end end
    puts sum
    
    def check_symbol(y, rx, lines)
    	carr = [ lines[y][rx.begin]?, lines[y][rx.end]? ]
    	carr += rx.map {|x| lines[y-1][x]? } if y > 0  
    	carr += rx.map {|x| lines[y+1][x]? } if y < lines.size-1 
    
    	carr.each {|c| return true if c && c != '.' && !c.number?}
    	false
    end
    
  • Nighed ( Nighed@sffa.community ) 
    link
    fedilink
    English
    arrow-up
    2
    ·
    edit-2
    3 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]));
            }
        }
    
    
  • Jummit ( Jummit@lemmy.one ) 
    link
    fedilink
    arrow-up
    2
    ·
    3 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)!;
    };