this post was submitted on 05 Dec 2024
26 points (100.0% liked)

Advent Of Code

1012 readers
2 users here now

An unofficial home for the advent of code community on programming.dev!

Advent of Code is an annual Advent calendar of small programming puzzles for a variety of skill sets and skill levels that can be solved in any programming language you like.

AoC 2024

Solution Threads

M T W T F S S
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25

Rules/Guidelines

Relevant Communities

Relevant Links

Credits

Icon base by Lorc under CC BY 3.0 with modifications to add a gradient

console.log('Hello World')

founded 2 years ago
MODERATORS
 

Day 5: Print Queue

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

(page 2) 7 comments
sorted by: hot top controversial new old
[โ€“] Deebster@programming.dev 1 points 2 months ago

Rust

I don't love this code, but I didn't initially use a hashmap and it runs so fast it wasn't worth the time to refactor.

use std::{cmp::Ordering, fs, str::FromStr};

use color_eyre::eyre::{Report, Result};
use itertools::Itertools;

struct Updates(Vec<Vec<isize>>);

impl FromStr for Updates {
    type Err = Report;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let pages = s
            .lines()
            .map(|l| l.split(",").map(|n| n.parse::<isize>()).collect())
            .collect::<Result<_, _>>()?;
        Ok(Self(pages))
    }
}

impl Updates {
    fn get_valid(&self, rules: &OrderingRules) -> Self {
        let pages = self
            .0
            .clone()
            .into_iter()
            .filter(|p| rules.validate(p))
            .collect();
        Self(pages)
    }

    fn get_invalid(&self, rules: &OrderingRules) -> Self {
        let pages = self
            .0
            .clone()
            .into_iter()
            .filter(|p| !rules.validate(p))
            .collect();
        Self(pages)
    }
}

struct OrderingRules(Vec<(isize, isize)>);

impl OrderingRules {
    fn validate(&self, pnums: &Vec<isize>) -> bool {
        self.0.iter().all(|(a, b)| {
            let Some(a_pos) = pnums.iter().position(|&x| x == *a) else {
                return true;
            };
            let Some(b_pos) = pnums.iter().position(|&x| x == *b) else {
                return true;
            };
            a_pos < b_pos
        })
    }

    fn fix(&self, pnums: &Vec<isize>) -> Vec<isize> {
        let mut v = pnums.clone();

        v.sort_by(|a, b| {
            let mut fr = self
                .0
                .iter()
                .filter(|(ra, rb)| (ra == a || ra == b) && (rb == a || rb == b));
            if let Some((ra, _rb)) = fr.next() {
                if ra == a {
                    Ordering::Less
                } else {
                    Ordering::Greater
                }
            } else {
                Ordering::Equal
            }
        });
        v
    }
}

impl FromStr for OrderingRules {
    type Err = Report;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let v = s
            .lines()
            .map(|l| {
                l.splitn(2, "|")
                    .map(|n| n.parse::<isize>().unwrap())
                    .collect_tuple()
                    .ok_or_else(|| Report::msg("Rules need two items"))
            })
            .collect::<Result<_, _>>()?;
        Ok(Self(v))
    }
}

fn parse(s: &str) -> Result<(OrderingRules, Updates)> {
    let parts: Vec<_> = s.splitn(2, "\n\n").collect();
    let rules = OrderingRules::from_str(parts[0])?;
    let updates = Updates::from_str(parts[1])?;
    Ok((rules, updates))
}

fn part1(filepath: &str) -> Result<isize> {
    let input = fs::read_to_string(filepath)?;
    let (rules, updates) = parse(&input)?;
    let res = updates
        .get_valid(&rules)
        .0
        .iter()
        .map(|v| v[v.len() / 2])
        .sum();
    Ok(res)
}

fn part2(filepath: &str) -> Result<isize> {
    let input = fs::read_to_string(filepath)?;
    let (rules, updates) = parse(&input)?;
    let res = updates
        .get_invalid(&rules)
        .0
        .iter()
        .map(|v| rules.fix(&v))
        .map(|v| v[v.len() / 2])
        .sum();
    Ok(res)
}

