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

Advent Of Code

1080 readers
1 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
[–] wer2@lemm.ee 1 points 6 months ago

Lisp

Part 1 and 2


(defun p1-process-rules (line)
  (mapcar #'parse-integer (uiop:split-string line :separator "|")))

(defun p1-process-pages (line)
  (mapcar #'parse-integer (uiop:split-string line :separator ",")))

(defun middle (pages)
  (nth (floor (length pages) 2) pages))

(defun check-rule-p (rule pages)
  (let ((p1 (position (car rule) pages))
        (p2 (position (cadr rule) pages)))
    (or (not p1) (not p2) (< p1 p2))))

(defun ordered-p (pages rules)
  (loop for r in rules
        unless (check-rule-p r pages)
          return nil
        finally
           (return t)))

(defun run-p1 (rules-file pages-file) 
  (let ((rules (read-file rules-file #'p1-process-rules))
        (pages (read-file pages-file #'p1-process-pages)))
    (loop for p in pages
          when (ordered-p p rules)
            sum (middle p)
          )))

(defun fix-pages (rules pages)
  (sort pages (lambda (p1 p2) (ordered-p (list p1 p2) rules)) ))

(defun run-p2 (rules-file pages-file) 
  (let ((rules (read-file rules-file #'p1-process-rules))
        (pages (read-file pages-file #'p1-process-pages)))
    (loop for p in pages
          unless (ordered-p p rules)
            sum (middle (fix-pages rules p))
          )))

[–] Deebster@programming.dev 1 points 6 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 6 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)
[–] JRaccoon@discuss.tchncs.de 1 points 6 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)
[–] Quant@programming.dev 1 points 6 months ago

Uiua

This is the first one that caused me some headache because I didn't read the instructions carefully enough.
I kept trying to create a sorted list for when all available pages were used, which got me stuck in an endless loop.

Another fun part was figuring out to use memberof (∈) instead of find (βŒ•) in the last line of FindNext. So much time spent on debugging other areas of the code

Run with example input here

FindNext ← βŠ™(
  ⊑1⍉,
  βŠƒβ–½(β–½Β¬)⊸∈
  βŠ™βŠ™(⊑0⍉.)
  :βŠ™(⟜(β–½Β¬βˆˆ))
)

# find the order of pages for a given set of rules
FindOrder ← (
  β—΄β™­.
  []
  ⍒(βŠ‚FindNext|β‹…(>1β§»))
  βŠ™β—ŒβŠ‚
)

PartOne ← (
  &rs ∞ &fo "input-5.txt"
  βˆ©Β°β–‘Β°βŠŸβŠœβ–‘Β¬βŒ•"\n\n".
  βŠ™(⊜(β–‘βŠœβ‹•β‰ @,.)β‰ @\n.β†˜1)
  ⊜(βŠœβ‹•β‰ @|.)β‰ @\n.

  βŠ™.
  Β€
  ⊞(β—‘(Β°β–‘:)
    ⟜:βŠ™(Β°βŠŸβ‰)
    =2+∩∈
    β–½
    FindOrder
    βŠΈβ‰Β°β–‘:
    βŠ™β—Œ
  )
  ≑◇(⊑⌊÷2β§».)β–½β™­
  /+
)

PartTwo ← (
  &rs ∞ &fo "input-5.txt"
  βˆ©Β°β–‘Β°βŠŸβŠœβ–‘Β¬βŒ•"\n\n".
  βŠ™(⊜(β–‘βŠœβ‹•β‰ @,.)β‰ @\n.β†˜1)
  ⊜(βŠœβ‹•β‰ @|.)β‰ @\n.
  βŠ™.
  ⍜€⊞(
    β—‘(Β°β–‘:)
    ⟜:βŠ™(Β°βŠŸβ‰)
    =2+∩∈
    β–½
    FindOrder
    βŠΈβ‰Β°β–‘:
    βŠŸβˆ©β–‘
  )
  βŠ™β—Œ
  βŠƒ(⊑0)(⊑1)⍉
  ≑◇(⊑⌊÷2β§».)▽¬≑°░
  /+
)

&p "Day 5:"
&pf "Part 1: "
&p PartOne
&pf "Part 2: "
&p PartTwo
[–] sleeplessone@lemmy.ml 0 points 6 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 β€Ί