Connections
Puzzle #587
🟨🟨🟨🟨
🟩🟩🟩🟩
🟦🟦🟦🟦
🟪🟪🟪🟪
Skill 95/99 Uniqueness 1 in 25
Connections
Puzzle #587
🟨🟨🟨🟨
🟩🟩🟩🟩
🟦🟦🟦🟦
🟪🟪🟪🟪
Skill 95/99 Uniqueness 1 in 25
Wordle 1,303 4/6
⬛⬛🟨⬛⬛
⬛🟨⬛🟨⬛
⬛🟨🟨🟩🟨
🟩🟩🟩🟩🟩
Huh, took a second to get it, but clever concept
Strands #310
“Front women”
🔵🔵🔵🔵
🔵🟡🔵🔵
They’re always so confidently incorrect. People seem to want everything that happens to fit in a box to make it comprehensible. Change is scary. The idea that everything is just chaos and the miracle of our existence is just random, and that there is no “they” pulling the strings is scary. Better to believe that there is some kind of plan or shadowy people behind the scenes to give everything a sense of order than to come to grips with facts. Believing you are smarter than all the sheeple and have it all figured out is a nice warm blanket.
We may be all alone in the universe. If we are, then all we’ve got is each other, and the rock in space we happen to live on. I wish the conspiracy theorists and the selfish, greedy folks with all the resources who are plunging us toward a bleak future could have a little perspective and empathy. This life is a shared experience and always has the same ending, so why not try to learn about each other and make someone else’s existence a little more pleasant while we’re here, rather than cave to fear and embracing ignorance?
The background is a euclidian nightmare
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.
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
Got so lucky