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

Advent Of Code

987 readers
14 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 1 year 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 3 weeks 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))
          )))

[–] madmo@programming.dev 1 points 3 weeks ago

Rust

Used a sorted/unsorted comparison to solve the first part, the second part was just filling out the else branch.

use std::{
    cmp::Ordering,
    collections::HashMap,
    io::{BufRead, BufReader},
};

fn main() {
    let mut lines = BufReader::new(std::fs::File::open("input.txt").unwrap()).lines();

    let mut rules: HashMap<u64, Vec<u64>> = HashMap::default();

    for line in lines.by_ref() {
        let line = line.unwrap();

        if line.is_empty() {
            break;
        }

        let lr = line
            .split('|')
            .map(|el| el.parse::<u64>())
            .collect::<Result<Vec<u64>, _>>()
            .unwrap();

        let left = lr[0];
        let right = lr[1];

        if let Some(values) = rules.get_mut(&left) {
            values.push(right);
            values.sort();
        } else {
            rules.insert(left, vec![right]);
        }
    }

    let mut updates: Vec<Vec<u64>> = Vec::default();

    for line in lines {
        let line = line.unwrap();

        let update = line
            .split(',')
            .map(|el| el.parse::<u64>())
            .collect::<Result<Vec<u64>, _>>()
            .unwrap();

        updates.push(update);
    }

    let mut middle_sum = 0;
    let mut fixed_middle_sum = 0;

    for update in updates {
        let mut update_sorted = update.clone();
        update_sorted.sort_by(|a, b| {
            if let Some(rules) = rules.get(a) {
                if rules.contains(b) {
                    Ordering::Less
                } else {
                    Ordering::Equal
                }
            } else {
                Ordering::Equal
            }
        });

        if update.eq(&update_sorted) {
            let middle = update[(update.len() - 1) / 2];
            middle_sum += middle;
        } else {
            let middle = update_sorted[(update_sorted.len() - 1) / 2];
            fixed_middle_sum += middle;
        }
    }

    println!("part1: {} part2: {}", middle_sum, fixed_middle_sum);
}
[–] Deebster@programming.dev 1 points 3 weeks 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 3 weeks 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 3 weeks 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 3 weeks 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
load more comments
view more: β€Ή prev next β€Ί