Skip to content

Commit

Permalink
More work on 16
Browse files Browse the repository at this point in the history
  • Loading branch information
rtsuk committed Dec 18, 2022
1 parent 72a4da6 commit 84e1fba
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 160 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ edition = "2021"
[dependencies]
anyhow = "1.0.66"
euclid = { version = "0.22.7", features = ["serde"] }
internment = "0.7.0"
itertools = "0.10.5"
nom = "7.1.1"
once_cell = "1.16.0"
pathfinding = "4.0.0"
petgraph = "0.6.2"
ranges = "0.3.3"
Expand Down
231 changes: 71 additions & 160 deletions src/bin/day16.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
#![allow(unused)]
use anyhow::Error;
use internment::Intern;
use itertools::Itertools;
use once_cell::sync::Lazy;
use pathfinding::prelude::*;
use petgraph::{
dot::{Config, Dot},
graphmap::UnGraphMap,
visit::{EdgeRef, NodeRef},
};
use regex::Regex;
use std::collections::BTreeMap;
use std::{
collections::{BTreeMap, HashMap, HashSet},
sync::Mutex,
};
use structopt::StructOpt;

type Coord = u8;
type Point = (Coord, Coord);
type RoomId = Intern<String>;
type OpenValves = HashSet<RoomId>;

const DATA: &str = include_str!("../../data/day16.txt");
const EXAMPLE_SOLUTION: &str = include_str!("../../data/day16_example.txt");
Expand All @@ -26,51 +33,29 @@ Valve HH has flow rate=22; tunnel leads to valve GG
Valve II has flow rate=0; tunnels lead to valves AA, JJ
Valve JJ has flow rate=21; tunnel leads to valve II"#;

fn letter_code_to_point(s: &str) -> Point {
let values: Vec<_> = s.chars().map(|c| c as Coord - 'A' as Coord).collect();
(values[0], values[1])
}

fn point_to_letter_code(p: Point) -> String {
[p.0, p.1]
.iter()
.map(|v| char::from(('A' as u8 + *v as u8)))
.collect::<String>()
}

#[derive(Debug, Clone)]
struct Room {
name: String,
location: Point,
room_id: RoomId,
flow: usize,
open: bool,
tunnels: Vec<Point>,
tunnels: Vec<RoomId>,
}

impl Room {
fn new(captures: regex::Captures) -> Self {
let name = captures[1].to_string();
let location = letter_code_to_point(&name);
let room_id = Intern::new(captures[1].to_string());
Self {
name,
location,
room_id,
flow: captures[2].parse::<usize>().expect("usize"),
open: false,
tunnels: captures[3].split(", ").map(letter_code_to_point).collect(),
}
}

fn flow(&self) -> usize {
if self.open {
self.flow
} else {
0
tunnels: captures[3]
.split(", ")
.map(|s| Intern::new(s.to_string()))
.collect(),
}
}
}

type RoomSet = BTreeMap<String, Room>;
type FlowGraph = UnGraphMap<Point, ()>;
type RoomMap = HashMap<RoomId, Room>;
type FlowGraph = UnGraphMap<RoomId, String>;

