days 9 10 11 and 12
This commit is contained in:
parent
bd93ac4c3d
commit
14c1e412eb
6
day09/Cargo.toml
Normal file
6
day09/Cargo.toml
Normal file
@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "day09"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
345
day09/src/main.rs
Normal file
345
day09/src/main.rs
Normal file
@ -0,0 +1,345 @@
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::env;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum Record {
|
||||
Gap { start: i32, length: i32},
|
||||
File { start: i32, length: i32, file: i32 }
|
||||
}
|
||||
|
||||
struct Drive {
|
||||
in_order: Vec<Record>,
|
||||
files: HashMap<i32, Record>,
|
||||
}
|
||||
fn main() {
|
||||
println!("Hello, AoC day 09!");
|
||||
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() != 2 {
|
||||
println!("wrong number of arguments!");
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
let file_path = &args[1];
|
||||
|
||||
let contents = fs::read_to_string(file_path).expect("Should have been able to read the file");
|
||||
|
||||
let mut drive = read_drive(&contents);
|
||||
print_drive(&drive);
|
||||
compress(&mut drive);
|
||||
print_drive(&drive);
|
||||
let sum = checksum(&drive);
|
||||
println!("checksum post-compaction is {sum}.");
|
||||
|
||||
let mut drive = read_drive_enum(&contents);
|
||||
print_enums(&drive);
|
||||
drive = compress_enums(drive);
|
||||
print_enums(&drive);
|
||||
let sum = checksum_enum(&drive);
|
||||
println!("checksum post-compression is {sum}")
|
||||
}
|
||||
|
||||
fn read_drive(contents: &str) -> Vec<i32> {
|
||||
let mut files: i32 = 0;
|
||||
let mut empty: i32 = 0;
|
||||
let mut is_file = true;
|
||||
for character in contents.chars() {
|
||||
let maybe = character.to_string().parse::<i32>();
|
||||
if maybe.is_err() {
|
||||
continue;
|
||||
}
|
||||
let num = maybe.unwrap();
|
||||
if is_file {
|
||||
files += num;
|
||||
} else {
|
||||
empty += num;
|
||||
}
|
||||
is_file = !is_file;
|
||||
}
|
||||
|
||||
let mut drive: Vec<i32> = vec![-1; (empty+files).try_into().unwrap()];
|
||||
let mut file_number: i32 = 0;
|
||||
let mut index: usize = 0;
|
||||
let mut is_file = true;
|
||||
for character in contents.chars() {
|
||||
let maybe = character.to_string().parse::<u32>();
|
||||
if maybe.is_err() {
|
||||
continue;
|
||||
}
|
||||
let num = maybe.unwrap();
|
||||
if is_file {
|
||||
for _i in 0..num {
|
||||
drive[index] = file_number;
|
||||
index += 1;
|
||||
}
|
||||
file_number += 1;
|
||||
} else {
|
||||
index += num as usize;
|
||||
}
|
||||
is_file = !is_file;
|
||||
}
|
||||
|
||||
return drive;
|
||||
}
|
||||
|
||||
fn print_drive(drive: &[i32]) {
|
||||
for &x in drive {
|
||||
if x == -1 {
|
||||
print!(".");
|
||||
} else {
|
||||
print!("{}", x);
|
||||
}
|
||||
}
|
||||
print!("\n");
|
||||
}
|
||||
|
||||
fn checksum(drive: &[i32]) -> i64 {
|
||||
let mut sum: i64 = 0;
|
||||
for index in 0..drive.len() {
|
||||
if drive[index] == -1 { continue; }
|
||||
sum += (drive[index] as i64) * (index as i64);
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
fn compress(drive: &mut Vec<i32>) {
|
||||
let mut write_index: usize = 0;
|
||||
for read_index in (0..drive.len()).rev() {
|
||||
if write_index == drive.len() { break; }
|
||||
|
||||
let file = drive[read_index];
|
||||
if file == -1 { continue; }
|
||||
|
||||
while drive[write_index] != -1 {
|
||||
write_index += 1;
|
||||
}
|
||||
if write_index >= read_index { break; }
|
||||
|
||||
drive[write_index] = file;
|
||||
drive[read_index] = -1;
|
||||
write_index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn read_drive_enum(contents: &str) -> Drive {
|
||||
let mut drive: Vec<Record> = Vec::with_capacity(contents.len());
|
||||
let mut files: HashMap<i32, Record> = HashMap::new();
|
||||
|
||||
let mut is_file = true;
|
||||
let mut file_number = 0;
|
||||
let mut index = 0;
|
||||
for c in contents.trim().chars() {
|
||||
let num = c.to_string().parse::<i32>().unwrap();
|
||||
if is_file {
|
||||
let file = Record::File {
|
||||
start: index,
|
||||
length: num,
|
||||
file: file_number,
|
||||
};
|
||||
drive.push(file);
|
||||
files.insert(file_number, file);
|
||||
|
||||
file_number += 1;
|
||||
} else {
|
||||
drive.push(Record::Gap { start: index, length: num });
|
||||
}
|
||||
index += num;
|
||||
is_file = !is_file;
|
||||
}
|
||||
|
||||
return Drive { in_order: drive, files: files };
|
||||
}
|
||||
|
||||
fn print_enums(drive: &Drive) {
|
||||
for x in drive.in_order.iter() {
|
||||
match x {
|
||||
Record::Gap { start: _, length } => {
|
||||
for _ in 0..*length {
|
||||
print!(".");
|
||||
}
|
||||
},
|
||||
Record::File { start: _, length, file } => {
|
||||
for _ in 0..*length {
|
||||
print!("{}", file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
print!("\n");
|
||||
}
|
||||
|
||||
fn compress_enums(mut drive: Drive) -> Drive {
|
||||
// for each file in backwards order,
|
||||
for file_number in (0..(drive.files.len() as i32)).rev() {
|
||||
let record = drive.files.get(&file_number);
|
||||
println!("looking at {}: {:?}", file_number, record);
|
||||
|
||||
// index into the in_order vec
|
||||
let file_index = find_order_index(&drive, file_number);
|
||||
|
||||
match record {
|
||||
// we will never find a gap in the files hashmap
|
||||
Some(Record::Gap {start: _, length: _}) => (),
|
||||
// if we found none then it's probably an off-by-one at the edges
|
||||
None => (),
|
||||
|
||||
Some(Record::File {start: file_start, length: file_length, file: _ }) => {
|
||||
// find the first gap where the object can live,
|
||||
for gap_index in 0..drive.in_order.len() {
|
||||
// if we're looking past the file, then we're done!
|
||||
if gap_index > file_index { break; }
|
||||
|
||||
match drive.in_order[gap_index] {
|
||||
// if we're looking at a file, skip it. can't put our file into an occupied spot.
|
||||
Record::File { .. } => { continue; },
|
||||
// if we're looking at a gap, proceed!
|
||||
Record::Gap { start: gap_start, length: gap_length } => {
|
||||
// found a gap that fits just right
|
||||
if gap_length == *file_length {
|
||||
|
||||
// remove the gap
|
||||
drive.in_order.remove(gap_index);
|
||||
// insert the file into the gap
|
||||
let new_file = Record::File {
|
||||
start: gap_start,
|
||||
length: *file_length,
|
||||
file: file_number
|
||||
};
|
||||
drive.in_order.insert(gap_index, new_file);
|
||||
|
||||
// remove the file
|
||||
drive.in_order.remove(file_index);
|
||||
// insert the gap where the file was
|
||||
let new_gap = Record::Gap {
|
||||
start: *file_start,
|
||||
length: gap_length
|
||||
};
|
||||
drive.in_order.insert(file_index, new_gap);
|
||||
|
||||
// update the files hashmap
|
||||
drive.files.insert(file_number, new_file);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// the gap is bigger than the file
|
||||
if gap_length > *file_length {
|
||||
|
||||
let mut offset: i32 = 0;
|
||||
// remove the gap
|
||||
drive.in_order.remove(gap_index);
|
||||
offset -= 1;
|
||||
// insert the file into the gap
|
||||
let new_file = Record::File {
|
||||
start: gap_start,
|
||||
length: *file_length,
|
||||
file: file_number
|
||||
};
|
||||
drive.in_order.insert(gap_index, new_file);
|
||||
offset += 1;
|
||||
|
||||
// fill the gap
|
||||
if let Record::Gap {
|
||||
start: _next_gap_start,
|
||||
length: next_gap_length
|
||||
} = drive.in_order[gap_index + 1] {
|
||||
// if the next record is a gap, merge them
|
||||
drive.in_order[gap_index + 1] = Record::Gap {
|
||||
start:gap_start + *file_length,
|
||||
length: gap_length - file_length + next_gap_length
|
||||
};
|
||||
} else {
|
||||
// if the next record is a file, add a gap.
|
||||
let new_gap_fill = Record::Gap {
|
||||
start: gap_start + *file_length,
|
||||
length: gap_length - file_length
|
||||
};
|
||||
drive.in_order.insert(gap_index + 1, new_gap_fill);
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
let offset_index = (file_index as i32 + offset) as usize;
|
||||
// remove the file
|
||||
drive.in_order.remove(offset_index);
|
||||
// insert the gap where the file was
|
||||
let new_gap = Record::Gap {
|
||||
start: *file_start,
|
||||
length: *file_length
|
||||
};
|
||||
drive.in_order.insert(offset_index, new_gap);
|
||||
// if the new gap has a gap after it, merge them
|
||||
|
||||
if offset_index < drive.in_order.len()-1 {
|
||||
if let Record::Gap {
|
||||
start: _next_gap_start,
|
||||
length: next_gap_length
|
||||
} = drive.in_order[offset_index + 1] {
|
||||
drive.in_order.remove(offset_index + 1);
|
||||
drive.in_order[offset_index] = Record::Gap {
|
||||
start: *file_start,
|
||||
length: *file_length + next_gap_length
|
||||
};
|
||||
}
|
||||
}
|
||||
// if the new gap has a gap before it, merge them
|
||||
if offset_index > 0 {
|
||||
if let Record::Gap {
|
||||
start: prev_gap_start,
|
||||
length: prev_gap_length
|
||||
} = drive.in_order[offset_index - 1] {
|
||||
drive.in_order.remove((file_index as i32 + offset) as usize);
|
||||
drive.in_order[offset_index - 1] = Record::Gap {
|
||||
start: prev_gap_start,
|
||||
length: prev_gap_length + *file_length
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// update the files hashmap
|
||||
drive.files.insert(file_number, new_file);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// if the gap is neither bigger than the file nor equal to the file it won't fit!
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return drive;
|
||||
}
|
||||
|
||||
fn checksum_enum(drive: &Drive) -> i64 {
|
||||
let mut sum: i64 = 0;
|
||||
for record in drive.in_order.iter() {
|
||||
match record {
|
||||
Record::Gap { start: _, length: _ } => (),
|
||||
Record::File { start, length, file } => {
|
||||
for i in 0..*length {
|
||||
sum += (file * (i + start)) as i64;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
fn find_order_index(drive: &Drive, file_number: i32) -> usize {
|
||||
for i in 0..drive.in_order.len() {
|
||||
if let Record::File { file, .. } = drive.in_order[i] {
|
||||
if file == file_number {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return usize::MAX;
|
||||
}
|
6
day10/Cargo.toml
Normal file
6
day10/Cargo.toml
Normal file
@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "day10"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
124
day10/src/main.rs
Normal file
124
day10/src/main.rs
Normal file
@ -0,0 +1,124 @@
|
||||
use std::collections::HashSet;
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::env;
|
||||
use crate::Dir::{North, South, East, West };
|
||||
|
||||
// nodes are (coord, direction) pairs.
|
||||
// neighbors are step1+turnleft, step2+turnleft, step3+turnleft, step1+turnright, etc
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||
enum Dir { North, South, East, West }
|
||||
|
||||
fn step(start: &Coord, d: Dir, steps: i32) -> Coord {
|
||||
match d {
|
||||
North => (start.0, start.1 - steps),
|
||||
South => (start.0, start.1 + steps),
|
||||
East => (start.0 + steps, start.1),
|
||||
West => (start.0 - steps, start.1),
|
||||
}
|
||||
}
|
||||
|
||||
type Coord = (i32, i32);
|
||||
|
||||
fn main() {
|
||||
println!("Hello, AoC day 04!");
|
||||
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() != 2 {
|
||||
println!("wrong number of arguments!");
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
let file_path = &args[1];
|
||||
|
||||
let contents = fs::read_to_string(file_path).expect("Should have been able to read the file");
|
||||
let mut grid: HashMap<Coord, i8> = HashMap::new();
|
||||
let mut trailheads: HashSet<Coord> = HashSet::new();
|
||||
|
||||
let mut x;
|
||||
// build our grid!
|
||||
let mut y = 0;
|
||||
for line in contents.lines() {
|
||||
x = 0;
|
||||
for c in line.chars() {
|
||||
let num = c.to_string().parse::<i8>().unwrap();
|
||||
let coords = (x,y);
|
||||
if num == 0 {
|
||||
trailheads.insert(coords);
|
||||
}
|
||||
grid.insert(coords, num);
|
||||
x += 1;
|
||||
}
|
||||
y += 1;
|
||||
}
|
||||
|
||||
let mut sum: i32 = 0;
|
||||
for head in &trailheads {
|
||||
sum += score(*head, &grid);
|
||||
}
|
||||
println!("there are {sum} points in score total");
|
||||
|
||||
let mut sum: i32 = 0;
|
||||
for head in &trailheads {
|
||||
sum += rating(*head, &grid);
|
||||
}
|
||||
println!("there are {sum} points in rating total")
|
||||
}
|
||||
|
||||
fn score(head: Coord, grid: &HashMap<Coord, i8>) -> i32 {
|
||||
let mut visited: HashSet<Coord> = HashSet::new();
|
||||
let mut front: HashSet<Coord> = HashSet::from([head]);
|
||||
let mut peaks: HashSet<Coord> = HashSet::new();
|
||||
|
||||
while !front.is_empty() {
|
||||
let mut new_front: HashSet<Coord> = HashSet::new();
|
||||
|
||||
for x in front.drain() {
|
||||
if grid[&x] == 9 {
|
||||
peaks.insert(x);
|
||||
}
|
||||
visited.insert(x);
|
||||
for x2 in neighbors(&x, &grid) {
|
||||
if visited.contains(&x2) { continue; }
|
||||
new_front.insert(x2);
|
||||
}
|
||||
}
|
||||
front = new_front;
|
||||
}
|
||||
return peaks.len() as i32;
|
||||
}
|
||||
|
||||
fn rating(head: Coord, grid: &HashMap<Coord, i8>) -> i32 {
|
||||
let mut visited: HashSet<Coord> = HashSet::new();
|
||||
let mut front: Vec<Coord> = vec![head];
|
||||
|
||||
let mut points = 0;
|
||||
while !front.is_empty() {
|
||||
let mut new_front: Vec<Coord> = vec![];
|
||||
|
||||
for x in front.drain(..) {
|
||||
if grid[&x] == 9 {
|
||||
points += 1;
|
||||
}
|
||||
visited.insert(x);
|
||||
let x_neighbors = neighbors(&x, &grid);
|
||||
|
||||
for x2 in x_neighbors {
|
||||
if visited.contains(&x2) { continue; }
|
||||
new_front.push(x2);
|
||||
}
|
||||
}
|
||||
front = new_front;
|
||||
}
|
||||
return points as i32;
|
||||
}
|
||||
|
||||
|
||||
fn neighbors(head: &Coord, grid: &HashMap<Coord, i8>) -> HashSet<Coord> {
|
||||
let height = grid[head];
|
||||
let neighbors = vec![North, South, East, West]
|
||||
.into_iter()
|
||||
.map(|x| step(head, x, 1))
|
||||
.filter(|x| grid.contains_key(&x) && grid[&x]==height+1);
|
||||
return neighbors.collect();
|
||||
}
|
7
day11/Cargo.toml
Normal file
7
day11/Cargo.toml
Normal file
@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "day11"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
memoize = "0.4.2"
|
69
day11/src/main.rs
Normal file
69
day11/src/main.rs
Normal file
@ -0,0 +1,69 @@
|
||||
use std::iter;
|
||||
use std::env;
|
||||
|
||||
use std::fs;
|
||||
use memoize::memoize;
|
||||
|
||||
fn main() {
|
||||
println!("Hello, AoC day 09!");
|
||||
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() != 2 {
|
||||
println!("wrong number of arguments!");
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
let file_path = &args[1];
|
||||
|
||||
let initial_stones = fs::read_to_string(file_path)
|
||||
.expect("should be able to read the file")
|
||||
.trim()
|
||||
.to_string();
|
||||
let mut stones = initial_stones
|
||||
.split(" ")
|
||||
.map(|s| s.to_string())
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
for _i in 0..25 {
|
||||
stones = stones.into_iter().flat_map(blink).collect();
|
||||
}
|
||||
println!("there were {} stones after 25 blinks", stones.len());
|
||||
|
||||
let limit = 75;
|
||||
let mut count = 0;
|
||||
for s in initial_stones.split(" ") {
|
||||
count += blink_recursive(s.parse().unwrap(), limit);
|
||||
}
|
||||
println!("there were {count} stones after {limit} blinks!");
|
||||
}
|
||||
|
||||
fn blink(number: String) -> Box<dyn Iterator<Item=String>>{
|
||||
if number == "0" {
|
||||
return Box::new(iter::once("1".to_string()));
|
||||
}
|
||||
let l = number.len();
|
||||
if l % 2 == 0 {
|
||||
return Box::new(iter::once(number[..l/2].parse::<u128>().unwrap().to_string())
|
||||
.chain(iter::once(number[l/2..].parse::<u128>().unwrap().to_string())));
|
||||
}
|
||||
let number = number.parse::<u128>().unwrap();
|
||||
let new_string = (number*2024).to_string();
|
||||
return Box::new(iter::once(new_string));
|
||||
}
|
||||
|
||||
#[memoize]
|
||||
fn blink_recursive(label: i128, blinks: i32) -> i128 {
|
||||
if blinks == 0 {
|
||||
return 1;
|
||||
} else if label == 0 {
|
||||
return blink_recursive(1, blinks - 1);
|
||||
}
|
||||
let s = label.to_string();
|
||||
let len = s.len();
|
||||
if len % 2 == 0 {
|
||||
let first: i128 = s[..len/2].parse().unwrap();
|
||||
let second: i128 = s[len/2..].parse().unwrap();
|
||||
return blink_recursive(first, blinks - 1) + blink_recursive(second, blinks - 1);
|
||||
}
|
||||
return blink_recursive(2024*label, blinks - 1);
|
||||
}
|
6
day12/Cargo.toml
Normal file
6
day12/Cargo.toml
Normal file
@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "day12"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
252
day12/src/main.rs
Normal file
252
day12/src/main.rs
Normal file
@ -0,0 +1,252 @@
|
||||
use std::collections::HashSet;
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::env;
|
||||
use crate::Dir::{North, South, East, West };
|
||||
|
||||
// nodes are (coord, direction) pairs.
|
||||
// neighbors are step1+turnleft, step2+turnleft, step3+turnleft, step1+turnright, etc
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||
enum Dir { North, South, East, West }
|
||||
|
||||
fn step(start: &Coord, d: Dir, steps: i32) -> Coord {
|
||||
match d {
|
||||
North => (start.0, start.1 - steps),
|
||||
South => (start.0, start.1 + steps),
|
||||
East => (start.0 + steps, start.1),
|
||||
West => (start.0 - steps, start.1),
|
||||
}
|
||||
}
|
||||
|
||||
type Coord = (i32, i32);
|
||||
|
||||
fn main() {
|
||||
println!("Hello, AoC day 04!");
|
||||
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() != 2 {
|
||||
println!("wrong number of arguments!");
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
let file_path = &args[1];
|
||||
|
||||
let contents = fs::read_to_string(file_path).expect("Should have been able to read the file");
|
||||
let mut grid: HashMap<Coord, char> = HashMap::new();
|
||||
|
||||
let mut x;
|
||||
// build our grid!
|
||||
let mut y = 0;
|
||||
for line in contents.lines() {
|
||||
x = 0;
|
||||
for c in line.chars() {
|
||||
let coords = (x,y);
|
||||
grid.insert(coords, c);
|
||||
x += 1;
|
||||
}
|
||||
y += 1;
|
||||
}
|
||||
|
||||
|
||||
// part a
|
||||
let mut sum = 0;
|
||||
|
||||
let mut cache: Vec<HashSet<Coord>>= Vec::new();
|
||||
for (location, _color) in &grid {
|
||||
cache = fill_region_and_cache(location, &grid, cache);
|
||||
}
|
||||
|
||||
println!("we found {} regions", cache.len());
|
||||
for region in &cache {
|
||||
println!("a region with color {} was found to have {} squares",
|
||||
grid.get(region.iter().nth(0).unwrap()).unwrap(),
|
||||
region.len());
|
||||
}
|
||||
|
||||
for region in &cache {
|
||||
let mut total_perimeter = 0;
|
||||
let mut area = 0;
|
||||
for location in region {
|
||||
total_perimeter += perimeter(&location, &grid);
|
||||
area += 1;
|
||||
}
|
||||
sum += area * total_perimeter;
|
||||
}
|
||||
|
||||
println!("the total cost is {sum}");
|
||||
|
||||
|
||||
// part b
|
||||
// a list of sides (a side is a set of sidesegments)
|
||||
let mut sides_cache: Vec<HashSet<SideSegment>> = Vec::new();
|
||||
|
||||
// for every location in the grid,
|
||||
for (location, _color) in &grid {
|
||||
// for every side ajdacent to that location, update the side cache with its side.
|
||||
for sideseg in sides(location, &grid) {
|
||||
sides_cache = spread_over_side_and_cache(
|
||||
&sideseg,
|
||||
&grid,
|
||||
sides_cache);
|
||||
}
|
||||
}
|
||||
|
||||
sum = 0;
|
||||
|
||||
for region in &cache {
|
||||
let mut sides = 0;
|
||||
let mut area = 0;
|
||||
for side in &sides_cache {
|
||||
let first_location = side.iter().nth(0).unwrap().1;
|
||||
if region.contains(&first_location) {
|
||||
sides += 1;
|
||||
}
|
||||
}
|
||||
|
||||
area += region.len() as i32;
|
||||
|
||||
let color = grid.get(region.iter().nth(0).unwrap()).unwrap();
|
||||
|
||||
println!("a region with label {color} has area {area} and {sides} sides");
|
||||
|
||||
sum += area * sides;
|
||||
}
|
||||
|
||||
println!("the total cost is {sum}");
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
fn neighbors(head: &Coord) -> Vec<Coord> {
|
||||
return vec![North, South, East, West]
|
||||
.into_iter()
|
||||
.map(|x| step(head, x, 1))
|
||||
.collect();
|
||||
}
|
||||
|
||||
fn same_neighbors(location: &Coord, grid: &HashMap<Coord, char>) -> Vec<Coord> {
|
||||
if let Some(me) = grid.get(location) {
|
||||
return vec![North, South, East, West]
|
||||
.into_iter()
|
||||
.map(|x| step(location, x, 1))
|
||||
.filter(|x| grid.contains_key(x))
|
||||
.filter(|x| me == grid.get(x).expect("tried to look for a missing grid square"))
|
||||
.collect();
|
||||
}
|
||||
return vec![];
|
||||
}
|
||||
|
||||
fn fill_region_and_cache(
|
||||
location: &Coord,
|
||||
grid: &HashMap<Coord, char>,
|
||||
mut cache: Vec<HashSet<Coord>>
|
||||
) -> Vec<HashSet<Coord>> {
|
||||
let mut new_region: HashSet<Coord> = HashSet::from([*location]);
|
||||
let mut accumulator: HashSet<Coord> = HashSet::new();
|
||||
while !new_region.is_empty() {
|
||||
let mut blah: HashSet<Coord> = HashSet::new();
|
||||
for x in &new_region {
|
||||
for existing_region in &cache {
|
||||
if existing_region.contains(x) {
|
||||
return cache;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for x in &new_region {
|
||||
for n in same_neighbors(&x, grid) {
|
||||
if !accumulator.contains(&n) {
|
||||
blah.insert(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for x in new_region.drain() {
|
||||
accumulator.insert(x);
|
||||
}
|
||||
|
||||
new_region = blah;
|
||||
}
|
||||
|
||||
cache.push(accumulator);
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
fn spread_over_side_and_cache(
|
||||
side: &SideSegment,
|
||||
grid: &HashMap<Coord, char>,
|
||||
mut cache: Vec<HashSet<SideSegment>>
|
||||
) -> Vec<HashSet<SideSegment>> {
|
||||
let mut wavefront: HashSet<SideSegment> = HashSet::from([*side]);
|
||||
let mut accumulator: HashSet<SideSegment> = HashSet::new();
|
||||
while !wavefront.is_empty() {
|
||||
// we're iterating over the wavefront of squares to look at for whether they're
|
||||
// part of the side we're building.
|
||||
|
||||
let mut new_wavefront: HashSet<SideSegment> = HashSet::new();
|
||||
|
||||
// look at each side in the wavefront
|
||||
for x in &wavefront {
|
||||
// if it's in a side that we already have cached, then we're done.
|
||||
for existing_side in &cache {
|
||||
if existing_side.contains(x) {
|
||||
// return the cache unchanged.
|
||||
return cache;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// look at each side in the wavefrnot, to build up a new wavefront.
|
||||
for SideSegment(dir, coord) in &wavefront {
|
||||
|
||||
for n in same_neighbors(&coord, grid)
|
||||
// look at neighbors in this region.
|
||||
.into_iter()
|
||||
.flat_map(|next_coord| sides(&next_coord, grid))
|
||||
// look at sides of those neighbors.
|
||||
.filter(|SideSegment(dir2, _coord2)| dir2 == dir) {
|
||||
// filter down to just sides in the same direction.
|
||||
// n is a neighboring side with the same direction as us.
|
||||
if !accumulator.contains(&n) {
|
||||
// if n isn't already in the accumulator, add n to the new wavefront.
|
||||
new_wavefront.insert(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add all the siddes in the wavefront to the accumulator.
|
||||
for x in wavefront.drain() {
|
||||
accumulator.insert(x);
|
||||
}
|
||||
|
||||
wavefront = new_wavefront;
|
||||
}
|
||||
|
||||
cache.push(accumulator);
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
fn perimeter(location: &Coord, grid: &HashMap<Coord, char>) -> i32 {
|
||||
let mut perimeter = 0;
|
||||
for neighbor in neighbors(location) {
|
||||
let neighboring_region = grid.get(&neighbor);
|
||||
if neighboring_region != grid.get(location) {
|
||||
perimeter += 1;
|
||||
}
|
||||
}
|
||||
return perimeter;
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||
struct SideSegment (Dir, Coord);
|
||||
|
||||
fn sides(location: &Coord, grid: &HashMap<Coord, char>) -> Vec<SideSegment> {
|
||||
return vec![North, South, East, West]
|
||||
.into_iter()
|
||||
.map(|x| SideSegment(x, *location) )
|
||||
.filter(|SideSegment(dir, loc)| grid.get(loc) != grid.get(&step(loc, *dir, 1)) )
|
||||
.collect();
|
||||
}
|
Loading…
Reference in New Issue
Block a user