fn main() -> Result<()> {
    color_eyre::install()?;

    println!("Part 1: {}", part1("d05/input.txt")?);
    println!("Part 2: {}", part2("d05/input.txt")?);
    Ok(())
}
[โ€“] reboot6675@sopuli.xyz 1 points 2 months ago (1 children)

Go

Using a map to store u|v relations. Part 2 sorting with a custom compare function worked very nicely

spoiler

func main() {
	file, _ := os.Open("input.txt")
	defer file.Close()
	scanner := bufio.NewScanner(file)

	mapPages := make(map[string][]string)
	rulesSection := true
	middleSumOk := 0
	middleSumNotOk := 0

	for scanner.Scan() {
		line := scanner.Text()
		if line == "" {
			rulesSection = false
			continue
		}

		if rulesSection {
			parts := strings.Split(line, "|")
			u, v := parts[0], parts[1]
			mapPages[u] = append(mapPages[u], v)
		} else {
			update := strings.Split(line, ",")
			isOk := true

			for i := 1; i < len(update); i++ {
				u, v := update[i-1], update[i]
				if !slices.Contains(mapPages[u], v) {
					isOk = false
					break
				}
			}

			middlePos := len(update) / 2
			if isOk {
				middlePage, _ := strconv.Atoi(update[middlePos])
				middleSumOk += middlePage
			} else {
				slices.SortFunc(update, func(u, v string) int {
					if slices.Contains(mapPages[u], v) {
						return -1
					} else if slices.Contains(mapPages[v], u) {
						return 1
					}
					return 0
				})
				middlePage, _ := strconv.Atoi(update[middlePos])
				middleSumNotOk += middlePage
			}
		}
	}

	fmt.Println("Part 1:", middleSumOk)
	fmt.Println("Part 2:", middleSumNotOk)
}

load more comments (1 replies)
[โ€“] Hammerheart@programming.dev 1 points 2 months ago* (last edited 2 months ago)

Python

(Part 1) omg I can't believe this actually worked first try!

with open('input') as data:
    parts = data.read().rstrip().split("\n\n")
    ordering_rules = parts[0].split("\n")
    updates = parts[1].split("\n")

correct_updates = []
middle_updates = []

def find_relevant_rules(pg_num: str, rules: list[str]) -> list[str] | None:
    for rule in rules:
        return list(filter(lambda x: x.split("|")[0] == pg_num, rules))

def interpret_rule(rule: str) -> list[str]:
    return rule.split("|")

def interpret_update(update: str) -> list[str]:
    return update.split(",")

def find_middle_update_index(update: list[str]) -> int:
    num_of_elements = len(update)
    return num_of_elements // 2

for update in updates:
    is_correct = True
    for i, page in enumerate(interpret_update(update)):
       rules_to_check = find_relevant_rules(page, ordering_rules) 
       for rule in rules_to_check:
           if rule.split("|")[1] in interpret_update(update)[:i]:
               is_correct = False
    if is_correct:
        correct_updates.append(update)

for update in correct_updates:
    split_update = update.split(",")
    middle_updates.append(int(split_update[find_middle_update_index(split_update)]))
print(sum(middle_updates))
[โ€“] JRaccoon@discuss.tchncs.de 1 points 2 months ago (2 children)

Java

Part 2 was an interesting one and my solution kinda feels like cheating. What I did I only changed the validation method from part 1 to return the indexes of incorrectly placed pages and then randomly swapped those around in a loop until the validation passed. I was expecting this to not work at all or take forever to run but surprisingly it only takes three to five seconds to complete.

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.stream.Collectors;

public class Day05 {
    private static final Random random = new Random();

    public static void main(final String[] args) throws IOException {
        final String input = Files.readString(Path.of("2024\\05\\input.txt"), StandardCharsets.UTF_8);
        final String[] inputSplit = input.split("[\r\n]{4,}");

        final List<PageOrderingRule> rules = Arrays.stream(inputSplit[0].split("[\r\n]+"))
            .map(row -> row.split("\\|"))
            .map(row -> new PageOrderingRule(Integer.parseInt(row[0]), Integer.parseInt(row[1])))
            .toList();

        final List<ArrayList<Integer>> updates = Arrays.stream(inputSplit[1].split("[\r\n]+"))
            .map(row -> row.split(","))
            .map(row -> Arrays.stream(row).map(Integer::parseInt).collect(Collectors.toCollection(ArrayList::new)))
            .toList();

        System.out.println("Part 1: " + updates.stream()
            .filter(update -> validate(update, rules).isEmpty())
            .mapToInt(update -> update.get(update.size() / 2))
            .sum()
        );

        System.out.println("Part 2: " + updates.stream()
            .filter(update -> !validate(update, rules).isEmpty())
            .map(update -> fixOrder(update, rules))
            .mapToInt(update -> update.get(update.size() / 2))
            .sum()
        );
    }

