Skip to content

Instantly share code, notes, and snippets.

@sqrtM
Last active December 11, 2023 10:32
Show Gist options
  • Save sqrtM/81037698c63dc2c0cf5ba2437b3d1511 to your computer and use it in GitHub Desktop.
Save sqrtM/81037698c63dc2c0cf5ba2437b3d1511 to your computer and use it in GitHub Desktop.
aoc2023 (sometimes just the part 2s oops)
🦀🦀🦀🦀🦀🦀🦀🦀🦀
🦀🦀🦀🦀🦀🦀🦀🦀🦀
use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;
fn main() {
let mut final_answer = 0;
if let Ok(lines) = read_lines("input.txt") {
for line in lines {
if let Ok(unparsed_value) = line {
let v = unparsed_value
.replace("one", "o1e")
.replace("two", "t2o")
.replace("three", "t3e")
.replace("four", "f4r")
.replace("five", "f5e")
.replace("six", "s6x")
.replace("seven", "s7n")
.replace("eight", "e8t")
.replace("nine", "n9e");
let mut numerals: Vec<i32> = vec![];
for (_, c) in v.chars().enumerate() {
if c.is_numeric() {
numerals.push(c.to_string().parse::<i32>().unwrap())
}
}
let first = *numerals.get(0).unwrap();
let last = numerals.pop().unwrap();
numerals = vec![first, last];
let result = numerals.iter().fold(0, |acc, elem| acc * 10 + elem);
final_answer += result;
}
}
}
println!("{}", final_answer);
}
fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
where P: AsRef<Path>, {
let file = File::open(filename)?;
Ok(io::BufReader::new(file).lines())
}
use std::collections::HashMap;
use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;
use regex::Regex;
fn main() {
let mut games: HashMap<i32, HashMap<&str, i32>> = HashMap::new();
let game_regex = Regex::new(r"^Game (?<digit>\d+): ").unwrap();
let color_regex = Regex::new(r"(?P<number>\d+)\s*(?P<color>\w+)").unwrap();
if let Ok(lines) = read_lines("input.txt") {
for line in lines.flatten() {
let mut red = 0;
let mut blue = 0;
let mut green = 0;
let _: Vec<_> = game_regex
.replace_all(&line, "")
.split(';')
.collect::<Vec<&str>>()
.iter()
.map(|set| {
let game_number = game_regex
.captures(&line)
.unwrap()
.name("digit")
.unwrap()
.as_str()
.parse::<i32>()
.unwrap();
for capture in color_regex.captures_iter(set) {
let color = capture.name("color").unwrap().as_str();
let number = capture.name("number").unwrap().as_str().parse::<i32>().unwrap();
match color {
"red" => {
if number > red {
red = number
}
}
"blue" => {
if number > blue {
blue = number
}
}
"green" => {
if number > green {
green = number
}
}
_ => {}
}
}
let mut colors = HashMap::new();
colors.insert("red", red);
colors.insert("blue", blue);
colors.insert("green", green);
games.insert(game_number, colors);
})
.collect();
}
}
let power = games.iter().fold(0, |acc, x| {
println!("outer -> {:?}", acc);
acc + x.1.iter().fold(1, |bdd, y| {
println!("inner -> {:?}", bdd);
bdd * y.1
})
});
println!("power {:?}", power)
}
fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
where P: AsRef<Path>, {
let file = File::open(filename)?;
Ok(io::BufReader::new(file).lines())
}
use std::collections::HashMap;
use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;
fn main() {
if let Ok(lines) = read_lines("input.txt") {
let mut engine_rows: Vec<Vec<char>> = vec![];
for line in lines.flatten() {
engine_rows.push(line.chars().collect::<Vec<char>>());
}
//find_part_numbers(&engine_rows);
find_gears(&engine_rows);
}
}
fn find_part_numbers(arr: &[Vec<char>]) {
let mut part_numbers: HashMap<(usize, usize), i32> = HashMap::new();
for (i, line) in arr.iter().enumerate() {
for (j, char) in line.iter().enumerate() {
// found a number
if char.is_numeric() && find_adjacent_symbol(arr, i, j) {
let val = get_value(arr, i, j);
part_numbers.insert(val.0, val.1);
}
}
}
println!("{}", part_numbers.values().sum::<i32>());
}
fn find_gears(arr: &[Vec<char>]) {
let mut part_numbers: Vec<i32> = vec![];
for (i, line) in arr.iter().enumerate() {
for (j, char) in line.iter().enumerate() {
// found a gear
if *char == '*' {
// find numbers
let nums = find_adjacent_numerals(arr, i, j);
if !nums.is_empty() {
println!("found {} numerals around the gear at {} {} -> {:?}", nums.len(), i, j, nums);
let r = nums
.iter()
.map(|num| num.1)
.collect::<Vec<i32>>()
.iter()
.product::<i32>();
part_numbers.push(r);
}
}
}
}
println!("{:?}", part_numbers.iter().sum::<i32>());
}
fn find_adjacent_symbol(arr: &[Vec<char>], i: usize, j: usize) -> bool {
// search above
if i > 0 {
for k in usize::saturating_sub(j, 1)..usize::saturating_add(j, 2) {
if k <= arr[i - 1].len() && !arr[i - 1].get(k).unwrap_or(&'1').is_numeric() && *arr[i - 1].get(k).unwrap_or(&'.') as i32 != 46 {
// found a symbol
return true;
}
}
}
// search same line
for k in usize::saturating_sub(j, 1)..usize::saturating_add(j, 2) {
if k <= arr[i].len() && !arr[i].get(k).unwrap_or(&'1').is_numeric() && *arr[i].get(k).unwrap_or(&'.') as i32 != 46 {
// found a symbol
return true;
}
}
// search below
if i < arr.len() - 1 {
for k in usize::saturating_sub(j, 1)..usize::saturating_add(j, 2) {
if k <= arr[i + 1].len() && !arr[i + 1].get(k).unwrap_or(&'1').is_numeric() && *arr[i + 1].get(k).unwrap_or(&'.') as i32 != 46 {
// found a symbol
return true;
}
}
}
false
}
fn find_adjacent_numerals(arr: &[Vec<char>], i: usize, j: usize) -> Vec<((usize, usize), i32)> {
let mut numerals: Vec<((usize, usize), i32)> = vec![];
// search above
if i > 0 {
for k in usize::saturating_sub(j, 1)..usize::saturating_add(j, 2) {
if k <= arr[i - 1].len() && arr[i - 1].get(k).unwrap_or(&'.').is_numeric() {
// found a symbol
let num = get_value(arr, i - 1, k);
numerals.push(num);
}
}
}
// search same line
for k in usize::saturating_sub(j, 1)..usize::saturating_add(j, 2) {
if k <= arr[i].len() && arr[i].get(k).unwrap_or(&'.').is_numeric() {
// found a symbol
let num = get_value(arr, i, k);
numerals.push(num);
}
}
// search below
if i < arr.len() - 1 {
for k in usize::saturating_sub(j, 1)..usize::saturating_add(j, 2) {
if k <= arr[i + 1].len() && arr[i + 1].get(k).unwrap_or(&'.').is_numeric() {
// found a symbol
let num = get_value(arr, i + 1, k);
numerals.push(num);
}
}
}
numerals.dedup();
if numerals.len() == 2 {
numerals
} else {
vec![]
}
}
fn get_value(arr: &[Vec<char>], i: usize, j: usize) -> ((usize, usize), i32) {
let mut value: Vec<i32> = vec![];
if j > 0 && arr[i].get(usize::saturating_sub(j, 1)).unwrap().is_numeric() {
return get_value(arr, i, usize::saturating_sub(j, 1));
} else {
let mut index = j;
while arr[i].get(index).unwrap_or(&'.').is_numeric() {
value.push(arr[i][index].to_digit(10).unwrap() as i32);
index += 1;
}
}
((i, j), value.iter().fold(0, |acc, elem| acc * 10 + elem))
}
fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
where P: AsRef<Path>, {
let file = File::open(filename)?;
Ok(io::BufReader::new(file).lines())
}
use std::collections::HashMap;
use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;
use regex::Regex;
fn main() {
one();
two();
}
fn two() {
let mut copies: HashMap<i32, i32> = HashMap::new();
for i in 1..=196 {
copies.insert(i, 1);
}
if let Ok(lines) = read_lines("input.txt") {
for line in lines.flatten() {
let card_number = Regex::new(r"\bCard\s+(?P<game_number>\d+): ")
.unwrap().captures(&line)
.and_then(|captures| captures.name("game_number"))
.and_then(|match_| match_.as_str().parse().ok())
.unwrap();
let vecs = vecify(&line);
let matches = vecs[0].iter().filter(|&x| vecs[1].contains(x)).collect::<Vec<_>>().len() as i32;
for i in card_number + 1..=card_number + matches {
let val = copies.get(&i).unwrap();
copies.insert(i, val + copies.get(&card_number).unwrap());
}
}
}
println!("{:?}", copies.values().sum::<i32>());
}
fn one() {
let mut total_score = 0;
if let Ok(lines) = read_lines("input.txt") {
for line in lines.flatten() {
let vecs = vecify(&line);
let matches = vecs[0].iter().filter(|&x| vecs[1].contains(x)).collect::<Vec<_>>();
let ticket_score: i32 = 2_i32.pow(matches.len().saturating_sub(1) as u32);
if !matches.is_empty() {
total_score += ticket_score;
}
}
}
println!("{:?}", total_score);
}
fn vecify(line: &str) -> Vec<Vec<i32>> {
Regex::new(r"\bCard\s+(?P<game_number>\d+): ")
.unwrap()
.replace_all(line, "")
.split(" | ")
.map(
|s| s.split(' ').filter_map(
|y| y.parse::<i32>().ok()).collect()
)
.collect()
}
fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
where P: AsRef<Path>, {
let file = File::open(filename)?;
Ok(io::BufReader::new(file).lines())
}
use std::fs::File;
use std::io::{self, BufRead};
use std::ops::Range;
use std::path::Path;
use std::sync::{Arc, Mutex};
use rayon::iter::ParallelIterator;
use rayon::prelude::ParallelSlice;
#[derive(Default, Debug, Clone)]
struct Almanac {
destination: u64,
source: u64,
modifier: u64,
}
impl Almanac {
fn from_vec(vec: Vec<u64>) -> Self {
Self {
destination: vec[0],
source: vec[1],
modifier: vec[2],
}
}
fn incoming_range(&self) -> Range<u64> {
self.source..(self.source + self.modifier)
}
}
fn main() {
let mut almanacs: Vec<Vec<Almanac>> = vec![vec![]; 7];
let mut seeds: Vec<u64> = vec![];
let mut almanac_index: i32 = -1;
if let Ok(lines) = read_lines("input.txt") {
// ignore the parsing i know it's ugly its AOC give me a break
for line in lines.flatten() {
if line.contains("seeds:") {
seeds = line
.split(' ')
.filter_map(|x| x.parse().ok())
.collect();
}
if line.contains("map") {
almanac_index += 1;
continue;
}
if almanac_index < 0 || line.trim().is_empty() {
continue;
}
let arr = line
.split(' ')
.map(|x| x.parse::<u64>().unwrap())
.collect::<Vec<u64>>();
almanacs[almanac_index as usize].push(Almanac::from_vec(arr));
}
}
let nearest_location = Arc::new(Mutex::new(u64::MAX));
seeds.par_chunks(2).for_each(|chunk| {
println!("new chunk");
let local_nearest_location = Arc::clone(&nearest_location);
let range = chunk[0]..(chunk[0] + chunk[1]);
for number in range {
let value = traverse(&number, &almanacs, 0);
let mut lock = local_nearest_location.lock().unwrap();
if value < *lock {
*lock = value;
println!("new lowest: {}", *lock)
}
}
});
let nearest_location_value = *nearest_location.lock().unwrap();
println!("ANSWER: {}", nearest_location_value)
}
fn traverse(seed: &u64, almanacs: &[Vec<Almanac>], iteration: usize) -> u64 {
if iteration == almanacs.len() {
return *seed;
}
for almanac in &almanacs[iteration] {
if almanac.incoming_range().contains(seed) {
let new_seed = (seed - almanac.source) + almanac.destination;
return traverse(&new_seed, almanacs, iteration + 1);
}
}
traverse(seed, almanacs, iteration + 1)
}
fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
where P: AsRef<Path>, {
let file = File::open(filename)?;
Ok(io::BufReader::new(file).lines())
}
use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;
use regex::Regex;
const TIME: usize = 0;
const DIST: usize = 1;
fn main() {
one();
two();
}
fn two() {
if let Ok(lines) = read_lines("input.txt") {
let mut race_stats: Vec<u64> = vec![];
for line in lines.flatten() {
// this should just .zip, i'm dumb
let num = Regex::new(r"\b\d+\b")
.unwrap()
.find_iter(&line)
.map(|mat| mat.as_str())
.collect::<String>()
.parse::<u64>()
.unwrap();
race_stats.push(num)
}
let mut possible_solutions: Vec<u64> = vec![];
for t in 1..race_stats[TIME] {
let distance_travelled = t * (race_stats[TIME] - t);
if distance_travelled > race_stats[DIST] {
possible_solutions.push(distance_travelled);
}
if distance_travelled < *possible_solutions.last().unwrap_or(&0) {
break;
}
}
println!("{:?}", possible_solutions.len());
}
}
fn one() {
if let Ok(lines) = read_lines("input.txt") {
let mut time_distance: Vec<Vec<i32>> = vec![];
for line in lines.flatten() {
let numerals: Vec<i32> = Regex::new(r"\b\d+\b")
.unwrap()
.find_iter(&line)
.map(|mat| mat.as_str().parse().unwrap())
.collect();
time_distance.push(numerals);
}
let mut possible_solutions: Vec<Vec<i32>> = vec![];
for (index, td) in time_distance[TIME].iter().enumerate() {
possible_solutions.push(vec![]);
for t in 1..*td {
let distance_travelled = t * (td - t);
if distance_travelled > time_distance[DIST][index] {
possible_solutions[index].push(distance_travelled);
}
if distance_travelled < *possible_solutions[index].last().unwrap_or(&0) {
break;
}
}
}
println!("{:?}", possible_solutions.iter().map(|x| x.len()).product::<usize>());
}
}
fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
where P: AsRef<Path>, {
let file = File::open(filename)?;
Ok(io::BufReader::new(file).lines())
}
use std::collections::HashMap;
use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;
use crate::HandType::{
FiveOfAKind, FourOfAKind, FullHouse, HighCard, OnePair, ThreeOfAKind, TwoPair,
};
const PRIORITY: [char; 13] = [
'J', '2', '3', '4', '5', '6', '7', '8', '9', 'T', 'Q', 'K', 'A',
];
#[derive(PartialOrd, PartialEq, Debug)]
enum HandType {
FiveOfAKind = 6,
FourOfAKind = 5,
FullHouse = 4,
ThreeOfAKind = 3,
TwoPair = 2,
OnePair = 1,
HighCard = 0,
}
#[derive(Debug, PartialEq, Eq)]
struct Hand {
cards: [char; 5],
bid: i32,
}
impl Hand {
fn from_slice(slice: &[char], bid: i32) -> Self {
Self {
cards: <[char; 5]>::try_from(slice).unwrap(),
bid,
}
}
}
impl PartialOrd for Hand {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Hand {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
let self_score = score_hand(self);
let other_score = score_hand(other);
if self_score != other_score {
return (self_score as usize).cmp(&(other_score as usize));
}
// if same type -> get high card
for (&self_card, &other_card) in self.cards.iter().zip(other.cards.iter()) {
if self_card != other_card {
return PRIORITY
.iter()
.position(|&x| x == self_card)
.cmp(&PRIORITY.iter().position(|&x| x == other_card));
}
}
std::cmp::Ordering::Equal
}
}
fn main() {
let mut hands: Vec<Hand> = vec![];
if let Ok(lines) = read_lines("input.txt") {
for line in lines.flatten() {
let parsed = line.split(' ').collect::<Vec<&str>>();
let cards = parsed.first().unwrap().chars().collect::<Vec<char>>();
let bid: i32 = parsed.get(1).unwrap().parse().unwrap();
let hand = Hand::from_slice(&cards, bid);
hands.push(hand);
}
}
hands.sort();
println!(
"{:?}",
hands
.iter()
.enumerate()
.map(|(i, h)| h.bid * (i + 1) as i32)
.sum::<i32>()
);
}
fn score_hand(hand: &Hand) -> HandType {
let mut hand_map = HashMap::new();
hand.cards.iter().for_each(|x| {
let c = hand.cards.iter().filter(|n| n == &x).count();
hand_map.insert(x, c);
});
let jokers = hand_map.get(&'J').unwrap_or(&0);
// doing it like this was such a mistake, wow
return match hand_map.values().max().unwrap() {
5 => FiveOfAKind,
4 => {
return if jokers == &1usize || jokers == &4usize {
FiveOfAKind
} else {
FourOfAKind
};
}
3 => {
return if hand_map.values().len() == 2 {
if jokers == &2usize || jokers == &3usize {
FiveOfAKind
} else {
FullHouse
}
} else if jokers == &1usize {
FourOfAKind
} else {
ThreeOfAKind
};
}
2 => {
return if hand_map.values().len() == 3 {
if jokers == &2usize {
FourOfAKind
} else if jokers == &1usize {
FullHouse
} else {
TwoPair
}
} else if jokers == &1usize || jokers == &2usize {
ThreeOfAKind
} else {
OnePair
};
}
1 => {
return if jokers == &1usize { OnePair } else { HighCard };
}
_ => {
panic!()
}
};
}
fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
where
P: AsRef<Path>,
{
let file = File::open(filename)?;
Ok(io::BufReader::new(file).lines())
}
use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;
use num::integer::lcm;
use regex::Regex;
use crate::Direction::{L, R};
#[derive(Debug)]
struct Node {
name: String,
looking_for_left: String,
looking_for_right: String,
left: Option<Box<Node>>,
right: Option<Box<Node>>,
}
#[derive(PartialEq, Debug)]
enum Direction {
L,
R,
}
impl Direction {
fn from_str(input: char) -> Self {
if input == 'L' {
L
} else {
R
}
}
}
impl Node {
fn from_str(input: &str) -> Self {
let regex =
Regex::new(r#"^(?P<name>\w{3})\s*=\s*\((?P<left>\w{3}),\s*(?P<right>\w{3})\)$"#)
.unwrap();
let name = regex
.captures(input)
.unwrap()
.name("name")
.unwrap()
.as_str();
let left = regex
.captures(input)
.unwrap()
.name("left")
.unwrap()
.as_str();
let right = regex
.captures(input)
.unwrap()
.name("right")
.unwrap()
.as_str();
Self {
name: String::from(name),
looking_for_left: String::from(left),
looking_for_right: String::from(right),
left: None,
right: None,
}
}
fn populate(&mut self, direction: &Direction) {
// reading from the input like this each time was a pretty big mistake,
// and led to really slow execution time, but it does work...
if let Ok(lines) = read_lines("input.txt") {
for line in lines.flatten() {
let fmt_dir = if direction == &L {
format!("{} ", self.looking_for_left)
} else {
format!("{} ", self.looking_for_right)
};
if direction == &R && line.contains(&fmt_dir) && self.right.is_none() {
self.right = Some(Box::new(Node::from_str(&line)));
}
if direction == &L && line.contains(&fmt_dir) && self.left.is_none() {
self.left = Some(Box::new(Node::from_str(&line)));
}
}
}
}
fn traverse(self, direction: &Direction) -> Box<Node> {
if direction == &L {
self.left.unwrap()
} else {
self.right.unwrap()
}
}
}
fn main() {
//one();
two()
}
fn two() {
let directions: Vec<Direction> = get_directions()
.unwrap()
.chars()
.map(Direction::from_str)
.collect();
let starting_points = find_starting_points().unwrap();
let mut cycle = directions.iter().cycle();
let time_to_reach_z = starting_points
.into_iter()
.map(|mut x| {
let mut steps: u64 = 0;
while !x.name.ends_with('Z') {
if let Some(dir) = cycle.next() {
x.populate(dir);
x = *x.traverse(dir);
steps += 1;
}
}
steps
})
.collect::<Vec<u64>>();
println!(
"FINAL ANSWER : {:?}",
time_to_reach_z.into_iter().fold(1, lcm)
);
}
fn one() {
let directions: Vec<Direction> = get_directions()
.unwrap()
.chars()
.map(Direction::from_str)
.collect();
let first_node = find_aaa().unwrap();
let mut cycle = directions.iter().cycle();
let mut current_location = Box::new(first_node);
let mut steps = 0;
// slow but it works
while current_location.name != "ZZZ" {
if let Some(dir) = cycle.next() {
current_location.populate(dir);
current_location = current_location.traverse(dir);
steps += 1;
}
}
println!("{}", steps);
}
fn find_aaa() -> Result<Node, ()> {
if let Ok(lines) = read_lines("input.txt") {
for line in lines.flatten() {
if line.contains("AAA =") {
return Ok(Node::from_str(&line));
}
}
Err(())
} else {
Err(())
}
}
fn find_starting_points() -> Result<Vec<Node>, ()> {
let mut nodes: Vec<Node> = vec![];
if let Ok(lines) = read_lines("input.txt") {
for line in lines.flatten() {
if line.contains("A =") {
nodes.push(Node::from_str(&line));
}
}
Ok(nodes)
} else {
Err(())
}
}
fn get_directions() -> Option<String> {
if let Ok(lines) = read_lines("input.txt") {
for line in lines.flatten().enumerate() {
if line.0 == 0 {
return Some(line.1);
}
}
}
None
}
fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
where
P: AsRef<Path>,
{
let file = File::open(filename)?;
Ok(io::BufReader::new(file).lines())
}
use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;
fn main() {
if let Ok(lines) = read_lines("input.txt") {
let v: Vec<(i64, i64)> = lines
.flatten()
.map(|line| {
let mut vals: Vec<i64> = line.split(' ').flat_map(str::parse).collect();
let mut lasts: Vec<i64> = vec![];
let mut firsts: Vec<i64> = vec![];
while vals.iter().any(|&x| x != 0) {
firsts.push(*vals.first().unwrap());
lasts.push(*vals.last().unwrap());
vals = vals.windows(2).map(|w| w[1] - w[0]).collect();
}
(
firsts.into_iter().rev().reduce(|acc, x| x - acc).unwrap(),
lasts.iter().sum::<i64>(),
)
})
.collect::<Vec<(i64, i64)>>();
println!("past : {}", v.iter().map(|x| x.0).sum::<i64>());
println!("future: {}", v.iter().map(|x| x.1).sum::<i64>());
}
}
fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
where
P: AsRef<Path>,
{
let file = File::open(filename)?;
Ok(io::BufReader::new(file).lines())
}
use std::collections::HashMap;
use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;
use crate::Direction::{E, N, S, W};
use crate::PipeType::{
EastToWest, Ground, NorthToEast, NorthToSouth, NorthToWest, SouthToEast, SouthToWest,
StartingPoint,
};
// Just part one for today.
fn main() {
if let Ok(lines) = read_lines("input.txt") {
let map = lines
.flatten()
.enumerate()
.flat_map(|(y, line)| {
line.chars()
.enumerate()
.map(|(x, char)| ((x, y), Pipe::from_char_and_coords(&char, x, y)))
.collect::<Vec<((usize, usize), Pipe)>>()
})
.collect::<HashMap<(usize, usize), Pipe>>();
let answer = map.traverse(
Animal {
x: map.find_starting_point().0,
y: map.find_starting_point().1,
facing: E,
},
0,
);
println!("{:?}", (answer + 1) / 2);
}
}
trait Traverse {
fn traverse(&self, animal: Animal, steps: u32) -> u32;
fn find_starting_point(&self) -> (usize, usize);
fn facing_location(&self, animal: &Animal) -> (usize, usize) {
match animal.facing {
N => (animal.x, animal.y - 1),
S => (animal.x, animal.y + 1),
E => (animal.x + 1, animal.y),
W => (animal.x - 1, animal.y),
}
}
}
impl Traverse for HashMap<(usize, usize), Pipe> {
fn traverse(&self, animal: Animal, steps: u32) -> u32 {
let facing_pipe = self.get(&self.facing_location(&animal)).unwrap();
let new_data = facing_pipe.slide(&animal);
if !facing_pipe.is_starting_point() {
self.traverse(
Animal {
x: new_data.0 .0,
y: new_data.0 .1,
facing: new_data.1,
},
steps + 1,
)
} else {
steps
}
}
fn find_starting_point(&self) -> (usize, usize) {
self.iter()
.find_map(|(_, pipe)| {
if pipe.is_starting_point() {
Some(pipe.coords())
} else {
None
}
})
.unwrap()
}
}
#[derive(Debug, Copy, Clone)]
enum PipeType {
StartingPoint,
NorthToSouth,
EastToWest,
NorthToEast,
NorthToWest,
SouthToWest,
SouthToEast,
Ground,
}
#[derive(Debug)]
struct Pipe {
x: usize,
y: usize,
kind: PipeType,
}
#[derive(Debug)]
enum Direction {
N,
S,
E,
W,
}
#[derive(Debug)]
struct Animal {
x: usize,
y: usize,
facing: Direction,
}
impl Pipe {
fn is_starting_point(&self) -> bool {
match self.kind {
StartingPoint => true,
NorthToSouth | EastToWest | NorthToEast | NorthToWest | SouthToWest | SouthToEast
| Ground => false,
}
}
fn from_char_and_coords(char: &char, x: usize, y: usize) -> Self {
match char {
'S' => Self {
x,
y,
kind: StartingPoint,
},
'|' => Self {
x,
y,
kind: NorthToSouth,
},
'-' => Self {
x,
y,
kind: EastToWest,
},
'L' => Self {
x,
y,
kind: NorthToEast,
},
'J' => Self {
x,
y,
kind: NorthToWest,
},
'7' => Self {
x,
y,
kind: SouthToWest,
},
'F' => Self {
x,
y,
kind: SouthToEast,
},
'.' => Self { x, y, kind: Ground },
_ => panic!(),
}
}
fn coords(&self) -> (usize, usize) {
(self.x, self.y)
}
fn slide(&self, animal: &Animal) -> ((usize, usize), Direction) {
match animal.facing {
N => match self.kind {
NorthToSouth => ((animal.x, animal.y - 1), N),
SouthToEast => ((animal.x, animal.y - 1), E),
SouthToWest => ((animal.x, animal.y - 1), W),
_ => ((animal.x, animal.y), N),
},
S => match self.kind {
NorthToSouth => ((animal.x, animal.y + 1), S),
NorthToEast => ((animal.x, animal.y + 1), E),
NorthToWest => ((animal.x, animal.y + 1), W),
_ => ((animal.x, animal.y), S),
},
E => match self.kind {
EastToWest => ((animal.x + 1, animal.y), E),
NorthToWest => ((animal.x + 1, animal.y), N),
SouthToWest => ((animal.x + 1, animal.y), S),
_ => ((animal.x, animal.y), E),
},
W => match self.kind {
EastToWest => ((animal.x - 1, animal.y), W),
NorthToEast => ((animal.x - 1, animal.y), N),
SouthToEast => ((animal.x - 1, animal.y), S),
_ => ((animal.x, animal.y), W),
},
}
}
}
fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
where
P: AsRef<Path>,
{
let file = File::open(filename)?;
Ok(io::BufReader::new(file).lines())
}
use itertools::Itertools;
use std::collections::HashSet;
use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;
// this didn't actually get the right answer
// but i don't have any more time to work on
// this and it DID get the right answer for
// the test input so ¯\_(ツ)_/¯
fn main() {
if let Ok(lines) = read_lines("input.txt") {
let original_map = lines
.flatten()
.map(|line| line.chars().collect::<Vec<char>>())
.collect::<Vec<Vec<char>>>();
let empty_rows = original_map
.iter()
.enumerate()
.filter(|(_, row)| row.iter().all(|ch| ch == &'.'))
.collect::<Vec<(usize, &Vec<char>)>>()
.iter()
.map(|(i, _)| *i)
.collect::<Vec<usize>>();
let empty_cols = (0..original_map[0].len())
.map(|col| {
original_map
.iter()
.map(|row| row[col])
.collect::<Vec<char>>()
})
.collect::<Vec<Vec<char>>>()
.iter()
.enumerate()
.filter(|(_, column)| column.iter().all(|ch| ch == &'.'))
.collect::<Vec<(usize, &Vec<char>)>>()
.iter()
.map(|(i, _)| *i)
.collect::<Vec<usize>>();
let mut expanded_map = original_map.clone();
// this shows a certain immaturity i have when I solve
// questions like this. as i was solving this, i felt
// the need to create a whole new physical representation
// of the data i was manipulating rather than just running
// the points through calculations and getting the diffs
// abstractly. that "create a physical representation"
// instinct is something i know i need to work on.
for i in 0..original_map.len() + empty_rows.len() {
let mut added_rows = 0;
if empty_rows.contains(&i) {
added_rows += 1;
expanded_map.insert(i + added_rows, vec!['.'; original_map[0].len()])
}
let mut added_cols = 0;
for j in 0..expanded_map[i].len() {
if empty_cols.contains(&j) {
added_cols += 1;
expanded_map[i].insert(j + added_cols, '.');
}
}
}
let mut set: HashSet<(usize, usize)> = HashSet::new();
expanded_map.iter().enumerate().for_each(|(i, row)| {
row.iter().enumerate().for_each(|(j, c)| {
if c == &'#' {
set.insert((i, j));
}
})
});
let mut distances = vec![];
for (x, y) in set.iter().tuple_combinations() {
distances.push(manhattan(x, y))
}
for map in expanded_map {
println!("{:?}", map);
}
println!("{:?}", empty_cols);
println!("{:?}", empty_rows);
println!("{:?}", set.len());
println!("{:?}", distances.iter().sum::<usize>())
/*
9546608 -- too high
*/
}
}
fn manhattan((x1, y1): &(usize, usize), (x2, y2): &(usize, usize)) -> usize {
println!(
"{:?} -- {:?} -- {:?}",
(x1, y1),
(x2, y2),
(*x2 as isize - *x1 as isize).unsigned_abs() + (*y2 as isize - *y1 as isize).unsigned_abs()
);
(*x2 as isize - *x1 as isize).unsigned_abs() + (*y2 as isize - *y1 as isize).unsigned_abs()
}
fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
where
P: AsRef<Path>,
{
let file = File::open(filename)?;
Ok(io::BufReader::new(file).lines())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment