diff --git a/data/day9a.txt b/data/day9a.txt new file mode 100644 index 0000000..6d3dfd9 --- /dev/null +++ b/data/day9a.txt @@ -0,0 +1,100 @@ +7656798786535699986544456789569432012478910965431019876545456789456789769876598765467899898999876545 +8787988665323788999432346797698764123567899876542198765634365695345698955987679879878939767899865434 +9899876543212767898821235789789873294568976987643239874321254789234567943199798989989321656789654321 +3923997654102358987650124599899964989879465698654398765410123892123459894098987699995210345898765432 +2399898763212567986542235678989879878989354798789987654324234589019598789987676579874321256789876543 +1988799874323478987653446789876999868990123459898998765765365678998987678976543456965632369896988656 +0976598765434599998767556799985987756989244567987899876887456989987654567895432345986793578945699788 +1987439986569987899879677899999896645678955698976786987899567898996543456789321266799989999632129899 +9876521987698976989989788978998765534569766789865545698987678967987632389898735477898978789549098999 +9985410198987894577899899769987654323899987898754324569698789659876543678987649678987656678998987988 +9894321239976423456789965456976543212678999989767945789569896543989654579398758789876745567897656677 +9765432547985313345678976567987652104567893979879899893498999932198775691239769895995434456789545466 +9876557656983201235689987679876543213479932867998799912987897893249886789349878934987322345695435345 +8987698869874342345678998789987754328989421359897689909875456789345987898767989123976510656789921236 +7698799878965656756789109897899876567893210498766567897654347897656799939988991034985431267897810145 +7569944989876767969897999956798988878954321987655428989543278998767892123999432549997556348976521234 +6457123496987898978946789545987799989765459876543219878954469999878999019876553998998987899765434546 +5321012345698989989235679939876546999897597987985434569767599898989898929987969887899298969876545856 +7432345957789679892134567898765439898989986799876568978978989787898767898998879756789109456987956767 +8543469898896598743547678929899598787678965343987678989989876676789656987899998645899212347999897878 +9986598789987679654656789219988987676567894212499789993298985565696543456789865435678923456798789989 +6797987678998798765789898998767987544456789101239897894987654434589212347999876566789894567987678991 +5679876567899989876899987897658976432345678992945956989999432125679101238976989678998789679898567890 +4569875478789878987999876896549865421236989889799349878998943434678912347895498789765678998674346791 +3498764345678967898998765689432976532456898767678998767897894545789323556789329898654989876521235689 +4598854234569656989999764567941098743568999854567987654646789956896434768995434986543244987430134578 +5986543145678949876987643458942987654567987543679898743434597897987949989876949875432123986541245679 +9876543266799521965698754567894599778978987654598765432123456798999898999989898764321037897674346899 +9987654777895439874598765678985679899989398767679876541012345899998787899898789975542356798765487998 +9899875688976598753249876789876789998790129878789988652123458923987656788789654986753477899877678967 +8765989789987699842137989899987999987621245989899876543234567899876545634689543798769998998989789349 +9654499894398987652025698987698999876535367891998987654345678998765432123678932659898789987699893252 +8763210943239876543434567896539899987676498932987898765456789429876321014589991045987678997556989101 +7654329994125998754545678997645789699887569549856949987568993212987432345678989239876569895445678912 +8765498789034569875697789789856789543997678998769432198979894101298543467899878998765456789334578923 +9876989678946789976789895678977898432398789769878945239989799212999698578912567899654367890123489934 +2989878567897899987896986999898976521349898954989654349997678929886987679543489998767898921234567895 +1398765457789998798955699888789965432499987893498765498956567898765499889694678989878999432345679987 +0129874345699876549544598765679876549987976792989976987843456789874323996989989876989896587656789598 +9999985234789998632123798654578987698956985889876989876532677898765412345678999655495789698987896499 +8789892145678999321035987573237899987645234578965695997544788989974323496989998743234678919198997989 +7679789234567895492129876432056789997632123489654524598675699879865434789998987654345699909239789765 +6545678945698976989298765432178999876543234567943213469988789767987545678967998765456789898945698873 +7434567896789989978919896543469109998765445678932101979899895656997676789359889887689896787897987432 +4323458997898999869923987654569298999878576789543219898778954349898789891298763998799985656789876541 +5312367899937893998899999865678987895989687897694397654567895298789895990197652129898774345898997632 +4101456789546912987698999876989656793298798998989986743676789989643934989987642034997654234567898843 +3212345678959329898587899987898945689999899999879765432345699876542129976798543126989762104567899956 +4345696799998998765476789998967534577899987895469874321234789987659298765987654234578973213458999767 +6568989899987897654345678989754324566799876789355985432345678998998999654598987545699765454599998978 +7678978999976789543234569876543213345689989893234797645456889439987998963239987656789877667678987899 +8999867898765679652165678965432101234569998932145698776567899929876567892198998767891989788989976789 +9987954569654568969234699876543232345678987643458799897678999899865456789987589878932399899491265679 +9876543498963457898765789988664655457789398756899987998789989798954345899876476989543459954310124567 +9987632987652346789896998799765789868995459977899876789899878687993256798764345797654598765521234678 +9876521098541545678987897679876892979876569998945985456998765566789129987543254699987679876672347889 +9965432987630238989798934567987910989997678949439876567899654365678998999652123589999789987783456791 +9876549998321347897659323569999891999989989239323987698998721234599987898991012378999895498894567892 +9987678999543656789543212678998789898776590167919998789987650123499876987989423467899974349765678943 +9998799987657787899952104567987675797654321257898899899998743235789965765879436567899753267998789954 +8999893498767898999843212689978534689895592346987657978999656397897654554569945688987654379439896896 +7893912349898999789754327898767423576989989587996543567898787456899753543567898799998765998921945989 +6992101999999787689868936987653212345678978998989921456999899567997652101478949896989879887890239876 +4789219878997678545998545699743101234679567899878892367894987678976543212349932945678998776789399765 +3698998969876543434597656798656212356789456898656799468963298899987954524456891234569987675678987654 +4567897656998642323498769898768923487892345987545678979654359956999898765967890123498796454569898743 +9878998549876531014569899999878934598901399876534567899767897549898789876878921356987654343468789654 +6999989734988542123789989987989765679212988765423489939879965439767698989999932349896543232345678965 +5569878921987656245699976596799896789349876544315678921989976599654567898789865498765432101234567896 +4398767892398767656897985435678987898456985432101789439896897988773456987678976599886543272345678987 +3249656789469898967956798546789498987589996563234599598765789876562349876567897989987654365456789498 +1099545696578999898943987657892399798678949764345678997654678954341298765468999978898765566567894349 +2987656789689998789992398967901987639989434995696989896543219653210398874345698766789876678978943234 +3598967998799987689989459878929876525697549879987898789674598954331987543246987645678999899999854395 +4599878959899876578979967999698765434597659867899987678989987895567998632123498932345678989989765989 +5789999945998965464767899989569876546789797654989899569899896789789876541012349853456889879878979878 +9899989896987654323456998778457998787897987543676789456798765678999985432123459766567996767569998765 +4999878789998965434567897654349879898956799432575994349987654567899986545764569877678965453458987654 +3499765678999876575678976543298768939345698921434893298798543456798997656878978988789654312357896543 +2987654567893987689789987652129654321234997890126793129654212367897798767989989799898765501246789432 +1098743458932398789899878761098743210129876789237891019876523458965639879392495678959876712657998321 +3149894967891299893998765432129874321298765678945992323987434567894322989101234569545984327767897210 +4234989898932988992109878543299965452987654567896789456798765898943210993214345778934995456778976521 +4349878789549876789399989665989976769876543456989896567899876789765329865765478989129876568989987432 +5499865678998765678989998789876897998765432345679987678978987898765439876876568993299987679294598543 +6987654567999896789878999896545789999862101476789298789767898919878656987987978999989898989012999656 +9877783456899987894767899989434567987654212388990149897658999423989968998999899998876789992129898767 +9966432346789498943458998765623478798765323499321234998767989434599899879998798987645698989298769898 +7654321356894359954569987654310145679876434578935345999878978995698789965987657893434987678987654929 +8765210897953267895678998765321234589987547699549656789999656789987679954297645932129876567898943213 +7654326789943124999789989876432345891098678789698767995432345789876568892109434894298965458999652101 +8987534567891023988999876998763456789999789898789989654321996999987466789298746789987655357898943219 +9876545689942139877898765987654567996799899999896799765439889898994345678987657899999543234767894398 +6989876789543497766799954398965879345678958999989898976598776787898766789998789998998942123456789987 +5496997897654596545698799239986789234589546789878987897987665876799987897899899887897897545678999876 +4355698998965987639987678992197990145678935698767876799876543454689998956789998786956789656789678995 +3234579879896799998784566789298921266789123987656385689965432343467899345678987654545678987894569984 +2165689954789899876543645878999432378891012396543234799876521012378943234567898763234567898943298765 +3989798765679998765432124567896545989932123987654125689987632125489762123478969974345689989432109976 +4599899876789239854321012679998656798765434598785434578987654334599873234589659865456799876543413987 \ No newline at end of file diff --git a/data/day9t.txt b/data/day9t.txt new file mode 100644 index 0000000..610bad9 --- /dev/null +++ b/data/day9t.txt @@ -0,0 +1,5 @@ +2199943210 +3987894921 +9856789892 +8767896789 +9899965678 \ No newline at end of file diff --git a/src/bin/day9.rs b/src/bin/day9.rs new file mode 100644 index 0000000..6500ee4 --- /dev/null +++ b/src/bin/day9.rs @@ -0,0 +1,265 @@ +use itertools::Itertools; +use std::fmt; +use thiserror::Error; + +const TEST_DATA: &str = include_str!("../../data/day9t.txt"); +const DAY9_DATA: &str = include_str!("../../data/day9a.txt"); + +#[derive(Debug, Error)] +enum Oopsie { + #[error("Tried to parse an empty graph?")] + EmptyGraph, + #[error("Got a weird, inconsistent line width around {0}")] + InconsistentGraphWidth(usize), + #[error("Got weird character parsing graph: {0}")] + BadCharacter(char), +} + +struct Graph { + data: Vec, + width: usize, + height: usize, +} + +#[derive(Clone, PartialEq)] +struct Value(u8); + +impl fmt::Debug for Value { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl PartialEq for Value { + fn eq(&self, other: &u8) -> bool { + self.0 == *other + } +} + +impl TryFrom for Value { + type Error = Oopsie; + + fn try_from(value: char) -> Result { + Ok(Value( + value.to_digit(10).ok_or(Oopsie::BadCharacter(value))? as u8, + )) + } +} + +impl> Graph { + fn from_file_data(file_data: &str) -> Result, Oopsie> { + let mut data = Vec::with_capacity(file_data.len()); + let mut width = None; + let mut height = 0; + let mut temp_width = 0; + + for c in file_data.chars() { + if c == '\n' { + height += 1; + + if let Some(x) = width { + if x != temp_width { + return Err(Oopsie::InconsistentGraphWidth(height)); + } + } else { + width = Some(temp_width); + } + + temp_width = 0; + } else { + data.push(T::try_from(c)?); + temp_width += 1; + } + } + + if temp_width != 0 { + height += 1; + } + + if height == 0 { + return Err(Oopsie::EmptyGraph); + } + + Ok(Graph { + data, + width: width.unwrap(), + height, + }) + } + + fn get(&self, x: usize, y: usize) -> Option> { + if x >= self.width || y >= self.height { + return None; + } + + Some(Point { + x, + y, + value: &self.data[(y * self.width) + x], + }) + } + + fn points(&self) -> Points<'_, T> { + Points { + graph: self, + curx: 0, + cury: 0, + } + } + + fn neighbors(&self, x: usize, y: usize) -> Vec> { + let mut retval = Vec::new(); + + if x > 0 { + retval.push(self.get(x - 1, y).unwrap()); + } + if y > 0 { + retval.push(self.get(x, y - 1).unwrap()); + } + if let Some(v) = self.get(x + 1, y) { + retval.push(v); + } + if let Some(v) = self.get(x, y + 1) { + retval.push(v); + } + + retval + } +} + +impl Graph { + fn basin_around(&self, x: usize, y: usize) -> Vec> { + let base = match self.get(x, y) { + None => return Vec::new(), + Some(p) => p, + }; + let mut retval = Vec::new(); + let mut queue = vec![base.clone()]; + + while let Some(p) = queue.pop() { + if *p.value != 9 { + for x in self.neighbors(p.x, p.y).drain(..) { + if !retval.contains(&x) && !queue.contains(&x) { + queue.push(x); + } + } + + if !retval.contains(&p) { + retval.push(p); + } + } + } + + retval + } +} + +#[derive(Clone)] +struct Point<'a, T> { + x: usize, + y: usize, + value: &'a T, +} + +impl<'a, T> fmt::Debug for Point<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "({},{})", self.x, self.y) + } +} + +impl<'a, T> PartialEq for Point<'a, T> { + fn eq(&self, other: &Self) -> bool { + self.x == other.x && self.y == other.y + } +} + +struct Points<'a, T> { + graph: &'a Graph, + curx: usize, + cury: usize, +} + +impl<'a, T> Iterator for Points<'a, T> +where + T: Clone + TryFrom, +{ + type Item = Point<'a, T>; + + fn next(&mut self) -> Option { + if self.cury == self.graph.height { + return None; + } + + let next_value = self.graph.get(self.curx, self.cury)?; + + self.curx += 1; + if self.curx == self.graph.width { + self.curx = 0; + self.cury += 1; + } + + Some(next_value) + } +} + +struct LowPoints<'a> { + underlying: &'a mut Points<'a, Value>, +} + +impl<'a> Iterator for LowPoints<'a> { + type Item = Point<'a, Value>; + + fn next(&mut self) -> Option { + loop { + let candidate = self.underlying.next()?; + let neighbors = self.underlying.graph.neighbors(candidate.x, candidate.y); + + if neighbors.iter().all(|x| (*x).value.0 > candidate.value.0) { + return Some(candidate); + } + } + } +} + +impl<'a> Points<'a, Value> { + fn low_points(&'a mut self) -> LowPoints<'a> { + LowPoints { underlying: self } + } +} + +fn low_points(graph: &Graph) -> usize { + graph + .points() + .low_points() + .map(|x| x.value.0 as usize + 1) + .sum() +} + +fn basins(graph: &Graph) -> usize { + graph + .points() + .low_points() + .map(|p| graph.basin_around(p.x, p.y)) + .sorted_by(|a, b| a.len().cmp(&b.len()).reverse()) + .take(3) + .map(|x| x.len()) + .product() +} + +fn day9() -> Result<(), Oopsie> { + let test_graph: Graph = Graph::from_file_data(TEST_DATA)?; + println!("Test low point sum: {}", low_points(&test_graph)); + println!("Test basin sum is: {}", basins(&test_graph)); + + let real_graph: Graph = Graph::from_file_data(DAY9_DATA)?; + println!("Real graph sum: {}", low_points(&real_graph)); + println!("Real basin sum is: {}", basins(&real_graph)); + + Ok(()) +} + +fn main() { + if let Err(e) = day9() { + println!("Top level error: {}", e); + } +}