    private static Set<Integer> validate(final List<Integer> update, final List<PageOrderingRule> rules) {
        final Set<Integer> invalidIndexes = new HashSet<>();

        for (int i = 0; i < update.size(); i++) {
            final Integer integer = update.get(i);
            for (final PageOrderingRule rule : rules) {
                if (rule.x == integer && update.contains(rule.y) && i > update.indexOf(rule.y)) {
                    invalidIndexes.add(i);
                }
                else if (rule.y == integer && update.contains(rule.x) && i < update.indexOf(rule.x)) {
                    invalidIndexes.add(i);
                }
            }
        }

        return invalidIndexes;
    }

    private static List<Integer> fixOrder(final List<Integer> update, final List<PageOrderingRule> rules) {
        List<Integer> invalidIndexesList = new ArrayList<>(validate(update, rules));

        // Swap randomly until the validation passes
        while (!invalidIndexesList.isEmpty()) {
            Collections.swap(update, random.nextInt(invalidIndexesList.size()), random.nextInt(invalidIndexesList.size()));
            invalidIndexesList = new ArrayList<>(validate(update, rules));
        }

        return update;
    }

    private static record PageOrderingRule(int x, int y) {}
}
load more comments (2 replies)
[โ€“] sleeplessone@lemmy.ml 0 points 2 months ago

Rust

Kinda sorta got day 5 done on time.

use std::cmp::Ordering;

use crate::utils::{bytes_to_num, read_lines};

pub fn solution1() {
    let mut lines = read_input();
    let rules = parse_rules(&mut lines);

    let middle_rules_sum = lines
        .filter_map(|line| {
            let line_nums = rule_line_to_list(&line);
            line_nums
                .is_sorted_by(|&a, &b| is_sorted(&rules, (a, b)))
                .then_some(line_nums[line_nums.len() / 2])
        })
        .sum::<usize>();

    println!("Sum of in-order middle rules = {middle_rules_sum}");
}

pub fn solution2() {
    let mut lines = read_input();
    let rules = parse_rules(&mut lines);

    let middle_rules_sum = lines
        .filter_map(|line| {
            let mut line_nums = rule_line_to_list(&line);

            (!line_nums.is_sorted_by(|&a, &b| is_sorted(&rules, (a, b)))).then(|| {
                line_nums.sort_by(|&a, &b| {
                    is_sorted(&rules, (a, b))
                        .then_some(Ordering::Less)
                        .unwrap_or(Ordering::Greater)
                });

                line_nums[line_nums.len() / 2]
            })
        })
        .sum::<usize>();

    println!("Sum of middle rules = {middle_rules_sum}");
}

fn read_input() -> impl Iterator<Item = String> {
    read_lines("src/day5/input.txt")
}

fn parse_rules(lines: &mut impl Iterator<Item = String>) -> Vec<(usize, usize)> {
    lines
        .take_while(|line| !line.is_empty())
        .fold(Vec::new(), |mut rules, line| {
            let (a, b) = line.as_bytes().split_at(2);
            let a = bytes_to_num(a);
            let b = bytes_to_num(&b[1..]);

            rules.push((a, b));

            rules
        })
}

fn rule_line_to_list(line: &str) -> Vec<usize> {
    line.split(',')
        .map(|s| bytes_to_num(s.as_bytes()))
        .collect::<Vec<_>>()
}

fn is_sorted(rules: &[(usize, usize)], tuple: (usize, usize)) -> bool {
    rules.iter().any(|&r| r == tuple)
}

Reusing my bytes_to_num function from day 3 feels nice. Pretty fun challenge.

load more comments
view more: โ€น prev next โ€บ