Ooh that is tricky of them. Good catch!
Ooh that is tricky of them. Good catch!
I did run your code as-is in an ipython REPL to check. These were the results:
# With unmodified `main` function & `import string` not shown
In [4]: with open("inputs/day13.txt", "r") as f:
...: input_data = f.read().strip().replace(',', '').split('\n\n')
...:
In [5]: part_one, part_two = main(input_data)
In [6]: part_one
Out[6]: 39748
In [7]: part_two
Out[7]: 74926346266863
# Then I modified the function to check if x is fractional
In [8]: def main(input_data):
...: part1_total_cost = 0
...: part2_total_cost = 0
...: for machine in input_data:
...: Ax,Ay,Bx,By,Px,Py = [ int(l[2:]) for l in machine.split() if l[-1] in string.digits ]
...: y,r = divmod((Ay * Px - Ax * Py), (Ay * Bx - Ax * By))
...: if r == 0:
...: x = (Px - Bx * y) / Ax
...: if x % 1 == 0:
...: part1_total_cost += 3*x + y
...: y,r = divmod((Ay * (Px+10000000000000) - Ax * (Py+10000000000000)), (Ay * Bx - Ax * By))
...: if r == 0:
...: x = ((Px+10000000000000) - Bx * y) / Ax
...: if x % 1 == 0:
...: part2_total_cost += 3*x + y
...:
...: return part1_total_cost,part2_total_cost
...:
In [9]: part_one, part_two = main(input_data)
In [10]: part_one
Out[10]: 39748.0
In [11]: part_two
Out[11]: 74478585072604.0 # Correct answer for pt 2 of my input
If you’re curious to check against my puzzle input, it’s here
Thank you again for the back & forth, and for sharing your solution!
Thank you so much for your explanation! I kind of found some clues looking up perp dot products & other vector math things, but this breaks it down very nicely.
I implemented your solution in rust, and part 2 failed by +447,761,194,259 (this was using signed 64-bit integers, i64
). When I changed it to use signed 64 bit floating-point f64
and checked that the solution for x
produces a whole number it worked.
This is a really excellent, clean solution! Would you mind breaking down how the piece of linear algebra works (for a shmo like me who doesn’t remember that stuff frum school heh 😅)
Plus one for posteo. I’ve used them for several years now and have had no issues
Real thinker. Messed around with a couple solutions before this one. The gist is to take all the pairwise comparisons given and record them for easy access in a ranking matrix.
For the sample input, this grid would look like this (I left out all the non-present integers, but it would be a 98 x 98 grid where all the empty spaces are filled with Ordering::Equal
):
13 29 47 53 61 75 97
13 = > > > > > >
29 < = > > > > >
47 < < = < < > >
53 < < > = > > >
61 < < > < = > >
75 < < < < < = >
97 < < < < < < =
I discovered this can’t be used for a total order on the actual puzzle input because there were cycles in the pairs given (see how rust changed sort implementations as of 1.81). I used usize
for convenience (I did it with u8
for all the pair values originally, but kept having to cast over and over as usize
). Didn’t notice a performance difference, but I’m sure uses a bit more memory.
Also I Liked the simple_grid
crate a little better than the grid
one. Will have to refactor that out at some point.
use std::{cmp::Ordering, fs::read_to_string};
use simple_grid::Grid;
type Idx = (usize, usize);
type Matrix = Grid<Ordering>;
type Page = Vec<usize>;
fn parse_input(input: &str) -> (Vec<Idx>, Vec<Page>) {
let split: Vec<&str> = input.split("\n\n").collect();
let (pair_str, page_str) = (split[0], split[1]);
let pairs = parse_pairs(pair_str);
let pages = parse_pages(page_str);
(pairs, pages)
}
fn parse_pairs(input: &str) -> Vec<Idx> {
input
.lines()
.map(|l| {
let (a, b) = l.split_once('|').unwrap();
(a.parse().unwrap(), b.parse().unwrap())
})
.collect()
}
fn parse_pages(input: &str) -> Vec<Page> {
input
.lines()
.map(|l| -> Page {
l.split(",")
.map(|d| d.parse::<usize>().expect("invalid digit"))
.collect()
})
.collect()
}
fn create_matrix(pairs: &[Idx]) -> Matrix {
let max = *pairs
.iter()
.flat_map(|(a, b)| [a, b])
.max()
.expect("iterator is non-empty")
+ 1;
let mut matrix = Grid::new(max, max, vec![Ordering::Equal; max * max]);
for (a, b) in pairs {
matrix.replace_cell((*a, *b), Ordering::Less);
matrix.replace_cell((*b, *a), Ordering::Greater);
}
matrix
}
fn valid_pages(pages: &[Page], matrix: &Matrix) -> usize {
pages
.iter()
.filter_map(|p| {
if check_order(p, matrix) {
Some(p[p.len() / 2])
} else {
None
}
})
.sum()
}
fn fix_invalid_pages(pages: &mut [Page], matrix: &Matrix) -> usize {
pages
.iter_mut()
.filter(|p| !check_order(p, matrix))
.map(|v| {
v.sort_by(|a, b| *matrix.get((*a, *b)).unwrap());
v[v.len() / 2]
})
.sum()
}
fn check_order(page: &[usize], matrix: &Matrix) -> bool {
page.is_sorted_by(|a, b| *matrix.get((*a, *b)).unwrap() == Ordering::Less)
}
pub fn solve() {
let input = read_to_string("inputs/day05.txt").expect("read file");
let (pairs, mut pages) = parse_input(&input);
let matrix = create_matrix(&pairs);
println!("Part 1: {}", valid_pages(&pages, &matrix));
println!("Part 2: {}", fix_invalid_pages(&mut pages, &matrix));
}
On github
*Edit: I did try switching to just using std::collections::HashMap
, but it was 0.1 ms slower on average than using the simple_grid::Grid
… Vec[idx]
access is faster maybe?
Ugh. Spent way too long on today’s. Should have just used my own grid structure from last year. I will likely refactor to use that. Even though it’s likely a super slow implementation, the convenience of dealing with it is better than shoehorning in the grid::Grid<T>
from that crate.
use grid::Grid;
use crate::shared::{
grid2d::{iter_diag_nesw, iter_diag_nwse, Point},
util::read_lines,
};
fn parse_grid(input: &[String]) -> Grid<u8> {
let cols = input.first().unwrap().len();
Grid::from_vec(
input
.iter()
.flat_map(|row| row.chars().map(|c| c as u8).collect::<Vec<u8>>())
.collect(),
cols,
)
}
fn part1(grid: &Grid<u8>) -> usize {
let mut xmas_count = 0;
let rows = grid
.iter_rows()
.map(|d| String::from_utf8(d.copied().collect()).unwrap());
let cols = grid
.iter_cols()
.map(|d| String::from_utf8(d.copied().collect()).unwrap());
for diag in iter_diag_nesw(grid)
.chain(iter_diag_nwse(grid))
.filter_map(|d| {
if d.len() >= 4 {
Some(String::from_utf8(d.clone()).unwrap())
} else {
None
}
})
.chain(rows)
.chain(cols)
{
xmas_count += diag.matches("XMAS").count() + diag.matches("SAMX").count()
}
xmas_count
}
fn part2(grid: &Grid<u8>) -> usize {
let mut xmas_count = 0;
let valid = [
[b'M', b'M', b'S', b'S'],
[b'M', b'S', b'S', b'M'],
[b'S', b'M', b'M', b'S'],
[b'S', b'S', b'M', b'M'],
];
for x in 1..grid.cols() - 1 {
for y in 1..grid.rows() - 1 {
if grid.get(y, x) == Some(&b'A')
&& valid.contains(
&(Point::new(x as isize, y as isize)
.diagonal_neighbors(grid)
.map(|i| i.unwrap_or(0))),
)
{
xmas_count += 1;
}
}
}
xmas_count
}
pub fn solve() {
let input = read_lines("inputs/day04.txt");
let grid = parse_grid(&input);
println!("Part 1: {}", part1(&grid));
println!("Part 2: {}", part2(&grid));
}
And here’s a link to the Github if you care to see the gross supporting code :D
Didn’t do anything crazy here – ended up using regex like a bunch of other folks.
use regex::Regex;
use crate::shared::util::read_lines;
fn parse_mul(input: &[String]) -> (u32, u32) {
// Lazy, but rejoin after having removed `\n`ewlines.
let joined = input.concat();
let re = Regex::new(r"mul\((\d+,\d+)\)|(do\(\))|(don't\(\))").expect("invalid regex");
// part1
let mut total1 = 0u32;
// part2 -- adds `do()`s and `don't()`s
let mut total2 = 0u32;
let mut enabled = 1u32;
re.captures_iter(&joined).for_each(|c| {
let (_, [m]) = c.extract();
match m {
"do()" => enabled = 1,
"don't()" => enabled = 0,
_ => {
let product: u32 = m.split(",").map(|s| s.parse::<u32>().unwrap()).product();
total1 += product;
total2 += product * enabled;
}
}
});
(total1, total2)
}
pub fn solve() {
let input = read_lines("inputs/day03.txt");
let (part1_res, part2_res) = parse_mul(&input);
println!("Part 1: {}", part1_res);
println!("Part 2: {}", part2_res);
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_solution() {
let test_input = vec![
"xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5))".to_string(),
];
let (p1, p2) = parse_mul(&test_input);
eprintln!("P1: {p1}, P2: {p2}");
assert_eq!(161, p1);
assert_eq!(48, p2);
}
}
Solution on my github (Made it public now)
Seriously can’t agree more. Whatever small utility LLMs offer (I haven’t used them in my day-to-day work at all because a regular search works for me just fine, and I can create my own images), it’s not worth the incredible amount of resources used to perform a single query. Maybe if we had fusion energy and there were no environmental implications to further researching the theoretical limits of LLMs their use could be justified–but we don’t, there are, so it can’t.
deleted by creator
I realized after I posted 😅 thanks for pointing it out! I will go make it public
Turned out alright, I am looking forward to seeing what 2d coordinate grid code I can cannibalize from last year’s solutions 😄
+1 for Ferris!
Very important questions, you are right. I, a totally normal non-weird person, agree with you and would like to motion that genital photos of all congress members are released monthly to ensure they are correct.
/s
P.S. Fuck.
Thank you! Also love the username–great game in great series
I have been dancing around taking the plunge into GrapheneOS – I have a pixel. Glad to hear you say this, bc it gives me confidence that I could move to it and not lose absolutely all the apps I have become accustomed to. There exists a list of apps that are compatible once de-googled (un play-protected), right? Also, I saw you mentioned that graphene can sandbox google play?
In the US: 3 bedroom, 2.5 bath; 3 stories since the basement is finished. The basement has my office and the (giant) couch + TV along with the half-bath, guestroom, laundry and a storage closet for mostly outdoor gear. Ground floor has a bedroom where our roommate lives (helping him get out of credit card debt), bathroom, kitchen, dining. Upstairs is the main suite: just my wife’s and my room with attached bath and closet.
House has an attached 2-car garage and a big 3-4 car driveway as well, though overall the lot is pretty small by suburban standards in our area (<0.25 acre). Have two tiny dogs, and lots of plans for upgrades and remodels.
Beat me to it
😄haha WELL we had to get to Starfleet somehow…
I paid for Kagi for a while and many of my coworkers use it. It’s a solid and growing engine that’s getting a a lot right re: creating good UX and generating search results (which should be the goal of a search engine, *sigh).
That said, l use SearxNG daily nowadays because it’s decentralized and privacy-focused. You can use any of the public instances or host your own if you like.
Here’s an example of the search results for “Neuroscience” on the instance I use.