#[derive(Debug, PartialEq)]
enum Action {
Expand All @@ -81,110 +66,51 @@ enum Action {

#[derive(Default, Debug, Clone)]
struct Volcano {
rooms: RoomSet,
rooms: RoomMap,
graph: FlowGraph,
time: usize,
player_room: String,
}

fn successors(point: &Point, graph: &FlowGraph) -> Vec<Point> {
fn successors(point: &RoomId, graph: &FlowGraph) -> Vec<RoomId> {
graph.neighbors(*point).collect()
}

impl Volcano {
fn new(player_room: &str, rooms: RoomSet) -> Self {
fn new(rooms: RoomMap) -> Self {
let graph = Self::make_graph(&rooms);
Self {
rooms,
graph,
time: 0,
player_room: player_room.to_string(),
}
Self { rooms, graph }
}

fn make_graph(rooms: &RoomSet) -> FlowGraph {
fn make_graph(rooms: &RoomMap) -> FlowGraph {
let edges: Vec<_> = rooms
.values()
.flat_map(|r| {
r.tunnels
.iter()
.map(|t| (r.location, *t))
.map(|t| (r.room_id, *t))
.collect::<Vec<_>>()
})
.collect();

FlowGraph::from_edges(&edges)
}

fn current_flow(&self) -> usize {
self.rooms.values().map(Room::flow).sum()
}

fn path_between(&self, start: &str, end: &str) -> Vec<Point> {
let start = letter_code_to_point(start);
let end = letter_code_to_point(end);
fn path_between(&self, start: &str, end: &str) -> Vec<RoomId> {
let start = RoomId::new(start.to_string());
let end = RoomId::new(end.to_string());
let graph = self.graph.clone();
let path = bfs(&start, |p| successors(p, &graph), |p| p == &end).unwrap();
path[1..].to_vec()
}

fn actions(&self, path: &Vec<Point>) -> Vec<Action> {
let mut actions = vec![];
for p in path.iter() {
let name = point_to_letter_code(*p);
let room = self.rooms.get(&name).expect("room");
actions.push(Action::Move(name.clone()));
if room.flow > 0 && !room.open {
actions.push(Action::Open);
}
}
actions
}

fn do_action(&mut self, action: &Action) {
match action {
Action::Move(target) => {
self.player_room = target.clone();
}
Action::Open => {
let room = self.rooms.get_mut(&self.player_room).expect("room");
println!("opening {room:?}");
room.open = true;
}
Action::Idle => (),
}
}

fn calculate_total_pressure(&mut self, actions: &[Action]) -> usize {
let mut total_pressure = 0;

for action in actions {
let current_flow = self.current_flow();
total_pressure += current_flow;
self.do_action(&action);
}
total_pressure
}

fn rooms_with_closed_valves(&self) -> Vec<Point> {
fn rooms_with_valves(&self) -> Vec<RoomId> {
self.rooms
.values()
.filter_map(|r| (!r.open && r.flow > 0).then_some(letter_code_to_point(&r.name)))
.filter_map(|r| (r.flow > 0).then_some(r.room_id))
.collect()
}

fn paths_to_closed_valves(&self) -> Vec<(usize, Vec<Point>)> {
let start = &self.player_room;
let mut paths: Vec<_> = self
.rooms
.values()
.filter_map(|r| (!r.open && r.flow > 0).then_some((r.name.to_string(), r.flow)))
.map(|(name, flow)| (flow, self.path_between(start, name.as_str())))
.map(|(flow, path)| (flow * (30 - path.len()), path))
.collect();
paths.sort_by_key(|(f, p)| *f);
paths.reverse();
paths
fn current_flow(&self, open_valves: &OpenValves) -> usize {
open_valves.iter().map(|room_id| self.rooms.get(room_id).expect("room").flow).sum()
}
}

Expand All @@ -197,10 +123,10 @@ fn parse(s: &str) -> Volcano {
let rooms = re
.captures_iter(s)
.map(Room::new)
.map(|r| (r.name.to_string(), r))
.map(|r| (r.room_id, r))
.collect();

Volcano::new("AA", rooms)
Volcano::new(rooms)
}

#[derive(Debug)]
Expand Down Expand Up @@ -281,16 +207,26 @@ fn main() -> Result<(), Error> {
if opt.graph {
println!(
"{:?}",
Dot::with_config(&volcano.graph, &[Config::EdgeNoLabel])
Dot::with_attr_getters(
&volcano.graph,
&[Config::NodeNoLabel, Config::EdgeNoLabel],
&|_, er| format!("label = \"{}\"", er.weight()),
&|_, nr| format!("label = \"{}\"", nr.weight()),
),
);
} else {
let rooms = volcano.rooms_with_closed_valves();
let rooms = volcano.rooms_with_valves();
println!("{} rooms, {:?}", rooms.len(), rooms);

// for one_permutation in rooms.iter().permutations(rooms.len()) {
// println!("one_permutation, {:?}", one_permutation);
// }

for one_permutation in rooms.iter().permutations(rooms.len()) {
println!(
"one_permutation, {:?}",
one_permutation
.iter()
.map(|r_id| r_id.as_ref())
.collect::<Vec<_>>()
);
}
}

Ok(())
Expand Down Expand Up @@ -329,16 +265,31 @@ mod test {
assert_eq!(last_step.pressure, 81);
assert_eq!(last_step.open_valves.len(), 6);

let mut v = parse(SAMPLE);
let v = parse(SAMPLE);

let mut total_pressure = 0;

let mut open_valves = OpenValves::default();
let mut player_location = RoomId::new("AA".to_string());

for step in example_steps.iter() {
println!("doing step {:?}", step);
let current_flow = v.current_flow();
let current_flow = v.current_flow(&open_valves);
total_pressure += current_flow;
assert_eq!(step.pressure, v.current_flow());
v.do_action(&step.action);
assert_eq!(step.pressure, current_flow);
match &step.action {
Action::Move(t) => {
player_location = RoomId::new(t.to_string());
}

Action::Open => {
open_valves.insert(player_location);
}

Action::Idle => (),
}
dbg!(&player_location);
dbg!(&open_valves);
}

assert_eq!(total_pressure, 1651);
Expand All @@ -348,58 +299,18 @@ mod test {
fn test_volcano() {
let mut v = parse(SAMPLE);

let max_room = v
.rooms
.values()
.filter(|r| !r.open)
.max_by_key(|r| r.flow)
.map(|r| r.name.to_string());

assert_eq!(Some("HH"), max_room.as_ref().map(String::as_str));

if let Some(max_room_name) = max_room.as_ref() {
let r = v.rooms.get_mut(max_room_name).expect("room");
r.open = true;
}

let max_room = v
.rooms
.values()
.filter(|r| !r.open)
.max_by_key(|r| r.flow)
.map(|r| r.name.to_string());

assert_eq!(Some("JJ"), max_room.as_ref().map(String::as_str));

assert_eq!(v.current_flow(), 22);

let mut v = parse(SAMPLE);

let path = v.path_between("AA", "HH");
assert_eq!(path.len(), 5);

dbg!(&path);
let actions = v.actions(&path);
dbg!(&actions);
assert_eq!(actions.len(), 8);

let mut total_flow = 0;
for action in actions.iter() {
v.do_action(action);
let current_flow = v.current_flow();
println!("current_flow = {current_flow}");
total_flow += current_flow;
}

assert_eq!(v.current_flow(), 45);
assert_eq!(total_flow, 177);
}

#[test]
#[ignore]
fn test_permute() {
let mut v = parse(SAMPLE);

let rooms = v.rooms_with_closed_valves();
let rooms = v.rooms_with_valves();

let b: Vec<_> = rooms.iter().permutations(rooms.len()).collect();

Expand Down

0 comments on commit 84e1fba

Please sign in to comment.