Compare commits

..

18 Commits

Author SHA1 Message Date
48c96ce7ea day24: cheat solution used for part 2 submission 2024-12-11 09:54:10 -08:00
342f4c1883 day25: problem 1 solution - cheated with dot 2023-12-24 22:33:20 -08:00
881d0b08ad day24: problem 1 solution 2023-12-23 22:54:21 -08:00
c1eb7761e3 day23: problem 2 solution 2023-12-23 02:54:07 -08:00
98456ed98d day23: problem 1 solution 2023-12-22 22:33:34 -08:00
d6d4c0d056 gitignore 2023-12-22 20:58:14 -08:00
3bb3b3d6b6 day22: cleanup & nice output 2023-12-22 04:19:58 -08:00
dd91259fe2 day22: problem 2 solution.
no help but annoyingly i misread the hint about low/high and didn't submit my *correct* answer for over an hour while I bug hunted!
2023-12-22 03:47:01 -08:00
8495969877 day22: problem 1 solution 2023-12-22 01:32:08 -08:00
190fc92842 day21: problem 2 solution
not proud of this one either, completely cheated and it does not get
exactly the correct error (it was off by one for my input), but the
quadratic solver on Wolfram Alpha was able to do it.
2023-12-21 03:17:11 -08:00
eb6c1f42cd day 21: problem 1 solution
another unsatisfying one where i needed a visual hint to grok what i
should be doing.

a bunch of graph building stuff that wasn't needed for the part 1
solution at all.
2023-12-21 00:34:08 -08:00
512b05f624 day20: problem 2 solution - needed a hint 2023-12-20 01:48:09 -08:00
877101f9a2 day20: problem 1 solution 2023-12-20 00:13:23 -08:00
cdfecf821c day19: refactoring and cleanup 2023-12-19 02:01:47 -08:00
2b921b5fb2 day19: problem 2 solution 2023-12-19 01:01:36 -08:00
a5dea64b32 day19: problem 1 solution 2023-12-18 22:06:03 -08:00
8200c1a8cf root: add .rustfmt.toml 2023-12-18 02:04:22 -08:00
8d178ddfc6 day18: formatting and cleanup 2023-12-18 02:03:48 -08:00
28 changed files with 4495 additions and 66 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
**/target **/target
input

1
.rustfmt.toml Normal file
View File

@ -0,0 +1 @@
max_width = 120

View File

@ -1,9 +1,7 @@
use itertools::Itertools; use itertools::Itertools;
use std::collections::{HashMap, LinkedList}; use std::collections::LinkedList;
use std::fmt::{Display, Write};
use std::fs::File; use std::fs::File;
use std::io::{BufRead, BufReader, Lines}; use std::io::{BufRead, BufReader, Lines};
use std::ops::Range;
use std::time::Instant; use std::time::Instant;
// BOILERPLATE // BOILERPLATE
@ -47,12 +45,7 @@ enum Turn {
impl Direction { impl Direction {
const fn all() -> &'static [Self; 4] { const fn all() -> &'static [Self; 4] {
&[ &[Direction::Left, Direction::Right, Direction::Up, Direction::Down]
Direction::Left,
Direction::Right,
Direction::Up,
Direction::Down,
]
} }
const fn opposite(&self) -> Self { const fn opposite(&self) -> Self {
match self { match self {
@ -127,13 +120,7 @@ impl From<&str> for DigInstruction {
let (dir, count, color) = ( let (dir, count, color) = (
parts.next().unwrap(), parts.next().unwrap(),
parts.next().unwrap(), parts.next().unwrap(),
parts parts.next().unwrap().chars().skip(2).take(6).collect::<String>(),
.next()
.unwrap()
.chars()
.skip(2)
.take(6)
.collect::<String>(),
); );
Self { Self {
dir: dir.into(), dir: dir.into(),
@ -152,7 +139,7 @@ impl DigInstruction {
"1" => Direction::Down, "1" => Direction::Down,
"2" => Direction::Left, "2" => Direction::Left,
"3" => Direction::Up, "3" => Direction::Up,
s => panic!("`{}` is not a valid direction code", s) s => panic!("`{}` is not a valid direction code", s),
}; };
} }
} }
@ -177,9 +164,7 @@ struct DigTile {
impl Default for DigTile { impl Default for DigTile {
fn default() -> Self { fn default() -> Self {
Self { Self { position: (0, 0) }
position: (0, 0),
}
} }
} }
@ -188,9 +173,6 @@ type Position = (isize, isize);
#[derive(Debug)] #[derive(Debug)]
struct DigHole { struct DigHole {
tiles_loop: LinkedList<DigTile>, tiles_loop: LinkedList<DigTile>,
tiles_map: HashMap<Position, DigTile>,
x_range: Range<isize>,
y_range: Range<isize>,
area: u64, area: u64,
} }
@ -203,9 +185,6 @@ impl DigHole {
fn new() -> Self { fn new() -> Self {
DigHole { DigHole {
tiles_loop: LinkedList::new(), tiles_loop: LinkedList::new(),
tiles_map: HashMap::new(),
x_range: 0..0,
y_range: 0..0,
area: 0, area: 0,
} }
} }
@ -215,9 +194,7 @@ impl DigHole {
fn run_plan(&mut self, plan: &DigPlan) { fn run_plan(&mut self, plan: &DigPlan) {
let mut cur_pos = (0, 0); let mut cur_pos = (0, 0);
self.tiles_loop.push_back(DigTile { self.tiles_loop.push_back(DigTile { position: cur_pos });
position: cur_pos,
});
let mut move_offset; let mut move_offset;
for (idx, i) in plan.instructions.iter().enumerate() { for (idx, i) in plan.instructions.iter().enumerate() {
let prev_instruction = if idx > 0 { let prev_instruction = if idx > 0 {
@ -225,17 +202,14 @@ impl DigHole {
} else { } else {
&plan.instructions[plan.instructions.len() - 1] &plan.instructions[plan.instructions.len() - 1]
}; };
let Some(next_instruction) = plan let Some(next_instruction) = plan.instructions.get(idx + 1).or(Some(&plan.instructions[0])) else {
.instructions
.get(idx + 1)
.or(Some(&plan.instructions[0]))
else {
panic!() panic!()
}; };
let cur_turn = prev_instruction.dir.turn_kind(i.dir); let cur_turn = prev_instruction.dir.turn_kind(i.dir);
let next_turn = i.dir.turn_kind(next_instruction.dir); let next_turn = i.dir.turn_kind(next_instruction.dir);
// point needs to live on the 'outside' corner of the character. to
// achieve this we need to offset the move by the following // Point needs to live on the 'outside' corner of the character. to achieve this we need to offset the move
// by the following. Found this empirically but there's probably some mathematical principle behind it...
move_offset = match (cur_turn, next_turn) { move_offset = match (cur_turn, next_turn) {
(Turn::RightNinety, Turn::RightNinety) => 1, (Turn::RightNinety, Turn::RightNinety) => 1,
(Turn::RightNinety, Turn::LeftNinety) => 0, (Turn::RightNinety, Turn::LeftNinety) => 0,
@ -244,14 +218,8 @@ impl DigHole {
t => panic!("turn {:?} not allowed here", t), t => panic!("turn {:?} not allowed here", t),
}; };
cur_pos = self.pos_offset_n( cur_pos = self.pos_offset_n(cur_pos, i.dir.offset(), (i.count as isize + move_offset) as usize);
cur_pos, self.tiles_loop.push_back(DigTile { position: cur_pos });
i.dir.offset(),
(i.count as isize + move_offset) as usize,
);
self.tiles_loop.push_back(DigTile {
position: cur_pos,
});
} }
// Shoelace formula // Shoelace formula
@ -266,36 +234,15 @@ impl DigHole {
} }
} }
impl Display for DigHole {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for y in self.y_range.clone() {
for x in self.x_range.clone() {
f.write_char(if (x, y) == (133, -267) {
'*'
} else if self.tiles_map.contains_key(&(x, y)) {
'#'
} else {
'.'
})?;
}
writeln!(f)?;
}
Ok(())
}
}
impl<T: BufRead> From<Lines<T>> for DigPlan { impl<T: BufRead> From<Lines<T>> for DigPlan {
fn from(lines: Lines<T>) -> Self { fn from(lines: Lines<T>) -> Self {
Self { Self {
instructions: lines instructions: lines.map(|line| DigInstruction::from(line.unwrap().as_str())).collect(),
.map(|line| DigInstruction::from(line.unwrap().as_str()))
.collect(),
} }
} }
} }
// PROBLEM 1 solution // PROBLEM 1 solution
fn problem1<T: BufRead>(input: Lines<T>) -> u64 { fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
let plan = DigPlan::from(input); let plan = DigPlan::from(input);
let mut dig = DigHole::new(); let mut dig = DigHole::new();

25
19/Cargo.lock generated Normal file
View File

@ -0,0 +1,25 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "day19"
version = "0.1.0"
dependencies = [
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
dependencies = [
"autocfg",
]

7
19/Cargo.toml Normal file
View File

@ -0,0 +1,7 @@
[package]
name = "day19"
version = "0.1.0"
edition = "2021"
[dependencies]
num-traits = "0.2.17"

775
19/input Normal file
View File

@ -0,0 +1,775 @@
kzq{m<596:R,x<1149:R,A}
in{s<2440:cd,tgz}
rs{m<2118:zx,s>3630:A,m<2297:cx,vfc}
hzz{m<2478:A,R}
ss{a<692:R,A}
sv{x<877:A,m<2320:R,A}
ph{x<2510:mf,x>3150:R,m>624:xtc,fr}
rg{s>440:hj,a>434:lg,ljm}
hdr{m<1158:R,m>1494:vfl,a<1353:A,R}
zhp{x>633:A,x<248:R,A}
fd{s<1838:A,m<3087:A,a>3495:R,jsg}
qvh{x<3002:crs,m<2331:A,s<3294:dh,R}
qbr{m>3556:R,m<3200:R,R}
sql{m<3219:A,zf}
zf{a<633:A,s<1348:A,A}
rr{x<3035:A,s<3096:A,a<2451:R,A}
bng{s>3079:R,a<724:jd,R}
lv{a<3333:tqj,hth}
bcp{a>1296:dtp,m>2875:dnq,x>1453:xjl,gp}
tz{m>3020:R,R}
gnz{m<692:R,m>1130:R,m>877:R,R}
xqg{x>3462:A,s<3298:A,s>3449:R,R}
pxd{s>1539:jvr,m<2976:frj,x>1748:mr,fts}
tj{m>3165:R,m<3044:R,a>1557:R,A}
jqm{x<2410:xf,m>3048:zm,zbl}
pv{a>2330:R,a>2259:A,x>3069:A,A}
qnz{m>1947:bz,x<2553:jr,x<3256:prq,bx}
cn{x>2438:nl,s<193:A,R}
kkb{s>3372:A,a>295:A,R}
rgr{a>707:R,a>400:hrd,npv}
klp{s<2137:jzp,x>1502:qnz,xj}
fm{m>3258:db,qd}
scf{a>490:A,m>3260:R,R}
btz{a>3068:dr,a>2668:hfb,m<2408:cs,fgr}
xqr{m>2286:A,a<2390:tf,x>3254:R,fkk}
tp{x<1044:A,a>2510:A,s<1735:A,A}
frj{x>2659:vhd,s>1415:lfl,sll}
kcn{s<1967:R,R}
vrm{a>1620:R,x>1345:A,s>3219:A,A}
vm{x<2777:gnz,m<614:hrg,a>903:shm,R}
bqq{a<994:A,m<2065:vrm,s<3419:bnv,zqt}
dtp{a>2018:cp,tbq}
qk{m>3174:slm,m>2948:jqm,gz}
gzq{m>1755:R,A}
nl{s>174:A,a<3022:A,m>2165:R,A}
mkk{a>2954:A,R}
gp{x>808:bng,nmr}
khf{m<1278:R,a>264:A,x>3812:A,A}
xxf{m>1340:A,s>2282:R,R}
bbk{a>1074:gqc,a>616:mdv,pgv}
nrp{x<2414:R,mjc}
kpt{m<3158:bcp,clt}
snl{m<868:km,m<1182:A,x<715:R,A}
cfm{s<3832:R,A}
pt{m>3354:rkx,A}
bz{a>3073:jz,s>2285:A,a>2653:tt,sgd}
zp{s>3328:R,m<981:R,A}
zkg{a<910:kjc,a<1539:hzz,s<2149:xdc,R}
jxm{x>2135:R,m>1114:R,a<668:A,ghm}
szq{s<450:cn,s>694:sdd,s>567:cm,lk}
vlz{s>584:R,s<287:R,A}
fr{s>1707:A,m<281:A,a<1488:R,R}
cdh{x>1427:R,x>1191:R,A}
brs{s>2803:A,a<821:A,s>2565:R,R}
ghm{s<3298:R,m>738:A,x>868:R,R}
cdn{s<745:R,a<1738:R,R}
vgc{s>3045:A,s>2796:R,A}
mjc{m<1443:R,a<1751:R,a<2183:A,R}
rhs{a<1453:A,m>933:kg,A}
vgh{m>3481:zmk,x>2384:ng,s<3473:pt,xvj}
lfb{x>1099:A,A}
cb{a<2973:qkv,x>2753:knq,R}
vd{a<1578:zbh,x>2560:A,s<1772:R,A}
dh{m<2492:R,m>2532:A,A}
tqj{m>1459:R,m>649:R,m>261:A,A}
sch{m<1650:R,A}
brm{x<2245:rm,vsm}
xqc{x>1906:R,s<2586:R,m>1404:R,A}
vmx{m<2123:qsd,s<2169:R,s<2335:R,A}
nrj{s>2051:ml,a<3354:A,x<772:tjm,czr}
zpq{a<805:R,s<2733:A,x<2371:R,R}
lq{m<2008:nxh,m<2296:vmx,zkg}
lfn{m>2015:R,A}
vhv{s>2798:A,m<2650:R,hsz}
rxq{a>2045:A,a>2002:A,R}
gv{x>3571:R,m>3216:A,R}
kjg{x>2443:R,s>3563:A,A}
qv{a>903:A,a<755:A,m<2822:R,A}
dqb{s<1310:A,s<1331:R,s<1351:R,A}
pll{s<1875:R,s<1928:A,a>3979:A,R}
cct{x>2015:cb,m<1524:vqc,a>3121:sdz,rs}
dq{a>3084:R,a>2989:A,x<1241:A,R}
xzx{a>2077:A,a<2023:A,a>2046:A,R}
nmr{s>3420:R,A}
klr{m<1060:A,m>1274:A,ms}
dr{s>1739:qvk,m<2207:R,a>3629:R,gk}
qvk{s<1772:A,a>3599:R,R}
gbj{a>855:R,m>2778:R,A}
drj{s<2725:R,A}
th{s<219:R,s>361:R,R}
dd{x<1745:zgq,a<2006:R,s<3396:tlq,vn}
cpr{x<2287:R,a>3768:R,A}
ntn{a<1603:R,x<3513:A,x<3736:R,A}
zlg{a<2254:xbz,m>1915:xqr,qhg}
mv{m<1685:A,s<2921:A,s<3099:nrm,A}
tnz{m>1738:R,R}
hql{m<1446:rj,qn}
bxk{x>1683:A,m<3826:R,x>1019:A,R}
pg{x>2110:A,A}
vsm{m<1847:R,x<2870:A,R}
qcs{m>3409:xh,x<948:A,x>1273:R,R}
zxz{a<961:gzj,x>900:bg,m<796:nn,A}
mp{a>2922:R,x<1561:A,A}
nb{m>1334:lq,a<942:kjq,s<2216:vcc,zd}
kjc{x>2597:R,m<2416:R,s<2114:R,R}
nz{a>349:A,x<1677:R,A}
cj{x<2805:kjg,x<3336:A,R}
kq{m<1306:R,s<3407:gmg,A}
jp{x<2530:hd,pmn}
vhj{s<2857:A,s<3029:A,x<3204:A,A}
zcr{s>3421:R,A}
kgq{m<2273:bqq,m>2432:mmf,x>1209:zxp,dzs}
crs{a<302:R,A}
fq{m>2037:vgj,m<1874:kfg,m>1968:jx,dtq}
rjx{x<2876:sf,xlc}
qsg{x>2668:A,s<2509:R,R}
gz{a<1575:tvn,a<1927:ljt,a>2208:vmj,nqk}
fzj{s>2118:A,R}
jtn{s<3102:A,m<2129:R,s>3609:A,R}
htb{x<1752:lf,s<2690:zb,a>3052:ntg,chm}
nqk{m>2784:A,x>2006:rxq,xzx}
ktz{a>1145:lnr,zz}
cl{s>2245:R,A}
pp{x>3144:R,a>1054:A,A}
km{s<3596:R,a<217:R,a<378:A,A}
btd{a>328:R,x>3186:R,s<2776:R,R}
nv{a<3170:A,x<1822:A,R}
zqt{a<1992:R,s>3757:A,R}
zvh{x<3431:A,x<3695:R,a<181:A,khf}
rjc{s>3558:rxs,ngt}
fvr{s<1819:A,a>3820:R,A}
gdh{a<1044:A,m>3889:R,a<1313:A,R}
xjl{m>2748:zfc,x>2350:hkv,qdk}
dzs{a<1516:tbs,m<2379:zcr,x>802:A,lvn}
hnk{m<1382:cqm,x<618:R,m>2969:A,ck}
sl{s<2067:R,rq}
bn{m>2044:R,x>1947:R,s>2494:A,A}
klj{m>595:zs,a>1587:A,A}
hd{m<814:rt,hdr}
pm{x>1330:rjf,s>3318:snl,m<757:lgm,jhh}
xn{m>894:R,s<486:R,A}
zxp{s>3057:mnb,A}
bt{x>2338:R,s<2515:R,A}
qdh{x<1435:dfm,m>3320:sn,m<2950:knb,xcq}
zsf{x>3119:R,s<3965:A,R}
lxs{x<1891:R,R}
fp{a>839:A,a<370:A,x>3697:R,A}
fb{x>1576:A,x>1383:R,R}
ssq{x>1389:R,x<683:A,R}
psl{s>1912:nb,mdx}
gzd{a<286:R,m>2048:R,A}
cm{x<2249:R,R}
ps{m<3057:A,a<192:A,R}
ql{x<1455:R,s<2316:A,dz}
tl{s>2746:R,a<2925:jth,s<2631:R,dq}
fl{m<871:R,R}
dnd{s<3340:R,a<1631:A,R}
hpk{x<2148:nbd,kq}
hrd{m>2977:A,R}
cqm{a<3300:A,s<2326:R,R}
vhx{a>264:sx,czq}
hpb{m<1744:bkc,x<1249:cf,vkm}
ms{x>3633:R,a>279:A,x>3536:A,A}
qkk{a<2687:A,s<2740:R,A}
vfc{a<2771:R,A}
rjf{s<3472:br,x<1970:A,a>216:R,A}
xbz{x<3190:lkc,gvr}
mx{s>1027:R,A}
fxr{a<3952:R,R}
sz{m>3436:R,a>3677:R,a<3578:A,R}
qsd{s>2211:A,s>2062:A,x<1916:R,R}
jxd{a>3568:A,x>2134:R,hr}
xlc{m<3646:ntn,m>3749:vb,a<829:hmk,gc}
zmb{x<854:A,tv}
dxx{a>796:brs,m<1206:gq,R}
jj{m<1020:xhs,a<758:ghs,dxx}
jd{s>2702:A,a>437:A,x<1198:A,A}
vb{a<1183:A,x>3584:R,R}
zg{a<1774:R,rp}
fjl{m<1926:A,A}
sx{m>412:rlx,m<224:hsm,ts}
clt{m>3707:kbk,vgh}
sdd{x>1333:A,s>792:ffv,qc}
ltl{s<2747:A,A}
qd{a<1088:A,a>1543:R,R}
lnr{s>1491:A,tgm}
hr{s<2837:A,R}
tbn{m>1412:A,x<1334:ltl,bbq}
db{x<1961:R,R}
dfm{m>3324:R,A}
mdx{s>1504:cc,sb}
xvj{x<1523:R,R}
zm{x>3061:txs,x<2826:R,R}
bkc{s<3439:R,s<3712:R,R}
sp{a>996:A,R}
ftd{x>2631:R,m>2447:R,x>2241:R,R}
qxl{m>591:R,x>508:A,R}
ngs{x<1410:R,A}
nvl{m>836:R,A}
kbd{m>2655:jxd,shz}
bzz{m>2969:R,A}
hth{x>532:R,x<263:A,A}
dtq{x<1785:fjl,qpt}
qbz{m<1913:kkb,m<2018:R,x<2884:tvj,gzd}
tr{x<1816:zxz,s<3159:gj,a>942:vtz,vm}
pdr{a>3495:R,x<960:A,A}
sfd{m<1983:R,a>3730:A,A}
qm{a<3400:A,R}
vlx{x<1628:R,x>2516:A,R}
ztb{s<1848:A,s<1862:R,A}
tlq{m<3961:A,A}
mtx{x<628:R,s<2839:A,R}
dp{m<631:R,x>3424:R,A}
bch{x>544:R,s<1473:A,A}
qkv{s>3614:A,a<2756:R,A}
tlz{s<1526:R,A}
nr{a<1406:R,s>1965:ls,a<1804:R,kmj}
rn{m<3158:R,x>1428:R,A}
fn{s>2334:R,R}
fv{s>496:R,m>2847:A,R}
kdt{x<1651:qx,mn}
dt{s<1907:R,A}
fxp{s>1444:R,x<1151:R,s<1432:R,A}
skp{s<2684:ff,a>3661:grc,kbd}
sf{a<1342:R,R}
vn{x<2642:A,m>3964:A,x>3188:R,R}
cc{m<1326:ph,vd}
st{m>2033:kv,x>2199:mh,qs}
tt{s<2228:R,m>2870:A,x<2943:R,A}
zxc{x<2549:A,a<790:fl,a>821:hs,rv}
nzg{a>721:R,a<709:R,m<1312:R,R}
dpm{x<2029:dvz,tkb}
xzp{x<1033:zhp,m<3650:vk,s>1404:cdh,R}
kmt{x>1508:A,m>3316:sdt,ngs}
jr{x<2063:zvg,x<2340:A,m>945:cl,R}
slv{m<658:A,m>699:R,x<1549:R,R}
txs{x>3387:R,x>3223:A,A}
jbj{x<1080:R,R}
slm{s>2107:qgv,x>1425:pbp,nr}
xj{s<2246:lv,hnk}
rxs{a>3208:R,s>3711:tz,bvt}
sgd{m>2799:R,m<2302:A,s>2226:A,A}
gng{m>1061:xqc,m>382:A,s<2565:A,rkh}
tq{m>3700:R,m>3578:A,m<3552:R,R}
gqc{m<751:vc,m<1228:trs,qln}
bzg{a<3173:A,x>2040:A,A}
gr{s>1874:A,A}
sm{x>1443:fc,m<3252:pqp,lh}
vt{x<324:sch,m<2405:zdb,s<2751:A,mtx}
rrz{a<2925:A,m<1807:A,A}
dvm{s>3793:R,a<714:R,a>723:A,R}
ns{a>216:fzj,s<2226:A,pl}
tsn{m<1180:R,x<1442:ss,a<693:A,dvm}
lgm{m<295:mt,qxl}
gq{a>772:R,s>2860:A,A}
tv{x<1247:R,m>2373:A,R}
shm{a>923:R,R}
tc{m>1326:pd,x<2176:A,A}
zs{a>956:R,x<1552:A,m<1047:A,A}
kbk{a<1245:zr,m>3892:dd,a>1823:hf,rkz}
gc{s>1625:A,x<3456:R,A}
vv{m<1670:jp,a<1470:tvs,dx}
dv{a<3900:dt,x<2401:R,a>3952:pll,A}
rh{x>1522:md,zmb}
bv{s<813:R,x<1898:A,m<2291:A,A}
dfs{a>2165:A,x<2625:A,m<905:A,R}
sq{m<1537:A,a>3700:R,s>3518:R,R}
zbh{m>1984:R,s>1714:A,s>1589:R,A}
pmn{x>3353:mxr,xn}
fc{m<3293:A,R}
hj{m>2607:nz,bv}
cjt{s<3222:R,a>778:A,R}
md{a<2755:A,a<2974:bqz,m<1812:A,R}
jcn{x<3486:A,A}
tm{x<1626:R,x>2473:A,R}
rp{s<2043:A,A}
dtt{x<2469:A,x>2652:A,R}
rkh{a<3899:R,a<3939:A,a>3972:A,R}
vtz{x<3222:jnj,A}
dz{a>1273:R,x<2592:R,a>1109:R,A}
zbl{x<3369:A,a<1301:A,A}
xcc{x<1386:R,x>2717:A,A}
snz{x>1513:R,A}
pnp{m>2870:R,s<1615:R,A}
nzs{x<2228:svh,a>3290:drm,qck}
dx{m<2704:st,bnq}
ml{m>2093:A,m<723:R,x>705:A,A}
nrm{x<2693:R,x>3329:R,a<1508:A,R}
ls{a>2023:R,x<645:A,x<1062:R,R}
zbk{s<3432:A,R}
pcx{a>133:A,R}
xc{a<2881:A,m>2239:R,R}
bvt{x>2521:R,m>3250:A,A}
vfl{a>1549:A,A}
gb{s>2827:fxr,a<3925:vp,tk}
nt{a<3562:R,s>1880:R,A}
qh{s<2766:R,m>1823:R,m<1032:R,R}
vkb{m>3657:R,a<1604:A,R}
qdk{a>446:vrt,a>237:R,A}
nx{s<3053:A,x>2661:A,s<3652:A,A}
ffv{m>2118:A,x<591:A,R}
gnx{m>3550:A,s>340:R,A}
xdc{s<2011:R,a<1886:A,m<2525:R,R}
rnx{x>1741:A,R}
vgj{a<1194:R,a>1316:A,mjf}
rz{m<245:A,R}
jzp{x<1378:nrj,qm}
dhz{m<2327:dnd,x<3029:ftd,m<2461:R,A}
mmf{x>1349:R,m>2512:A,R}
chm{m<1360:sh,a>2753:dmf,x<2850:zdz,rd}
bb{a<1712:R,s<1716:A,fgd}
lfl{s>1480:R,fxp}
vkm{a<944:fb,cdz}
shz{x>2518:R,s>2824:pdr,x<1098:A,R}
lvn{a<1975:A,A}
ng{m<3372:pjq,R}
ntg{x<2896:bvx,hql}
qck{a>2892:R,x>2827:gx,m<1542:njr,R}
xg{m<274:R,R}
qs{a<1914:cdn,R}
bd{m>3657:jbj,m<3576:R,x>715:hk,R}
mnb{m>2328:A,s<3622:A,a<1275:A,A}
lkc{s>3247:R,A}
jz{a<3649:A,a<3878:R,R}
pgv{x<2543:pm,m<747:vhx,s<3269:kb,hx}
vfk{s>2335:sd,R}
mxr{s<525:pz,fp}
qln{s<3057:tbn,m>1379:nrp,tc}
hsm{a<391:A,R}
mdv{a>842:tr,s<3143:jj,mfx}
tgm{x<1435:R,A}
bj{x>2353:R,A}
frk{s>1770:A,lfb}
tnt{a>228:R,x<307:A,A}
bx{s<2240:R,m>928:gf,x<3724:gm,fn}
vr{s<3307:R,m>3549:R,A}
zd{a<1856:ql,vfk}
jth{s<2601:A,A}
tbq{s>3089:A,s<2692:bzz,A}
zz{x>1544:tlz,bch}
mq{m>1048:R,s<3046:dfs,m<905:nvl,R}
xsb{a>968:A,s>3282:R,R}
ctf{m>2182:dhz,s>3326:cj,m<1769:mv,xxb}
cd{a>2405:qjl,s<1247:vv,m<2651:psl,cz}
sdt{m<3413:A,s<1386:A,R}
nm{m>1061:A,m<861:R,a<1430:R,zp}
kbp{a>257:R,R}
czq{s>3301:A,pcx}
rv{x<3177:R,x<3631:R,m>605:R,R}
lg{x<2664:A,fqp}
rq{s<2141:R,x<1452:A,x<2322:A,A}
rkx{m>3406:R,s>2856:R,a>1356:R,R}
rxz{a>3491:ft,m>1104:R,s<1879:A,A}
tqc{s>2638:A,R}
kb{m<1115:btd,a<356:zvh,nh}
svh{m>2131:A,s>1205:A,sdj}
gzj{s>3413:R,m<563:R,R}
tgz{a>2498:nc,m<1536:bbk,m>2606:kpt,dpm}
nc{s>2973:mml,a>3390:skp,htb}
zr{m>3877:A,a<786:zbk,sp}
hf{m<3830:A,R}
rj{s>2812:A,R}
lvf{s<2217:ghz,m<352:fg,s<2328:R,R}
tvn{x<1573:A,ct}
pqp{a<1606:A,s>1424:R,x>1211:A,A}
fpm{a<3321:A,a>3380:A,R}
lpq{a<2688:A,m>550:A,R}
skb{a>496:R,a>370:A,m>3052:A,R}
vqc{a>2824:A,mdj}
sk{s>3032:R,a>1895:kzq,A}
fqp{x<3283:R,x>3734:R,m>2573:A,A}
kv{x<1403:R,A}
jhh{a<365:zv,s>2937:R,a<532:A,A}
qx{m<216:A,m<345:A,R}
tvj{m>2055:R,x>2595:R,R}
fkk{s<3440:A,R}
nh{s>2904:R,x>3474:A,R}
bnq{x<1726:qcs,x>2609:xjj,pg}
bhv{s<1815:btz,a<3302:rh,m<1426:xs,dfq}
grc{a>3883:gb,a>3753:brm,a<3710:vhv,xr}
pl{x<1731:R,a<138:R,m<1085:R,R}
ngt{x>2573:xqg,a>3461:sz,m>3259:vr,vmf}
jnq{m<2176:R,m>2968:R,x>3137:A,R}
sb{m>1679:lfn,s>1408:snz,klj}
jvr{a>1008:bb,s<1695:np,ln}
kjq{m<593:lvf,m<873:bcs,a>378:djc,ns}
hh{s>2869:R,A}
kj{x>1634:rrz,s<2750:A,m>2365:mp,R}
xf{m<3098:A,m<3128:cr,a<995:rn,vj}
zv{x<860:A,a>221:R,x>1038:A,R}
rm{a>3836:A,A}
tbs{a>927:A,R}
gm{m<317:A,A}
sdz{s>3569:R,m<1986:tnz,fpm}
lf{x<960:vt,x<1466:tl,kj}
qhg{m<1782:vgc,a<2401:A,rr}
nxh{a<1275:R,gzq}
tk{x<2449:A,R}
cp{s<3395:R,s>3741:cfm,tm}
zdb{x>591:A,s<2781:A,R}
mhm{x<3419:R,x<3710:A,m>2686:A,A}
pz{m>885:R,a<1395:A,R}
jxl{a<1824:rz,A}
vmr{a>570:jqd,m>2081:qvh,m>1814:qbz,dkx}
kmj{s<1935:R,A}
prq{m>920:xxf,R}
cf{s>3313:A,x>700:A,m>1872:A,R}
qgv{a>1042:vlx,a>596:rqd,R}
mr{x>3011:gv,x>2379:mjx,fm}
cx{a>2892:A,A}
xz{m>2929:R,s<2814:R,s>2986:A,A}
ck{x<994:A,s>2364:A,m>2064:A,R}
zdd{s>911:nzs,szq}
vj{s>2138:A,m>3156:A,R}
jnj{a<996:A,s<3488:R,R}
kfg{s<786:qtq,A}
tjm{s<2018:R,s<2038:R,R}
dkx{x>2834:R,x<2537:R,nx}
zfc{m<2823:gbj,crk}
ff{m>1893:dc,a>3780:gng,xjz}
vmf{x<1019:A,A}
ghs{a<703:A,a>738:R,nzg}
gs{a<393:R,a<477:R,s<3909:R,zsf}
zvg{s>2319:A,x>1801:A,A}
gk{x<2559:R,A}
xjz{s<2581:R,x>2364:R,a<3606:A,tqc}
sd{x<2625:R,A}
mdj{s>3552:A,x>946:A,R}
nbd{x<1042:sq,m<915:A,tmj}
tmj{m>1880:A,x>1503:R,A}
drm{x>3299:R,s<1171:mx,R}
cs{m>1405:A,x>2157:A,tp}
xtc{x<2884:R,A}
gj{a>929:vhj,a<885:R,A}
mml{m>2426:rjc,a>3449:hpk,cct}
hfb{x<1738:xc,R}
vk{m<3572:A,m>3601:A,s>1380:R,R}
dmf{a<2866:A,R}
jx{m<1993:R,R}
vz{m<408:R,A}
cr{m>3114:R,x<927:A,R}
rqd{a>827:R,m<3616:A,R}
hxm{a<2012:A,a<2147:R,s>1974:R,R}
fgr{m>3145:A,R}
ljm{a>175:th,x>2267:R,s>203:A,R}
sh{x<2556:A,s<2802:qkk,x>3244:R,lpq}
mh{x<3136:R,jcn}
jcv{x>3293:A,A}
xh{m<3719:A,x<825:A,R}
nn{s>3158:R,a>1035:A,m>397:A,A}
np{m<3051:pnp,scf}
xd{m<3074:A,s<2935:A,a>2618:A,R}
njr{m>623:R,x<2518:A,R}
vmj{x>1396:pv,a<2288:bfk,R}
rt{a>991:xg,a>395:R,a>238:A,vz}
lmf{a<941:tq,s<1601:R,R}
zdz{s>2837:R,m<2501:cvx,a<2586:R,A}
gmg{a>3638:A,a>3540:R,A}
zmk{m>3609:vkb,jc}
skj{a>908:R,a>564:R,x<489:tnt,R}
mb{m>3811:ktz,x<1811:gl,rjx}
rl{x<2034:R,m>1003:A,a<3236:A,R}
dvz{m>1954:kgq,hpb}
hmk{x>3265:R,s>1639:A,s>1393:R,R}
dfq{s>1884:hjr,a>3606:bq,m<2391:hvz,fd}
jqd{x<2830:cjt,jtn}
vhd{m>2806:pp,a<962:dsd,A}
knb{a>1140:R,qv}
br{x<1778:R,R}
zkt{s>3133:R,m<3860:R,A}
zgq{a>1715:R,m<3933:R,A}
xjj{a<1879:R,x>3421:qbr,x<3014:R,R}
bg{m<731:A,m<1261:R,a<1026:A,A}
ct{x<2963:A,s<2119:R,x>3312:A,A}
bvx{x>2177:R,m<1910:rl,x>1943:bzg,nv}
hg{x>2696:jnq,x>2077:bt,bn}
hs{x<3271:R,A}
tvs{a<654:rg,m<2741:fq,qdh}
qtq{a<982:R,a<1234:A,R}
gcg{m>3101:R,a<480:R,A}
vp{a>3910:R,s>2776:A,a>3898:A,A}
pd{x>1921:A,A}
xt{a>967:R,m>3516:A,a>776:R,A}
qc{a>3321:R,A}
tkb{a<1063:vmr,a>1959:zlg,ctf}
bcs{s>2230:lxs,s>2030:R,m<743:slv,A}
qn{a<3266:A,a>3348:R,m<2667:R,A}
cvx{x>2282:A,m<2005:R,a<2628:A,R}
hrg{m>356:R,s>3696:R,A}
hvz{s<1848:A,a<3490:A,s<1870:A,xcc}
lh{s>1362:R,x>1231:R,a>1747:R,R}
vc{m<422:jxl,x>2315:zdg,sk}
fts{x<952:skj,a>1143:sm,x<1335:sql,kmt}
jsg{x>2596:R,x>1393:A,m>3518:R,A}
hk{x>1083:R,m>3604:R,A}
mt{s>2812:R,x<595:R,s>2688:R,A}
cdz{x>1644:R,m>1862:R,A}
pxv{a<3725:nt,a<3830:A,A}
njd{m<2225:R,mkk}
mjx{s<1366:dqb,tj}
bfk{a>2248:R,R}
xs{m<518:pxv,a<3589:rxz,a>3783:dv,gr}
djc{s>2174:A,js}
zx{x<1016:A,x<1528:R,R}
mzd{a>2015:R,R}
knq{a>3143:R,x>3377:R,R}
bq{s>1846:A,s>1827:cpr,fvr}
zdg{s>3443:A,a>1681:mzd,s<2909:dp,A}
sll{m<2763:R,s<1342:ssq,R}
ft{m<1121:A,s>1882:A,s>1847:R,R}
dnq{x>1487:bl,s<3256:tpk,m<3063:rgr,gmd}
bqz{a>2865:R,A}
sdj{m>1268:A,s<1037:R,a<3341:A,R}
dc{s>2554:A,m<2684:R,qsg}
bbq{s>2709:A,s>2586:A,R}
js{m<1039:A,A}
hjr{x<1882:R,s>1927:R,jcv}
lk{a>3257:bj,a>2920:A,m>1832:fv,R}
ts{a>401:R,a<339:A,s<3429:A,R}
xr{x<1991:sv,s<2852:qh,s<2893:hh,sfd}
bl{m<2988:A,a>784:xsb,a>295:skb,ps}
kg{x<1065:R,x>1534:A,R}
gf{x<3539:A,a<3041:R,R}
ckj{s<2886:A,s>2893:A,A}
fg{s<2314:A,m<139:R,x>1421:A,R}
mn{x<3000:A,m>315:R,m<177:A,R}
vrt{s>3263:A,a<878:R,x>2044:A,R}
npv{x<687:R,A}
ljt{x<2374:R,A}
pbp{a>1398:hxm,a>588:xt,a<333:kcn,R}
fgd{s>1796:R,x>2219:R,m<3069:A,R}
xhs{a<752:R,zpq}
tf{m<2132:A,m>2187:A,A}
qjl{s<1624:zdd,s>1981:klp,bhv}
gx{s<1192:A,s>1376:R,R}
hsz{m>3435:R,s<2747:A,A}
rkz{a>1486:bxk,m<3808:R,a>1388:A,zkt}
mjf{x>2320:A,x<1139:A,R}
gl{a>1425:bd,s>1660:frk,s>1500:lmf,xzp}
gvr{m<2241:R,R}
bnv{s<3071:R,R}
vcc{m<618:sl,x>2269:zg,rhs}
ghz{x>1927:A,a<394:A,m<231:A,R}
crk{m<2842:R,x>2970:A,x<2213:R,R}
mfx{a>731:zxc,m<547:kdt,s>3502:tsn,jxm}
pjq{m<3273:A,a<945:A,A}
tpk{m>2975:drj,xz}
qpt{x>2909:A,a>991:A,a<810:R,R}
jc{m>3552:R,R}
rd{s<2850:R,x>3613:A,s>2904:xd,ckj}
rlx{x>3373:R,A}
hkv{a>502:mhm,s>3324:R,A}
dsd{s<1357:R,x<3340:R,R}
ln{s>1813:ztb,m>3116:rnx,A}
czr{m>2538:R,A}
xxb{s>2993:R,x>2787:R,dtt}
mf{s<1679:A,m<538:A,R}
zb{s>2579:njd,hg}
sn{m>3668:gdh,x>2740:A,s>624:R,gnx}
trs{a<1933:nm,mq}
xcq{m>3184:vlz,x<2483:R,R}
hx{s>3738:gs,x<3448:kbp,klr}
cz{s>1882:qk,m<3508:pxd,mb}
gmd{x>924:A,x<437:A,gcg}
{x=363,m=218,a=594,s=411}
{x=973,m=37,a=2533,s=132}
{x=2768,m=1537,a=645,s=580}
{x=2079,m=168,a=502,s=656}
{x=38,m=145,a=3452,s=1162}
{x=70,m=1238,a=99,s=560}
{x=1261,m=1540,a=283,s=28}
{x=1194,m=780,a=1538,s=2681}
{x=1236,m=1847,a=1403,s=1086}
{x=2227,m=163,a=716,s=410}
{x=9,m=369,a=572,s=1418}
{x=201,m=1472,a=943,s=3541}
{x=2730,m=1777,a=127,s=3031}
{x=329,m=139,a=1310,s=2983}
{x=54,m=981,a=1378,s=783}
{x=658,m=410,a=482,s=490}
{x=1555,m=1246,a=365,s=2769}
{x=1075,m=95,a=806,s=750}
{x=408,m=125,a=2296,s=1599}
{x=715,m=1257,a=1287,s=1638}
{x=63,m=1295,a=169,s=1802}
{x=2068,m=4,a=768,s=365}
{x=1390,m=3366,a=1730,s=1631}
{x=1900,m=622,a=761,s=1108}
{x=1857,m=678,a=136,s=965}
{x=3295,m=47,a=303,s=63}
{x=2853,m=700,a=315,s=647}
{x=379,m=304,a=914,s=59}
{x=2591,m=385,a=1410,s=1473}
{x=140,m=1237,a=769,s=2670}
{x=747,m=395,a=1988,s=633}
{x=30,m=381,a=1966,s=2597}
{x=1969,m=1191,a=501,s=328}
{x=127,m=1200,a=47,s=2408}
{x=1409,m=2136,a=205,s=3122}
{x=150,m=88,a=1082,s=3059}
{x=320,m=2917,a=1371,s=22}
{x=2237,m=1186,a=466,s=2252}
{x=2460,m=7,a=1758,s=874}
{x=421,m=126,a=1338,s=1479}
{x=150,m=675,a=2555,s=267}
{x=1007,m=1178,a=2927,s=2435}
{x=902,m=190,a=77,s=1279}
{x=1393,m=716,a=686,s=569}
{x=339,m=1199,a=275,s=338}
{x=116,m=1997,a=739,s=142}
{x=1332,m=69,a=74,s=1953}
{x=2324,m=2089,a=101,s=1595}
{x=2966,m=696,a=305,s=1221}
{x=810,m=1860,a=141,s=49}
{x=995,m=382,a=1020,s=756}
{x=899,m=133,a=746,s=952}
{x=325,m=1880,a=554,s=1749}
{x=506,m=1478,a=897,s=597}
{x=659,m=424,a=20,s=1447}
{x=4,m=310,a=1121,s=286}
{x=3357,m=396,a=1204,s=2886}
{x=1209,m=614,a=2397,s=408}
{x=505,m=339,a=72,s=709}
{x=3478,m=1412,a=1777,s=1}
{x=2271,m=136,a=83,s=477}
{x=3008,m=280,a=2167,s=1522}
{x=543,m=772,a=433,s=3560}
{x=227,m=2037,a=1444,s=272}
{x=869,m=80,a=2585,s=1212}
{x=893,m=1207,a=124,s=126}
{x=2178,m=2030,a=3000,s=1438}
{x=450,m=1798,a=1307,s=2147}
{x=469,m=1554,a=1080,s=393}
{x=49,m=389,a=813,s=39}
{x=3041,m=607,a=379,s=1435}
{x=62,m=121,a=47,s=2394}
{x=1589,m=2474,a=1599,s=3}
{x=439,m=1493,a=284,s=3}
{x=619,m=1377,a=1107,s=3452}
{x=1802,m=1132,a=851,s=111}
{x=2154,m=464,a=2611,s=2147}
{x=843,m=423,a=355,s=838}
{x=3,m=423,a=139,s=857}
{x=42,m=843,a=2219,s=3695}
{x=1068,m=575,a=88,s=222}
{x=3097,m=238,a=913,s=2452}
{x=109,m=206,a=291,s=728}
{x=3635,m=1815,a=26,s=19}
{x=49,m=115,a=2998,s=26}
{x=2093,m=391,a=267,s=2896}
{x=282,m=2364,a=62,s=62}
{x=361,m=719,a=1679,s=137}
{x=631,m=1131,a=203,s=2281}
{x=794,m=1004,a=868,s=636}
{x=1702,m=415,a=35,s=236}
{x=63,m=1690,a=682,s=2009}
{x=1815,m=3459,a=144,s=374}
{x=91,m=590,a=1651,s=1681}
{x=1561,m=2303,a=307,s=1463}
{x=842,m=927,a=868,s=455}
{x=51,m=23,a=427,s=247}
{x=2431,m=187,a=267,s=184}
{x=1596,m=1904,a=668,s=1553}
{x=922,m=1435,a=336,s=1301}
{x=1038,m=1632,a=220,s=117}
{x=1935,m=1433,a=2988,s=316}
{x=21,m=1158,a=1988,s=1242}
{x=840,m=1272,a=418,s=1198}
{x=1227,m=1811,a=158,s=10}
{x=1836,m=56,a=19,s=1573}
{x=461,m=621,a=532,s=92}
{x=2082,m=1292,a=1032,s=322}
{x=2094,m=249,a=865,s=2030}
{x=1092,m=265,a=1745,s=1720}
{x=2361,m=1087,a=209,s=1934}
{x=92,m=111,a=3188,s=2172}
{x=248,m=2163,a=1351,s=1194}
{x=1675,m=2194,a=324,s=1764}
{x=2148,m=3124,a=854,s=1823}
{x=520,m=617,a=649,s=49}
{x=2497,m=12,a=270,s=161}
{x=248,m=2,a=1026,s=1196}
{x=367,m=860,a=1141,s=61}
{x=411,m=90,a=3401,s=2156}
{x=284,m=265,a=421,s=866}
{x=2152,m=2245,a=1206,s=1056}
{x=1578,m=74,a=93,s=1494}
{x=225,m=50,a=2223,s=1888}
{x=2448,m=372,a=587,s=820}
{x=1222,m=215,a=31,s=1753}
{x=2906,m=1170,a=1120,s=291}
{x=1253,m=329,a=478,s=2489}
{x=563,m=150,a=103,s=1051}
{x=212,m=980,a=1325,s=882}
{x=580,m=754,a=555,s=1470}
{x=522,m=397,a=1018,s=763}
{x=2327,m=1100,a=975,s=511}
{x=324,m=1719,a=48,s=569}
{x=1872,m=3353,a=54,s=1330}
{x=2082,m=1209,a=2390,s=747}
{x=1510,m=2290,a=591,s=45}
{x=60,m=1482,a=924,s=598}
{x=775,m=675,a=1635,s=1560}
{x=316,m=170,a=168,s=1335}
{x=1821,m=79,a=2073,s=92}
{x=612,m=1814,a=1651,s=3496}
{x=116,m=2643,a=1073,s=301}
{x=313,m=92,a=2892,s=109}
{x=1109,m=279,a=701,s=763}
{x=86,m=214,a=1910,s=99}
{x=1049,m=320,a=530,s=216}
{x=192,m=366,a=887,s=1516}
{x=353,m=107,a=257,s=225}
{x=117,m=72,a=810,s=147}
{x=341,m=17,a=190,s=2748}
{x=1397,m=572,a=2275,s=1494}
{x=386,m=1691,a=346,s=985}
{x=603,m=584,a=1464,s=291}
{x=49,m=46,a=2277,s=1200}
{x=3116,m=1,a=500,s=166}
{x=310,m=1054,a=1271,s=1423}
{x=1690,m=721,a=1555,s=232}
{x=1713,m=2002,a=1132,s=611}
{x=229,m=1721,a=1590,s=42}
{x=16,m=1444,a=2296,s=144}
{x=371,m=339,a=1073,s=1585}
{x=1025,m=10,a=491,s=196}
{x=58,m=1161,a=492,s=1401}
{x=305,m=1366,a=919,s=236}
{x=1427,m=654,a=2052,s=2399}
{x=1312,m=723,a=408,s=173}
{x=589,m=703,a=29,s=126}
{x=2609,m=1461,a=266,s=129}
{x=299,m=253,a=857,s=2220}
{x=259,m=600,a=494,s=546}
{x=2978,m=988,a=1660,s=2661}
{x=193,m=478,a=213,s=1631}
{x=3378,m=2275,a=534,s=2317}
{x=654,m=2641,a=284,s=1768}
{x=50,m=76,a=672,s=120}
{x=85,m=630,a=1446,s=1321}
{x=306,m=986,a=808,s=1724}
{x=782,m=1554,a=741,s=2220}
{x=1115,m=48,a=3283,s=958}
{x=214,m=2498,a=996,s=981}
{x=1437,m=53,a=2052,s=914}
{x=3256,m=441,a=47,s=108}
{x=689,m=1954,a=60,s=133}
{x=1,m=2270,a=903,s=13}
{x=2373,m=956,a=2739,s=860}
{x=1270,m=357,a=2115,s=1868}
{x=114,m=587,a=349,s=62}
{x=186,m=491,a=464,s=46}
{x=2001,m=99,a=649,s=1854}
{x=408,m=386,a=681,s=774}
{x=697,m=775,a=2740,s=1582}
{x=525,m=2075,a=11,s=822}
{x=621,m=1069,a=233,s=255}
{x=979,m=2750,a=241,s=199}
{x=2006,m=665,a=2211,s=1776}
{x=483,m=213,a=2042,s=67}
{x=1181,m=1199,a=1513,s=919}
{x=1378,m=274,a=438,s=546}
{x=59,m=224,a=3034,s=254}

392
19/src/main.rs Normal file
View File

@ -0,0 +1,392 @@
use std::collections::HashMap;
use std::fs::File;
use std::io::{BufRead, BufReader, Lines};
use std::ops::{Range, IndexMut, Index};
use std::time::Instant;
use num_traits::Num;
// BOILERPLATE
type InputIter = Lines<BufReader<File>>;
fn get_input() -> InputIter {
let f = File::open("input").unwrap();
let br = BufReader::new(f);
br.lines()
}
fn main() {
let start = Instant::now();
let ans1 = problem1(get_input());
let duration = start.elapsed();
println!("Problem 1 solution: {} [{}s]", ans1, duration.as_secs_f64());
let start = Instant::now();
let ans2 = problem2(get_input());
let duration = start.elapsed();
println!("Problem 2 solution: {} [{}s]", ans2, duration.as_secs_f64());
}
// DATA
const INPUT_RANGE: Range<u64> = 1..4001;
#[derive(Debug)]
struct RulePredicate {
op: PredicateOperator,
var: char,
}
#[derive(Debug, Clone)]
enum RuleAction {
Terminate(bool),
Jump(String),
}
impl From<&str> for RuleAction {
fn from(s: &str) -> Self {
match s {
"A" => Self::Terminate(true),
"R" => Self::Terminate(false),
s => Self::Jump(s.into()),
}
}
}
#[derive(Debug)]
struct Rule {
pred: RulePredicate,
action: RuleAction,
}
#[derive(Debug)]
enum PredicateOperator {
Always,
Lt(u64),
Gt(u64),
}
#[derive(Debug, Clone)]
struct XmasRanges {
ranges: [Vec<Range<u64>>; 4],
}
impl XmasRanges {
fn none() -> XmasRanges {
Self {
ranges: [vec![0..0], vec![0..0], vec![0..0], vec![0..0]],
}
}
fn all() -> XmasRanges {
Self {
ranges: [
vec![INPUT_RANGE],
vec![INPUT_RANGE],
vec![INPUT_RANGE],
vec![INPUT_RANGE],
],
}
}
fn idx(c: char) -> usize {
match c {
'x' => 0,
'm' => 1,
'a' => 2,
's' => 3,
c => panic!("`{}` is not a valid xmas char", c),
}
}
fn insert(&mut self, c: char, v: Vec<Range<u64>>) {
self.ranges[Self::idx(c)] = v;
}
fn count_states(&self) -> u64 {
self.ranges
.iter()
.map(|ranges| ranges.iter().map(|range| range.end - range.start).sum::<u64>())
.product()
}
}
impl Index<char> for XmasRanges {
type Output = Vec<Range<u64>>;
fn index(&self, index: char) -> &Self::Output {
&self.ranges[Self::idx(index)]
}
}
impl IndexMut<char> for XmasRanges {
fn index_mut(&mut self, index: char) -> &mut Self::Output {
&mut self.ranges[Self::idx(index)]
}
}
fn range_overlap<T: Num + Ord + Copy>(r1: &Range<T>, r2: &Range<T>) -> Option<Range<T>> {
let new_start = std::cmp::max(r1.start, r2.start);
let new_end = std::cmp::min(r1.end - T::one(), r2.end - T::one());
if new_start <= std::cmp::min(r1.end - T::one(), r2.end - T::one()) && new_end >= std::cmp::max(r1.start, r2.start)
{
Some(new_start..new_end + T::one())
} else {
None
}
}
fn range_exclude<T: Num + Ord + Copy>(keep: &Range<T>, exclude: &Range<T>) -> Vec<Range<T>> {
let mut residual = Vec::new();
if let Some(overlap) = range_overlap(keep, exclude) {
if keep.start < overlap.start {
residual.push(keep.start..overlap.start);
}
if keep.end > overlap.end {
residual.push(overlap.end..keep.end);
}
} else {
residual.push(keep.clone());
}
residual
}
impl From<&str> for PredicateOperator {
fn from(s: &str) -> Self {
let (op_s, val_s) = s.split_at(1);
match op_s {
"<" => PredicateOperator::Lt(val_s.parse().unwrap()),
">" => PredicateOperator::Gt(val_s.parse().unwrap()),
s => panic!("unknown operator {}", s),
}
}
}
impl From<&str> for RulePredicate {
fn from(s: &str) -> Self {
let (var_s, pred_s) = s.split_at(1);
Self {
op: pred_s.into(),
var: var_s.chars().next().unwrap(),
}
}
}
impl RulePredicate {
fn check(&self, part: &Part) -> bool {
match self.op {
PredicateOperator::Always => true,
PredicateOperator::Gt(val) => part.0[&self.var] > val,
PredicateOperator::Lt(val) => part.0[&self.var] < val,
}
}
fn matching_range(&self) -> Range<u64> {
match self.op {
PredicateOperator::Always => INPUT_RANGE,
PredicateOperator::Gt(val) => val + 1..INPUT_RANGE.end,
PredicateOperator::Lt(val) => INPUT_RANGE.start..val,
}
}
}
impl From<&str> for Rule {
fn from(s: &str) -> Self {
if let Some((predicate_s, action_s)) = s.split_once(':') {
Self {
pred: predicate_s.into(),
action: action_s.into(),
}
} else {
Self {
pred: RulePredicate {
op: PredicateOperator::Always,
var: '\0',
},
action: s.into(),
}
}
}
}
impl Rule {
fn predicate_result(&self, ranges: XmasRanges) -> (XmasRanges, XmasRanges) {
if let PredicateOperator::Always = self.pred.op {
(ranges, XmasRanges::none())
} else {
let (mut matching, mut unmatching) = (ranges.clone(), ranges.clone());
let relevant_ranges = &ranges[self.pred.var];
matching.insert(
self.pred.var,
relevant_ranges
.iter()
.filter_map(|range| range_overlap(range, &self.pred.matching_range()))
.collect(),
);
unmatching.insert(
self.pred.var,
relevant_ranges
.iter()
.flat_map(|range| range_exclude(range, &self.pred.matching_range()))
.collect(),
);
(matching, unmatching)
}
}
fn possible_ranges(&self, wfs: &Workflows, ranges: XmasRanges) -> (u64, XmasRanges) {
let (matching, unmatching) = self.predicate_result(ranges);
match &self.action {
RuleAction::Terminate(true) => (matching.count_states(), unmatching),
RuleAction::Terminate(false) => (0, unmatching),
RuleAction::Jump(wf) => (wfs.0[wf].possible_ranges(wfs, matching), unmatching),
}
}
}
#[derive(Debug)]
struct Workflow {
name: String,
rules: Vec<Rule>,
}
impl From<&str> for Workflow {
fn from(s: &str) -> Self {
let (name_s, rest_s) = s.split_once('{').unwrap();
let rules = rest_s.split_once('}').unwrap().0.split(',').map(|r| r.into()).collect();
Self {
name: name_s.into(),
rules,
}
}
}
impl Workflow {
fn execute(&self, part: &Part) -> RuleAction {
for r in &self.rules {
if r.pred.check(part) {
return r.action.clone();
}
}
panic!("unhandled part {:?}", part);
}
fn possible_ranges(&self, wfs: &Workflows, mut ranges: XmasRanges) -> u64 {
let mut accum = 0u64;
for r in &self.rules {
let count;
(count, ranges) = r.possible_ranges(wfs, ranges);
accum += count
}
accum
}
}
impl Workflows {
fn execute(&self, part: &Part) -> bool {
let mut action = RuleAction::Jump("in".into());
loop {
match &action {
RuleAction::Terminate(b) => return *b,
RuleAction::Jump(j) => {
let next_workflow = &self.0[j];
action = next_workflow.execute(part)
}
}
}
}
fn count_possible_states(&self) -> u64 {
self.0["in".into()].possible_ranges(self, XmasRanges::all())
}
}
#[derive(Debug)]
struct Workflows(HashMap<String, Workflow>);
#[derive(Debug)]
struct Part(HashMap<char, u64>);
impl From<&str> for Part {
fn from(s: &str) -> Self {
let (_, vars_s) = s.split_once('{').unwrap();
let vars = vars_s.split_once('}').unwrap().0.split(',');
let mut part = HashMap::new();
for var in vars {
let (name, val) = var.split_once('=').unwrap();
part.insert(name.chars().next().unwrap(), val.parse().unwrap());
}
Self(part)
}
}
type Parts = Vec<Part>;
// PROBLEM 1 solution
fn problem1<T: BufRead>(mut input: Lines<T>) -> u64 {
let mut wfs = Workflows(HashMap::new());
let mut parts: Parts = Vec::new();
while let Some(Ok(line)) = input.next() {
if line != "" {
let wf: Workflow = line.as_str().into();
wfs.0.insert(wf.name.clone(), wf);
} else {
break;
}
}
while let Some(Ok(line)) = input.next() {
parts.push(line.as_str().into());
}
parts
.iter()
.filter(|part| wfs.execute(part))
.map(|part| part.0.values().sum::<u64>())
.sum::<u64>()
}
// PROBLEM 2 solution
fn problem2<T: BufRead>(mut input: Lines<T>) -> u64 {
let mut wfs = Workflows(HashMap::new());
let mut parts: Parts = Vec::new();
while let Some(Ok(line)) = input.next() {
if line != "" {
let wf: Workflow = line.as_str().into();
wfs.0.insert(wf.name.clone(), wf);
} else {
break;
}
}
while let Some(Ok(line)) = input.next() {
parts.push(line.as_str().into());
}
wfs.count_possible_states()
}
#[cfg(test)]
mod tests {
use crate::*;
use std::io::Cursor;
const EXAMPLE: &str = &"px{a<2006:qkq,m>2090:A,rfg}
pv{a>1716:R,A}
lnx{m>1548:A,A}
rfg{s<537:gd,x>2440:R,A}
qs{s>3448:A,lnx}
qkq{x<1416:A,crn}
crn{x>2662:A,R}
in{s<1351:px,qqz}
qqz{s>2770:qs,m<1801:hdj,R}
gd{a>3333:R,R}
hdj{m>838:A,pv}
{x=787,m=2655,a=1222,s=2876}
{x=1679,m=44,a=2067,s=496}
{x=2036,m=264,a=79,s=2244}
{x=2461,m=1339,a=466,s=291}
{x=2127,m=1623,a=2188,s=1013}";
#[test]
fn problem1_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem1(c.lines()), 19114);
}
#[test]
fn problem2_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem2(c.lines()), 167409079868000);
}
}

201
20/Cargo.lock generated Normal file
View File

@ -0,0 +1,201 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
dependencies = [
"memchr",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "day20"
version = "0.1.0"
dependencies = [
"lazy-regex",
"num",
]
[[package]]
name = "lazy-regex"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d12be4595afdf58bd19e4a9f4e24187da2a66700786ff660a418e9059937a4c"
dependencies = [
"lazy-regex-proc_macros",
"once_cell",
"regex",
]
[[package]]
name = "lazy-regex-proc_macros"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44bcd58e6c97a7fcbaffcdc95728b393b8d98933bfadad49ed4097845b57ef0b"
dependencies = [
"proc-macro2",
"quote",
"regex",
"syn",
]
[[package]]
name = "memchr"
version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]]
name = "num"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af"
dependencies = [
"num-bigint",
"num-complex",
"num-integer",
"num-iter",
"num-rational",
"num-traits",
]
[[package]]
name = "num-bigint"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-complex"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214"
dependencies = [
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-iter"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
dependencies = [
"autocfg",
"num-bigint",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "proc-macro2"
version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "syn"
version = "2.0.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"

10
20/Cargo.toml Normal file
View File

@ -0,0 +1,10 @@
[package]
name = "day20"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
lazy-regex = "3.1.0"
num = "0.4.1"

58
20/input Normal file
View File

@ -0,0 +1,58 @@
%hm -> cr
%qc -> nd
&dh -> rm
%ph -> zz
%ps -> kc, dt
%qb -> dt
%jl -> vt, tb
%fh -> dm, gr
broadcaster -> np, mg, vd, xr
%zz -> sq
&rm -> rx
%nd -> br
%nx -> vr, vt
%qf -> dt, dv
%np -> xm, ph
%dm -> nf, gr
%sq -> kj
%bv -> fp, xm
%br -> kt
%mg -> dz, gr
&dt -> vd, dv, dh, hm, ks, hd, kq
%ks -> qf
&qd -> rm
%xr -> vt, rn
%vr -> tg, vt
%lc -> xm
%tq -> gr, fh
%cr -> kq, dt
%vd -> dt, ks
%tb -> nx
%dz -> gr, fd
&gr -> dp, mg, fd, qn
%nf -> gr
%dv -> hm
%qj -> lc, xm
%kc -> dt, gf
%gf -> dt, qb
%vh -> xm, sv
%sr -> vt
%fp -> qg, xm
%kj -> vh
%pc -> tq, gr
%kq -> hd
%xd -> xg, gr
%tg -> sr, vt
&bb -> rm
%rn -> vt, qc
%hd -> ps
%qg -> xm, qj
&dp -> rm
%qn -> pc
%kt -> jl
%sv -> bv
&vt -> bb, nd, qc, xr, br, tb, kt
%fd -> mx
&xm -> zz, sv, sq, ph, kj, np, qd
%xg -> gr, qn
%mx -> gr, xd

362
20/src/main.rs Normal file
View File

@ -0,0 +1,362 @@
use std::collections::{BinaryHeap, HashMap, VecDeque};
use std::fmt::Display;
use std::fs::File;
use std::hash::{Hash, Hasher};
use std::io::{BufRead, BufReader, Lines};
use std::time::Instant;
use lazy_regex::lazy_regex;
use num::integer::lcm;
// BOILERPLATE
type InputIter = Lines<BufReader<File>>;
fn get_input() -> InputIter {
let f = File::open("input").unwrap();
let br = BufReader::new(f);
br.lines()
}
fn main() {
let start = Instant::now();
let ans1 = problem1(get_input());
let duration = start.elapsed();
println!("Problem 1 solution: {} [{}s]", ans1, duration.as_secs_f64());
let start = Instant::now();
let ans2 = problem2(get_input());
let duration = start.elapsed();
println!("Problem 2 solution: {} [{}s]", ans2, duration.as_secs_f64());
}
// PARSE
const MODULE_PATTERN: lazy_regex::Lazy<lazy_regex::Regex> = lazy_regex!("^([%&]?)([a-z]+) -> ([a-z, ]+)$");
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
enum PulseType {
Low,
High,
}
impl From<bool> for PulseType {
fn from(value: bool) -> Self {
match value {
true => PulseType::High,
false => PulseType::Low,
}
}
}
impl From<PulseType> for bool {
fn from(value: PulseType) -> Self {
match value {
PulseType::High => true,
PulseType::Low => false,
}
}
}
impl Display for PulseType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PulseType::Low => f.write_str("low"),
PulseType::High => f.write_str("high"),
}
}
}
#[derive(Debug, Clone, Copy)]
enum ModuleKind {
FlipFlop,
Conjunction,
Broadcast,
Button,
}
#[derive(Debug, Clone)]
struct Module {
name: String,
kind: Option<ModuleKind>,
inputs: Vec<String>,
outputs: Vec<String>,
on: bool,
last_input_states: Vec<PulseType>,
}
impl Hash for Module {
fn hash<H: Hasher>(&self, state: &mut H) {
// the only states that change are 'on' and 'last_input_states', the rest are constant once created
self.on.hash(state);
self.last_input_states.hash(state);
}
}
impl Module {
fn new(name: String) -> Self {
Self {
name,
kind: None,
inputs: Vec::new(),
outputs: Vec::new(),
on: false,
last_input_states: Vec::new(),
}
}
fn source_idx(&self, name: &str) -> usize {
self.inputs.iter().position(|in_name| in_name == name).unwrap()
}
fn with_kind(name: String, kind: ModuleKind) -> Self {
let mut m = Self::new(name);
m.kind = Some(kind);
m
}
fn and_destinations(mut self, destinations: &str) -> Self {
for dest in destinations.split(", ") {
self.outputs.push(dest.into());
}
self
}
fn and_source(mut self, source: String) -> Self {
self.add_source(source);
self
}
fn add_source(&mut self, source: String) {
self.inputs.push(source);
self.last_input_states.push(PulseType::Low);
}
fn reset(&mut self) {
// relevant state is on and last_input_states
self.on = false;
for input in &mut self.last_input_states {
*input = PulseType::Low;
}
}
fn state_space(&self) -> u128 {
match self.kind {
Some(ModuleKind::Broadcast) | Some(ModuleKind::Button) => 1,
Some(ModuleKind::FlipFlop) => 2,
Some(ModuleKind::Conjunction) => 1 << self.last_input_states.len(), // 2^n
None => 1
}
}
}
impl From<&str> for Module {
fn from(s: &str) -> Self {
let Some(re_result) = MODULE_PATTERN.captures(s) else {
panic!("unparseable module: {}", s);
};
let (_, [kind, name, destinations]) = re_result.extract();
if name == "broadcaster" {
return Module::with_kind(name.into(), ModuleKind::Broadcast).and_destinations(destinations);
};
match kind {
"%" => Module::with_kind(name.into(), ModuleKind::FlipFlop).and_destinations(destinations),
"&" => Module::with_kind(name.into(), ModuleKind::Conjunction).and_destinations(destinations),
_ => panic!("invalid module kind {}", kind),
}
}
}
#[derive(Debug)]
struct Job {
signal: PulseType,
targets: Vec<String>,
from: String,
priority: usize,
}
impl PartialEq for Job {
fn eq(&self, other: &Self) -> bool {
self.priority == other.priority
}
}
impl Eq for Job {}
impl PartialOrd for Job {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
other.priority.partial_cmp(&self.priority)
}
}
impl Ord for Job {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.partial_cmp(other).unwrap()
}
}
#[derive(Debug)]
struct Machine {
modules: HashMap<String, Module>,
work_queue: BinaryHeap<Job>,
count_low: u64,
count_high: u64,
}
impl<T: BufRead> From<Lines<T>> for Machine {
fn from(mut lines: Lines<T>) -> Self {
let mut modules = HashMap::from([(
"button".into(),
Module::with_kind("button".into(), ModuleKind::Button).and_destinations("broadcaster"),
)]);
while let Some(Ok(line)) = lines.next() {
let mut module = Module::from(line.as_str());
for output in &module.outputs {
if let Some(output_mod) = modules.get_mut(output) {
// already exists, push to input list
output_mod.add_source(module.name.to_owned());
} else {
// forward declaration of 'unknown' modules
modules.insert(
output.clone(),
Module::new(output.clone()).and_source(module.name.clone()),
);
}
}
if let Some(existing_mod) = modules.get(&module.name) {
module.inputs = existing_mod.inputs.clone(); // might have been pre-prepared, the rest we take ours
module.last_input_states = existing_mod.last_input_states.clone();
}
modules.insert(module.name.clone(), module);
}
Machine {
modules,
work_queue: BinaryHeap::new(),
count_low: 0,
count_high: 0,
}
}
}
impl Machine {
fn press_button(&mut self) {
self.send_pulse(PulseType::Low, "broadcaster", "button", 1);
}
fn send_pulse(&mut self, signal: PulseType, target: &str, from: &str, priority: usize) {
// count pulse when it is received
match signal {
PulseType::Low => self.count_low += 1,
PulseType::High => self.count_high += 1,
};
match self.modules[target].kind {
Some(ModuleKind::Button) | Some(ModuleKind::Broadcast) => {
self.send_all_outputs(PulseType::Low, target, priority)
}
Some(ModuleKind::FlipFlop) => match signal {
PulseType::High => (),
PulseType::Low => {
let new_state = !self.modules.get_mut(target).unwrap().on;
self.modules.get_mut(target).unwrap().on = new_state;
self.send_all_outputs(new_state.into(), target, priority)
}
},
Some(ModuleKind::Conjunction) => {
let target_m = self.modules.get_mut(target).unwrap();
let source_idx = target_m.source_idx(from);
target_m.last_input_states[source_idx] = signal;
if target_m.last_input_states.iter().all(|state| *state == PulseType::High) {
self.send_all_outputs(PulseType::Low, target, priority)
} else {
self.send_all_outputs(PulseType::High, target, priority)
}
}
None => (),
}
}
fn send_all_outputs(&mut self, signal: PulseType, from: &str, priority: usize) {
self.work_queue.push(Job {
signal,
targets: self.modules[from].outputs.iter().map(|op| op.to_owned()).collect(),
from: from.to_owned(),
priority: priority + 1,
})
}
fn run(&mut self) {
while let Some(job) = self.work_queue.pop() {
for target in job.targets {
self.send_pulse(job.signal, &target, &job.from, job.priority);
}
}
}
// the number of cycles it took until the target sends a low pulse, or None if the target node sent no low pulses but the job finished
fn time_to_state(&mut self, goal:&str, state: PulseType) -> Option<u64> {
while let Some(job) = self.work_queue.pop() {
if job.from == goal && job.signal == state {
return Some(self.count_high + self.count_low);
}
for target in job.targets {
self.send_pulse(job.signal, &target, &job.from, job.priority);
}
}
None
}
fn reset(&mut self) {
for (_name, m) in &mut self.modules {
m.reset();
}
self.count_low = 0;
self.count_high = 0;
}
fn state_space(&self) -> u128 {
self.modules.values().map(|m| m.state_space()).product()
}
}
// PROBLEM 1 solution
const PROBLEM1_ITERATIONS: usize = 1000;
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
let mut machine = Machine::from(input);
for _ in 0..PROBLEM1_ITERATIONS {
machine.press_button();
machine.run();
}
machine.count_low * machine.count_high
}
// PROBLEM 2 solution
fn problem2<T: BufRead>(input: Lines<T>) -> u128 {
let mut machine = Machine::from(input);
println!("STATES: {}", machine.state_space());
// Find the rx module and look at its parent(s)
let rx = &machine.modules["rx"];
// my input has a single conjunction as parent, todo implement other possibilities
let con = &machine.modules[&rx.inputs[0]];
// for each input, find how long it takes for it to be High, so the conjunction sends a low to rx
let mut cycles: Vec<_> = Vec::new();
for input in con.inputs.clone() {
print!("searching distance to {}...", input);
machine.reset();
let mut button_count = 0;
loop {
machine.press_button();
button_count += 1;
if let Some(distance) = machine.time_to_state(&input, PulseType::High) {
println!("got {} pulses, {} button presses", distance, button_count);
cycles.push(button_count as u128);
break;
}
}
}
cycles.iter().fold(1, |accum, cycle| lcm(accum, *cycle))
}
#[cfg(test)]
mod tests {
use crate::*;
use std::io::Cursor;
const EXAMPLE: &str = &"broadcaster -> a
%a -> inv, con
&inv -> b
%b -> con
&con -> output";
#[test]
fn problem1_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem1(c.lines()), 11687500);
}
}

465
21/Cargo.lock generated Normal file
View File

@ -0,0 +1,465 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "approx"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6"
dependencies = [
"num-traits",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bytemuck"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "console"
version = "0.15.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8"
dependencies = [
"encode_unicode",
"lazy_static",
"libc",
"unicode-width",
"windows-sys",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751"
dependencies = [
"cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d2fe95351b870527a5d09bf563ed3c97c0cffb87cf1c78a591bf48bb218d9aa"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"memoffset",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06d96137f14f244c37f989d9fff8f95e6c18b918e71f36638f8c49112e4c78f"
dependencies = [
"cfg-if",
]
[[package]]
name = "day21"
version = "0.1.0"
dependencies = [
"indicatif",
"polyfit-rs",
"primes",
"rayon",
"test-case",
]
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "encode_unicode"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
[[package]]
name = "indicatif"
version = "0.17.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25"
dependencies = [
"console",
"instant",
"number_prefix",
"portable-atomic",
"unicode-width",
]
[[package]]
name = "instant"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
[[package]]
name = "matrixmultiply"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2"
dependencies = [
"autocfg",
"rawpointer",
]
[[package]]
name = "memoffset"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
dependencies = [
"autocfg",
]
[[package]]
name = "nalgebra"
version = "0.31.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20bd243ab3dbb395b39ee730402d2e5405e448c75133ec49cc977762c4cba3d1"
dependencies = [
"approx",
"matrixmultiply",
"nalgebra-macros",
"num-complex",
"num-rational",
"num-traits",
"simba",
"typenum",
]
[[package]]
name = "nalgebra-macros"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01fcc0b8149b4632adc89ac3b7b31a12fb6099a0317a4eb2ebff574ef7de7218"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "num-complex"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214"
dependencies = [
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
dependencies = [
"autocfg",
]
[[package]]
name = "number_prefix"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
[[package]]
name = "paste"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
[[package]]
name = "polyfit-rs"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab98d9704c7300e37472a6790a447eaf501d664a1889737faa53c26790d2b697"
dependencies = [
"nalgebra",
]
[[package]]
name = "portable-atomic"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
[[package]]
name = "primes"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68a61082d8bceecd71a3870e9162002bb75f7ba9c7aa8b76227e887782fef9c8"
[[package]]
name = "proc-macro2"
version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rawpointer"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
[[package]]
name = "rayon"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
]
[[package]]
name = "safe_arch"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f398075ce1e6a179b46f51bd88d0598b92b00d3551f1a2d4ac49e771b56ac354"
dependencies = [
"bytemuck",
]
[[package]]
name = "simba"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f3fd720c48c53cace224ae62bef1bbff363a70c68c4802a78b5cc6159618176"
dependencies = [
"approx",
"num-complex",
"num-traits",
"paste",
"wide",
]
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b7d0a2c048d661a1a59fcd7355baa232f7ed34e0ee4df2eef3c1c1c0d3852d8"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "test-case"
version = "3.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb2550dd13afcd286853192af8601920d959b14c401fcece38071d53bf0768a8"
dependencies = [
"test-case-macros",
]
[[package]]
name = "test-case-core"
version = "3.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adcb7fd841cd518e279be3d5a3eb0636409487998a4aff22f3de87b81e88384f"
dependencies = [
"cfg-if",
"proc-macro2",
"quote",
"syn 2.0.42",
]
[[package]]
name = "test-case-macros"
version = "3.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.42",
"test-case-core",
]
[[package]]
name = "typenum"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-width"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
[[package]]
name = "wide"
version = "0.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c68938b57b33da363195412cfc5fc37c9ed49aa9cfe2156fde64b8d2c9498242"
dependencies = [
"bytemuck",
"safe_arch",
]
[[package]]
name = "windows-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"

13
21/Cargo.toml Normal file
View File

@ -0,0 +1,13 @@
[package]
name = "day21"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
indicatif = "0.17.7"
polyfit-rs = "0.2.1"
primes = "0.3.0"
rayon = "1.8.0"
test-case = "3.3.1"

131
21/input Normal file
View File

@ -0,0 +1,131 @@
...................................................................................................................................
..............#...#........#...............#.#.......#....##...........#.#..#......#.....#.........#..##...........................
.#..##..##..#......#.#.#...##......###...##......#.#..#...................#.....#..........#..#................#...#..........#....
.....................#..#.......#.....##.........#.......................................#........#....#.#..........#.#......#.#...
......##...#.....#.......#...#..###..........#..........#......................#........#.............#.....#........#....#........
.................##.................#...#.........#................................#........#...........#............#........#....
..............#.#....#........##.....##..........#.............##.............#...#......#..##..........#.........#.....#...#..#...
..#....#.......#...##..#......#.........#.#..#....#.#..............#........#...#...#...............#.#.....#..#..##......#...#....
...#......##...##..#...#..........#...#..........#..............#...#..........#..#..#..#........#.#.#.#.#...........###...........
.#.#.#...#.#...##............................................#......................#...#......#......##.#.........................
.##.#.........#...#..................##...........#............#..................###...#...........#.................#....#.......
.....................#...#.#................#................#.#........................#...#...##....##....#.#...#.........#...#..
...#.......#...#.........................#.#..................#........##..........................#......##....##............#....
......................#.....................#...............#.......#.#...................#...............#...#........#.##......#.
............................#..........................#........#....#.##..#.......#.##..#...#.#..#...............#.#..#....#......
.......................................................#.#..#.#....#......................#............#....#..#.......#.........#.
.....##........#...............#.......#...............#.#..........##..#...................#................#..#...#.#....#...###.
....#.....................##................#.......#.......#...#.#...#...............#................#...............##..........
....#........#..#.......#.#.#........................#.#...........#.......#..#.........#....#.......#........#.#.#................
......###.....#..#...........#.....#..#..#........#......#.#........###.....##............#..#...........#.........................
.....#...........#...................#..#..........#..#...###.#.........#.##...............#..##..........#.........#..............
.#.........#....#............#.##..........................##.....#........#....#.............#.......#...#..............##.#......
.#..#...#.......#...#.......##.......#.#.............................#...#...#.#...........#...#.................#.........#.#.#...
...#....................#.....##..............#.................#.................#............#......#....#.......#.........#.....
.......#...#.............#.##...#.#...................#.....#..#........#................................#.............#..#...##...
...#.###..............#....#........................#......#...#.........#......#.#....................#........#...#...#.......#..
............#.#...#...........................#....###.###...#........#..#........#...................##.....###......#........#...
.....#...#................#.##..##..................#....#.........#............##...................##................#.....#.....
.#....#..............##...#..................#......#..........##.#....#......##.#...............#....##..............#....#.......
....#.#.##...........##.....#...........#.........#...#........#..#.......#......#................#.#...#....#...#.................
.......#...#...#...#..#.......#..........#..................#..#........#..#.#.....#.#.....................##......................
.....##.........#.......##...#.............#.##.#.#...#.##.#...#......#...#....#.......#..#.........#...........#.##.###.#.....#...
...................#.#.......................#.....#.#......#.....##...#.#....#..#..........#...........#..##....#...#.......#.#...
.....#..#..#.........#...#.#.........#...#.#...##...#......##........#...............#.......#................#.....#...#.#........
........#.....###.....................#.........#.........#.........#....##............#..##.............#.........#..#.......#....
.#.........#....##..##.................#...#................................#...#.....#.........#.......#............#....#......#.
..#.........#.......#..................#..#.#...#........#.#.......#...##.#........#..........#.#..........#..#.....#.........#....
..#...............#...................##.#.......#...#..........#....................##.........#.................#..#......##..#..
.#........#.......#............##...........##........#.......................#................#....................#...###......#.
........###..........#..............#.............#..#.#.#............##...............#........#...#........#...................#.
...#............................#...##.##........#......#..#...................#.#.....#...#.##...............#........#...#.......
..#...##....................#.....##......#.......#..........#......#.........#...#..............#..............#..................
.....#..#....................#....###.....#..#.............#...#....#................#.....#.....................#........#........
.....#.#...#.....#..............##............#....##.#....#.#........##...........#....#.........................#....#......##...
....#..#.....##...........#.#.#.#............#....#.....#.........#........................##.......#.#..............##...#........
...............#..............#................................#....#........#.......#.......##......#.#............#......#....#..
.#......#....##........#.............#..#.#.............#...#..#.........#.........#................#..................#...........
.........#..............#....#......#.....##......#............#....#.........#....#...................#....#.........#......#..#..
..............................#....#.#........#.#.....#..#.................#....#...#..........#............##.......#.#.#....#....
.#..................#....#..#.......#.##..............#...#.......#................#...............................................
.#..##.........................#.....###......#....#......#.....#............#.#......##......##......##....#..........##.....#....
..........................##.#.....#..........................................#.#..#....#..#..................#...........#........
..##.....#.......#....#.........#..........#....................#.........#........###........................#..........#.........
.#.#...............#..........#.......#.....#....##.#..#.##...............#...#...#.#......#.......#.......#.#.............#....##.
..................#...............##.........##..#........#..#..........#.....................#............................#.......
.....#................#...##.......##.........#.....#.#....#.#........#...#..#....#..............#..#....#....#....#...............
..##.#........#...............#.#..#...................##...#......#.#...#..#.....##.................##...#.#...................#..
...#...............#......#.....#.......#......#.........#...#......#...#..#....#..#......#.......#.......#...#.##.#...........##..
..............##..........##..........#..##...............#...........##..#..#..#.#.......#...#.............#......................
..........#.#........................#...#.#..#.#...##..#.#.#.#.......#....#.#.#..............#...#...#........###..##...........#.
......................#...#.#..##.....#....#.....###.#..#.....#..........#........#...........##...#..#...........##..#............
..................................####.#..............#.................#........#.#............##......#.#..........#.............
..................#..........#.............................#...........#..#..#......#................#.......##...##..#............
....................#...#....#.#.#.##......#................#.................#...........#....#..........#....#...##.###..........
.................#.......#...#............###.###..#.......#......#.......#..##......#.#.....#.....................#...............
.................................................................S.................................................................
.......##...........#.........###.......#....###.#.#....#.#....#....#........#.......#...#...#.#.........#...................#.....
.......#...............#............#...#..#.......##...#...#.#............#.............#......##..........#..............#.......
.......##.........#.....................##...#...#.................#...#.......#..####......#.....#..............#..##.............
........#..#.............#.#..##..............#...#.............#.....#......#...#.#.......#...............##...##.................
.........#..#...#..#.#..#..#...................##......#.....#..#...##..............#......#..........#.##...#.#.##.#..............
..............#..........#....................................#.........#.......#..#............##................#................
...........#...#.....#.........#....#..###....#...#.......#....#...............##.....#.......#.#.....#.#....#.#..#................
..#...................#........###..............#.#................#...###...##.............#.###......#...#...#.....#..........#..
.#.#............#.....#....###......................................#..........................#..................#................
................#..#...#................#.................##.................##..........#.......#...#.............#...............
.....##...........#....#.......#......#.#.....#.....#................#.....#.........#..#.................#........#........#......
.#.#.............#..#.#..............#.....#..#.#.....#..#......#.........#.....#.#......#..#.......#.#......##............#..#....
...................#............#....#...........#........#...................#.....##..............#........###...............#...
......###..........###.#.......#.................................................#..#.........#.#...#.........#.#..........#.#.....
..#.................#..#..#...#............#...#...#..##.#....##..#..................##.....#......#.........................#...#.
......................#.#.....#.#...#.......##...........#...#....#.............#.......#.............#.#.#.#......................
......#..#.#..........#....#.........#......#................#.........##.#....#.......#........#.#....................#......#....
..#....#....#...............#..........#...#.#......#...................#.........#..#.#......#..#.........................##....#.
..............#..........##......#.##...#...#.#................#...#..#.......#.#.....#........#...###..##.........#..##.........#.
........##...........................##.......#.#..................#........#.......#.#........#.........................##....#...
..#.#...........#.......................#.#.....#....#.....#...............####...#..............#...#....................#.#......
.......#...#....#.#.......#####...##.....#.#.......#....#.#........#................................#.##..........##....#..#.......
.........#.....#................##.#..................#.....................#...#................#.###.#.......###....#............
............#.#....#........#....#..................#...##.............#............#.......#.....#..#............##...............
....#.....#..........#..............#...#...#........##.....#.#....#....#........##.............................#.#.......#........
..#..#.#.......#.................#................................#......##.#..#..............#.#................#...#.#...........
.....#.....#........................#..#................#......#.......#.........###.#.#.........#.........#.....#.......#.........
.......#.......................................#.......#...............#.#..###...#.#.##.......#...........#.#.....#.##........#...
......##.........#.................................#.........#........##.#................#.#...#.............#...#..#........#....
......#.....#.#.......#...........#..#...........#.........#..#.#.......#.........##.....#....#.#...............#...#...#....#...#.
..#...#.#....#....#..............................#..#...#.......#.#.#..##..................#...............#......#..###..#.#......
.....#...#..#......#.....##...............#..........#..#.#####.......#...........#...................#.....#...#.........#..#.#...
.................#.......#..................#........#.......##...##.#....#.#.............#.............#....#....#................
...........####..#.....#....#.............#.......#...#..#.....#.............#...#....#.............#.#....#.#.............#.......
...#..#..#...#...#.#.....#.......................#....#..###.###.....#.........#....................#....#..#............#.....#...
.#.##....##...#......#.#.....#......................#.##..................#........#..#................#.#.......#.............#...
....##........#.......#..#..#............#..#..#..#...#......###..#..#..#...##...............................#.#........#......#.#.
..#..#..........#..........#..#.#.............#.....#...........#.##.......#............................#...........#...#..........
..#.#..#...#....#.....#.....#...#................#.....#.....#....#..#...#..#....#....#.....................#....#.................
.......#........#...#.............#.................#...#.#.....#.#....#..........#......................#..#............#.........
....#........#........#..............#.........................#...................#..........#.........#..#.........#.............
................#........#.........#............................#.#.............#..##.......#......#..#.................#......#.#.
..#.....#.........#...#..#.#.........................#...#...#...........#.....#...........#........#.............#.....#........#.
....#...#....#......#....#...#..#.................#...##.....#.#............................##...#...#..##.#.....................#.
...#..##...............#............................#...........#......#.#.....#............#........#.##................#.........
................#..#.............#..#..............#.#....#...#.#...#...#.#.##.............................#..#...........#........
.......#.......#....###............#.#.#.....................#.#..#....#.................##.....#.#..........##..#......#..........
.......#..........##.#.......#.##...#...............##.....#..........#................#............#...#........#........#........
...###.#.............................#.....#.#............##..#...#.....###..........#...............................#.....#....#..
....###.#...#.......#.##........#......#..##.#..........#.#...#.#....#.#..............##....#..#...#...........#...#.#..#..........
.....#....#.#..##.....#..........#..#...##..............#.......#.......#.............#..#.....#.......#.....#...##...###.......#..
........##...#...##..#.#.....#..........................#.#.#..#..#...................#....#....#..#....#..........#....##.........
...........##.....#...#........##..#........#....#..........#..#...............................................#......#.#.......#..
...#..##.........##....#...........##...#.......##.........#........#...........#........#.#.##...........#....#.....###...#.......
...#...#........................#..#...#..#........................................#......#..#...#.#....#...........#..............
...#..#.......#..#......##...................#...............#.....#.#.............#.....#.........#.#..##.....#..........#..#.....
....###....#..#.....#.#........#.##.......##...#...............#...............###.###.#...#............##.....#.................#.
....#...#.........#.##............#.....#........#............................................##.#......#..##.........#....###..#..
............#...##..#..........#........#.......#.#..........................#..#.#.......#......#.......................##.#..#...
....#......#....#....#..#.....#........#......#....#..#.#..............................#..#....#............#....#.................
..#.............#...#......#...#.....................#.##...............................##...#.......#........................#.##.
....#....##........#..............#....#.......#.....................................#........#........#...##..........#...........
....#.#.##..#..#....................#.................#.##.............#....#....#.............###...#...#..##....#.....##.........
.............#.......#...............#........#.#.....#..#.#................#.....#..##.....#....#...............#....#......#...#.
...................................................................................................................................

230
21/src/main.rs Normal file
View File

@ -0,0 +1,230 @@
use core::panic;
use indicatif::{ProgressBar, ProgressStyle};
use rayon::prelude::*;
use std::collections::HashSet;
use std::fs::File;
use std::io::{BufRead, BufReader, Lines};
use std::time::Instant;
// BOILERPLATE
type InputIter = Lines<BufReader<File>>;
fn get_input() -> InputIter {
let f = File::open("input").unwrap();
let br = BufReader::new(f);
br.lines()
}
fn main() {
let start = Instant::now();
let ans1 = problem1(get_input());
let duration = start.elapsed();
println!("Problem 1 solution: {} [{}s]", ans1, duration.as_secs_f64());
let start = Instant::now();
let ans2 = problem2(get_input());
let duration = start.elapsed();
println!("Problem 2 solution: {} [{}s]", ans2, duration.as_secs_f64());
}
// PARSE
type Position = (isize, isize);
type WrappedPosition = (usize, usize);
type Offset = (isize, isize);
#[derive(Debug, Clone)]
struct MapTile {
c: char,
}
impl MapTile {
fn new(c: char) -> Self {
Self { c }
}
}
struct GardenMap {
map: Vec<Vec<MapTile>>,
start: Position,
}
impl<T: BufRead> From<Lines<T>> for GardenMap {
fn from(lines: Lines<T>) -> Self {
let map = lines
.map(|line| line.unwrap().chars().map(|c| MapTile::new(c)).collect())
.collect();
let mut new = Self { map, start: (0, 0) };
new.find_start();
new
}
}
const ADJACENCIES: [Offset; 4] = [(-1, 0), (1, 0), (0, -1), (0, 1)];
impl GardenMap {
fn wrap_pos(&self, pos: &Position) -> WrappedPosition {
let (width, height) = (self.width() as isize, self.height() as isize);
(
if pos.0 < 0 {
(pos.0 + (-pos.0 / width + 1) * width) as usize % self.width()
} else {
pos.0 as usize % self.width()
},
if pos.1 < 0 {
(pos.1 + (-pos.1 / height + 1) * height) as usize % self.height()
} else {
pos.1 as usize % self.height()
},
)
}
fn width(&self) -> usize {
self.map[0].len()
}
fn height(&self) -> usize {
self.map.len()
}
fn at(&self, pos: &Position) -> &MapTile {
let pos = self.wrap_pos(pos);
&self.map[pos.1][pos.0]
}
// return the valid 'moves' from pos
fn adjacent_to(&self, pos: &Position) -> Vec<Position> {
ADJACENCIES
.iter()
.filter_map(|ofs| self.offset_pos(pos, ofs))
.filter(|pos| self.at(pos).c != '#')
.collect()
}
fn offset_pos(&self, pos: &Position, ofs: &Offset) -> Option<Position> {
let new_pos = (pos.0 as isize + ofs.0, pos.1 as isize + ofs.1);
return Some((new_pos.0, new_pos.1));
}
fn find_start(&mut self) {
for (y, row) in self.map.iter().enumerate() {
for (x, tile) in row.iter().enumerate() {
if tile.c == 'S' {
self.start = (x as isize, y as isize);
return;
}
}
}
panic!("didn't find the start square!");
}
fn reachable_after(&self, from: &Position, n: usize) -> u64 {
let bar = ProgressBar::new(n as u64).with_style(
ProgressStyle::with_template(
"[{elapsed_precise}/{eta_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {per_sec}",
)
.unwrap(),
);
let mut visited_after: Vec<HashSet<Position>> = Vec::new();
visited_after.push(HashSet::from([*from]));
for i in 1..n+1 {
visited_after.push(
visited_after[i - 1]
.iter()
.flat_map(|last| self.adjacent_to(last))
.collect(),
);
bar.inc(1);
// if primes::is_prime(i as u64) {
// println!("count after {} steps: {}", i, visited_after[i].len());
// }
}
visited_after[n].len() as u64
}
fn reachable_count_after(&self, from: &Position, n: usize) -> u64 {
let dim = self.width() as f64;
let target_mod = (n % self.width()) as f64;
let x_values:Vec<f64> = vec![target_mod, target_mod + dim, target_mod + 2.*dim];
let y_values:Vec<f64> = x_values.iter().map(|n| self.reachable_after(from, *n as usize) as f64).collect();
let coeffs = polyfit_rs::polyfit_rs::polyfit(
&x_values,
&y_values,
2,
).unwrap();
println!("values: x: {:?} y: {:?}", x_values, y_values);
println!("coefficients: {:?}", coeffs);
let f_n= n as f64;
let result = coeffs[0] + coeffs[1] * f_n + coeffs[2] * f_n.powf(2.0);
result.round() as u64
}
fn draw_with_bounds(&self, from: &Position, to: &Position) {
for row in from.1..to.1 + 1 {
for col in from.0..to.0 + 1 {
print!("{}", self.at(&(col, row)).c);
}
println!();
}
}
}
// fn print_visited(map: &GardenMap, visited: &Vec<Vec<bool>>) {
// for (y, row) in visited.iter().enumerate() {
// for (x, cell) in row.iter().enumerate() {
// print!("{}", if *cell { 'O' } else { map.at(&(x, y)).c });
// }
// println!();
// }
// }
// PROBLEM 1 solution
fn problem1_impl<T: BufRead>(input: Lines<T>, n: usize) -> u64 {
let map = GardenMap::from(input);
// println!("map: {:?} start: {:?}", map.map, &map.start);
// map.draw_with_bounds(
// &(-(map.width() as isize), -(map.height() as isize)),
// &(map.width() as isize * 2 + 1, map.height() as isize * 2 + 1),
// );
map.reachable_count_after(&map.start, n)
}
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
problem1_impl(input, 64)
}
// PROBLEM 2 solution
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
problem1_impl(input, 26501365)
}
#[cfg(test)]
mod tests {
use crate::*;
use std::io::Cursor;
use test_case::test_case;
const EXAMPLE: &str = &"...........
.....###.#.
.###.##..#.
..#.#...#..
....#.#....
.##..S####.
.##..#...#.
.......##..
.##.#.####.
.##..##.##.
...........";
#[test]
fn problem1_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem1_impl(c.lines(), 6), 16);
}
#[test_case(6, 16)]
#[test_case(10, 50)]
#[test_case(50, 1594)]
#[test_case(100, 6536)]
#[test_case(500, 167004)]
fn problem2_example(n: usize, expect: u64) {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem1_impl(c.lines(), n), expect);
}
}

128
22/Cargo.lock generated Normal file
View File

@ -0,0 +1,128 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "day22"
version = "0.1.0"
dependencies = [
"itertools",
"ndarray",
"petgraph",
]
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "fixedbitset"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "hashbrown"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]]
name = "indexmap"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "itertools"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
dependencies = [
"either",
]
[[package]]
name = "matrixmultiply"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2"
dependencies = [
"autocfg",
"rawpointer",
]
[[package]]
name = "ndarray"
version = "0.15.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adb12d4e967ec485a5f71c6311fe28158e9d6f4bc4a447b474184d0f91a8fa32"
dependencies = [
"matrixmultiply",
"num-complex",
"num-integer",
"num-traits",
"rawpointer",
]
[[package]]
name = "num-complex"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214"
dependencies = [
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
dependencies = [
"autocfg",
]
[[package]]
name = "petgraph"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9"
dependencies = [
"fixedbitset",
"indexmap",
]
[[package]]
name = "rawpointer"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"

11
22/Cargo.toml Normal file
View File

@ -0,0 +1,11 @@
[package]
name = "day22"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
itertools = "0.12.0"
ndarray = "0.15.6"
petgraph = "0.6.4"

385
22/src/main.rs Normal file
View File

@ -0,0 +1,385 @@
use itertools::Itertools;
use ndarray::prelude::*;
use petgraph::prelude::*;
use petgraph::visit::{IntoNodeReferences, Walker};
use std::collections::BinaryHeap;
use std::fmt::{Display, Write};
use std::fs::File;
use std::io::{BufRead, BufReader, Lines};
use std::str::FromStr;
use std::time::Instant;
// BOILERPLATE
type InputIter = Lines<BufReader<File>>;
fn get_input() -> InputIter {
let f = File::open("input").unwrap();
let br = BufReader::new(f);
br.lines()
}
fn main() {
let start = Instant::now();
let ans1 = problem1(get_input());
let duration = start.elapsed();
println!("Problem 1 solution: {} [{}s]", ans1, duration.as_secs_f64());
let start = Instant::now();
let ans2 = problem2(get_input());
let duration = start.elapsed();
println!("Problem 2 solution: {} [{}s]", ans2, duration.as_secs_f64());
}
// PARSE
#[derive(Hash, PartialEq, Eq, Clone, Debug)]
struct Coord {
x: usize,
y: usize,
z: usize,
}
impl FromStr for Coord {
type Err = Box<dyn std::error::Error>;
fn from_str(value: &str) -> Result<Self, Self::Err> {
let (x, y, z) = value.split(',').next_tuple().unwrap();
Ok(Self {
x: x.parse()?,
y: y.parse()?,
z: z.parse()?,
})
}
}
#[derive(Clone, Hash, PartialEq, Eq)]
struct BrickBlock {
c1: Coord,
c2: Coord,
}
impl std::fmt::Debug for BrickBlock {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(
f,
"[{},{},{} - {},{},{}]",
self.c1.x, self.c1.y, self.c1.z, self.c2.x, self.c2.y, self.c2.z
)
}
}
impl BrickBlock {
fn bottom_z_plane(&self) -> usize {
std::cmp::min(self.c1.z, self.c2.z)
}
fn top_z_plane(&self) -> usize {
std::cmp::max(self.c1.z, self.c2.z)
}
fn bottom_x_plane(&self) -> usize {
std::cmp::min(self.c1.x, self.c2.x)
}
fn top_x_plane(&self) -> usize {
std::cmp::max(self.c1.x, self.c2.x)
}
fn bottom_y_plane(&self) -> usize {
std::cmp::min(self.c1.y, self.c2.y)
}
fn top_y_plane(&self) -> usize {
std::cmp::max(self.c1.y, self.c2.y)
}
}
impl From<&str> for BrickBlock {
fn from(value: &str) -> Self {
let (c1, c2) = value.split_once('~').unwrap();
Self {
c1: c1.parse().unwrap(),
c2: c2.parse().unwrap(),
}
}
}
impl PartialOrd for BrickBlock {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(other.bottom_z_plane().cmp(&self.bottom_z_plane()))
}
}
// Note this is a reversed ordering
impl Ord for BrickBlock {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.bottom_z_plane().cmp(&other.bottom_z_plane())
}
}
type BlockMap = Array3<MapTile>;
type MapTile = Option<BrickBlock>;
struct BlockPile {
blocks: Vec<BrickBlock>,
block_map: Array3<MapTile>,
bounds: (usize, usize, usize),
graph: Graph<BrickBlock, (), Directed, usize>,
}
impl BlockPile {
fn remove_block(&mut self, block: &BrickBlock) {
// loop over the (inclusive) bounding coordinates and remove them all from the map
self.block_map
.slice_mut(s![
block.bottom_x_plane()..block.top_x_plane() + 1,
block.bottom_y_plane()..block.top_y_plane() + 1,
block.bottom_z_plane()..block.top_z_plane() + 1,
])
.fill(None);
self.blocks.remove(self.blocks.iter().position(|b| b == block).unwrap());
}
fn add_block(&mut self, block: &BrickBlock) {
// loop over the (inclusive) bounding coordinates and remove them all from the map
self.block_map
.slice_mut(s![
block.bottom_x_plane()..block.top_x_plane() + 1,
block.bottom_y_plane()..block.top_y_plane() + 1,
block.bottom_z_plane()..block.top_z_plane() + 1,
])
.fill(Some(block.to_owned()));
self.blocks.push(block.clone());
}
fn blocks_directly_above(&self, block: &BrickBlock) -> Vec<BrickBlock> {
// find the top plane of the block
// get the array range of all squares at z_plane + 1 within the bounds of x & y
// i think there is a built in way in ndarray to do this...
let directly_above = self.block_map.slice(s![
block.bottom_x_plane()..block.top_x_plane() + 1,
block.bottom_y_plane()..block.top_y_plane() + 1,
block.top_z_plane() + 1..std::cmp::min(block.top_z_plane() + 2, self.bounds.2)
]);
directly_above.iter().filter_map(|v| v.clone()).unique().collect()
}
fn supported_by(&self, block: &BrickBlock) -> usize {
let z_plane = std::cmp::min(block.c1.z, block.c2.z);
// get the slice of tiles below us
let directly_below = self.block_map.slice(s![
block.bottom_x_plane()..block.top_x_plane() + 1,
block.bottom_y_plane()..block.top_y_plane() + 1,
z_plane - 1..z_plane // the layer below
]);
directly_below.iter().filter_map(|v| v.clone()).unique().count()
}
fn blocks_above_will_move_if_we_are_gone(&mut self, block: &BrickBlock) -> bool {
self.blocks_directly_above(&block)
.iter()
.map(|b| self.supported_by(b))
.any(|b| b == 1) // block we support will move if we are their only support
}
fn blocks_supported_by_at_all(&self, block: &BrickBlock) -> Vec<BrickBlock> {
self.blocks_directly_above(&block).iter().map(|b| b.clone()).collect()
}
/// Find the plane of the first block directly below us
fn supporting_plane(&self, block: &BrickBlock) -> Option<usize> {
// find the bottom plane of ourselves
let z_plane = std::cmp::min(block.c1.z, block.c2.z);
// get the slice of tiles below us
let directly_below = self.block_map.slice(s![
block.bottom_x_plane()..block.top_x_plane() + 1,
block.bottom_y_plane()..block.top_y_plane() + 1,
1..z_plane // don't include our own plane
]);
// find the highest top z value of those
let block_below = directly_below
.indexed_iter()
.filter_map(|(idx, v)| if let Some(val) = v { Some((idx, val)) } else { None })
.max_by_key(|(_idx, v)| v.top_z_plane());
if let Some(block) = block_below {
Some(block.1.top_z_plane())
} else {
None
}
}
fn drop_blocks(&mut self) {
// VecDeque doesn't sort and Vec isn't convenient for pushback and popfront, so eh... use a heap.
let mut blocks_to_move = BinaryHeap::from(self.blocks.clone());
while let Some(mut block) = blocks_to_move.pop() {
let z_move = match self.supporting_plane(&block) {
Some(z) if z + 1 != block.bottom_z_plane() => block.bottom_z_plane() - (z + 1),
None if block.bottom_z_plane() != 1 => block.bottom_z_plane() - 1,
_ => {
continue;
} // we are in position already with nothing below us
};
self.remove_block(&block);
block.c1.z -= z_move;
block.c2.z -= z_move;
self.add_block(&block);
blocks_to_move.push(block);
}
}
fn build_graph(&mut self) {
self.blocks.sort_by_key(|b| b.bottom_z_plane());
for b in 0..self.blocks.len() {
self.graph.add_node(self.blocks[b].clone());
}
for b in 0..self.blocks.len() {
let block = &self.blocks[b];
let depends_on_us = self.blocks_supported_by_at_all(block);
for dependent in depends_on_us {
self.graph.add_edge(
b.into(),
self.blocks.iter().position(|b| b == &dependent).unwrap().into(),
(),
);
}
}
}
}
impl<T: BufRead> From<Lines<T>> for BlockPile {
fn from(lines: Lines<T>) -> Self {
let blocks = lines.map(|line| BrickBlock::from(line.unwrap().as_str())).collect_vec();
let mut bounds = (0, 0, 0);
for block in &blocks {
bounds.0 = std::cmp::max(block.top_x_plane() + 1, bounds.0);
bounds.1 = std::cmp::max(block.top_y_plane() + 1, bounds.1);
bounds.2 = std::cmp::max(block.top_z_plane() + 1, bounds.2);
}
let mut new = BlockPile {
blocks: Vec::new(),
bounds,
block_map: BlockMap::from_elem(bounds, None),
graph: Graph::default(),
};
for block in blocks {
new.add_block(&block);
}
new
}
}
impl Display for BlockPile {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for z in (0..self.bounds.2).rev() {
let z_plane = self.block_map.slice(s![.., .., z..z+1]);
if z_plane.iter().all(|b| b.is_none()) { continue };
for i in 0..self.bounds.0 {
// XZ view
let y = z_plane.slice(s![i..i + 1, .., ..]);
f.write_char(
match y.iter().enumerate().find_map(|(idx, val)| val.is_some().then(|| idx)) {
None => ' ',
Some(0) | Some(1) => '█',
Some(2) | Some(3) => '▓',
Some(4) | Some(5) => '▒',
Some(6) | Some(7) => '░',
_ => '░',
},
)?;
}
write!(f, " {} ", z)?;
for y in 0..self.bounds.1 {
let x = z_plane.slice(s![.., y..y + 1, ..]);
f.write_char(
match x.iter().enumerate().find_map(|(idx, val)| val.is_some().then(|| idx)) {
None => ' ',
Some(0) | Some(1) => '█',
Some(2) | Some(3) => '▓',
Some(4) | Some(5) => '▒',
Some(6) | Some(7) => '░',
_ => '░',
},
)?;
}
writeln!(f, " {}", z)?;
}
Ok(())
}
}
// PROBLEM 1 solution
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
let mut pile = BlockPile::from(input);
println!("{}", pile);
println!("dropping blocks!");
pile.drop_blocks();
println!("{}", pile);
let blocks = pile.blocks.clone();
let removable: Vec<_> = blocks
.iter()
.filter(|b| !pile.blocks_above_will_move_if_we_are_gone(*b))
.collect();
removable.len() as u64
}
// PROBLEM 2 solution
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
let mut pile = BlockPile::from(input);
pile.drop_blocks();
pile.build_graph();
println!("{}", pile);
let mut accum = 0;
let fixed_nodes = pile
.graph
.node_references()
.filter(|(_idx, b)| b.bottom_z_plane() == 1)
.map(|(idx, _b)| idx)
.collect_vec();
for node in pile.graph.node_indices() {
// remove links to node's neighbors
let dependents = pile.graph.neighbors(node).collect_vec();
let edges = pile.graph.edges(node).map(|v| v.id()).collect_vec();
for edge in edges {
pile.graph.remove_edge(edge);
}
// find how many nodes are reachable from z = 1 - these won't move
let safe_blocks = fixed_nodes
.iter()
.flat_map(|origin| {
Bfs::new(&pile.graph, *origin)
.iter(&pile.graph)
.map(|n| pile.graph[n].clone())
})
.unique()
.count();
// we are looking for the nodes that *will* disintegrate
accum += pile.graph.node_count() - safe_blocks;
// put the graph back how it was
for neigh in dependents {
pile.graph.add_edge(node, neigh, ());
}
}
accum as u64
}
#[cfg(test)]
mod tests {
use crate::*;
use std::io::Cursor;
const EXAMPLE: &str = &"1,0,1~1,2,1
0,0,2~2,0,2
0,2,3~2,2,3
0,0,4~0,2,4
2,0,5~2,2,5
0,1,6~2,1,6
1,1,8~1,1,9";
#[test]
fn problem1_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem1(c.lines()), 5);
}
#[test]
fn problem2_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem2(c.lines()), 7);
}
}

128
23/Cargo.lock generated Normal file
View File

@ -0,0 +1,128 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "day23"
version = "0.1.0"
dependencies = [
"itertools",
"ndarray",
"petgraph",
]
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "fixedbitset"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "hashbrown"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]]
name = "indexmap"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "itertools"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
dependencies = [
"either",
]
[[package]]
name = "matrixmultiply"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2"
dependencies = [
"autocfg",
"rawpointer",
]
[[package]]
name = "ndarray"
version = "0.15.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adb12d4e967ec485a5f71c6311fe28158e9d6f4bc4a447b474184d0f91a8fa32"
dependencies = [
"matrixmultiply",
"num-complex",
"num-integer",
"num-traits",
"rawpointer",
]
[[package]]
name = "num-complex"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214"
dependencies = [
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
dependencies = [
"autocfg",
]
[[package]]
name = "petgraph"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9"
dependencies = [
"fixedbitset",
"indexmap",
]
[[package]]
name = "rawpointer"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"

11
23/Cargo.toml Normal file
View File

@ -0,0 +1,11 @@
[package]
name = "day23"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
itertools = "0.12.0"
ndarray = "0.15.6"
petgraph = "0.6.4"

326
23/src/main.rs Normal file
View File

@ -0,0 +1,326 @@
use itertools::Itertools;
use ndarray::prelude::*;
use petgraph::algo::all_simple_paths;
use petgraph::prelude::*;
use std::collections::{HashMap, HashSet};
use std::fmt::{Debug, Display, Write};
use std::fs::File;
use std::io::{BufRead, BufReader, Lines};
use std::time::Instant;
// BOILERPLATE
type InputIter = Lines<BufReader<File>>;
fn get_input() -> InputIter {
let f = File::open("input").unwrap();
let br = BufReader::new(f);
br.lines()
}
fn main() {
let start = Instant::now();
let ans1 = problem1(get_input());
let duration = start.elapsed();
println!("Problem 1 solution: {} [{}s]", ans1, duration.as_secs_f64());
let start = Instant::now();
let ans2 = problem2(get_input());
let duration = start.elapsed();
println!("Problem 2 solution: {} [{}s]", ans2, duration.as_secs_f64());
}
// PARSE
#[derive(Clone)]
struct Node {
c: char,
pos: Position,
}
impl Display for Node {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({},{})", self.pos.0, self.pos.1)
}
}
impl Debug for Node {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({},{})", self.pos.0, self.pos.1)
}
}
type Position = (usize, usize);
#[derive(Clone)]
struct ForestMap {
map: Array2<char>,
indexes: HashMap<Position, NodeIndex>,
graph: StableDiGraph<Node, u64>,
start: Position,
end: Position,
}
const ADJACENCIES: [(isize, isize); 4] = [(-1, 0), (1, 0), (0, -1), (0, 1)];
fn offset_pos(map: &Array2<char>, pos: Position, ofs: (isize, isize)) -> Option<Position> {
let new_pos = (pos.0 as isize + ofs.0, pos.1 as isize + ofs.1);
if new_pos.0 >= 0
&& new_pos.0 < map.len_of(Axis(0)) as isize
&& new_pos.1 >= 0
&& new_pos.1 < map.len_of(Axis(1)) as isize
{
Some((new_pos.0 as usize, new_pos.1 as usize))
} else {
None
}
}
fn adjacent_to(map: &Array2<char>, pos: Position) -> Vec<Position> {
ADJACENCIES
.iter()
.filter_map(|ofs| offset_pos(map, pos, *ofs))
.collect()
}
impl<T: BufRead> From<Lines<T>> for ForestMap {
fn from(lines: Lines<T>) -> Self {
let rows = lines.map(|line| line.unwrap().chars().collect_vec()).collect_vec();
let map = Array::from_shape_vec([rows[0].len(), rows.len()], rows.into_iter().flatten().collect_vec())
.unwrap()
.reversed_axes();
let start = (map.slice(s![.., 0]).iter().position(|c| *c == '.').unwrap(), 0);
let end = (
map.slice(s![.., map.len_of(Axis(1)) - 1])
.iter()
.position(|c| *c == '.')
.unwrap(),
map.len_of(Axis(1)) - 1,
);
let mut graph = StableGraph::default();
let mut indexes = HashMap::new();
for (pos, c) in map.indexed_iter() {
if *c != '#' {
indexes.insert(pos, graph.add_node(Node { c: *c, pos }));
}
}
Self {
map,
start,
end,
graph,
indexes,
}
}
}
impl ForestMap {
fn build_graph(&mut self) {
for (pos, c) in self.map.indexed_iter() {
match c {
'#' => continue,
'.' => {
adjacent_to(&self.map, pos).iter().for_each(|adj| {
if self.indexes.contains_key(&adj) {
self.graph.add_edge(self.indexes[&pos], self.indexes[adj], 1);
}
});
}
'^' => {
if let Some(adj) = offset_pos(&self.map, pos, (0, -1)) {
if self.indexes.contains_key(&adj) {
self.graph.add_edge(self.indexes[&pos], self.indexes[&adj], 1);
}
}
}
'>' => {
if let Some(adj) = offset_pos(&self.map, pos, (1, 0)) {
if self.indexes.contains_key(&adj) {
self.graph.add_edge(self.indexes[&pos], self.indexes[&adj], 1);
}
}
}
'v' => {
if let Some(adj) = offset_pos(&self.map, pos, (0, 1)) {
if self.indexes.contains_key(&adj) {
self.graph.add_edge(self.indexes[&pos], self.indexes[&adj], 1);
}
}
}
'<' => {
if let Some(adj) = offset_pos(&self.map, pos, (-1, 0)) {
if self.indexes.contains_key(&adj) {
self.graph.add_edge(self.indexes[&pos], self.indexes[&adj], 1);
}
}
}
c => panic!("invalid map character {}", c),
}
}
}
fn build_graph2(&mut self) {
for (pos, c) in self.map.indexed_iter() {
match c {
'#' => continue,
'.' | '^' | '>' | 'v' | '<' => {
adjacent_to(&self.map, pos).iter().for_each(|adj| {
if self.indexes.contains_key(&adj) {
self.graph.add_edge(self.indexes[&pos], self.indexes[adj], 1);
}
});
}
c => panic!("invalid map character {}", c),
}
}
}
// Cull nodes that don't change the topology of the graph and combine their cost
fn simplify_graph(&mut self) {
let mut idxs: Vec<_> = self
.graph
.neighbors(self.indexes[&self.start])
.map(|idx| (self.indexes[&self.start], idx))
.collect();
let mut visited = HashSet::from([self.indexes[&self.start]]);
while let Some((last_idx, cur_idx)) = idxs.pop() {
if !visited.insert(cur_idx) {
continue;
}
let our_neighbors = self.graph.neighbors(cur_idx).collect_vec();
// if we have exactly 2 neighbours, then one is where we came from, and we can shortcut this node with a
// pair of new edges A <-> C and break the existing 4 edges between them
if our_neighbors.len() == 2 {
let next_idx = our_neighbors.iter().find(|n| **n != last_idx).unwrap();
// remove the 4 existing edges
// careful of order of operations, as removing edges invalidates edge indexes
let forward_cost = self
.graph
.remove_edge(self.graph.find_edge(cur_idx, *next_idx).unwrap())
.unwrap();
let last_forward_cost = self
.graph
.remove_edge(self.graph.find_edge(last_idx, cur_idx).unwrap())
.unwrap();
let backward_cost = self
.graph
.remove_edge(self.graph.find_edge(cur_idx, last_idx).unwrap())
.unwrap();
let next_backward_cost = self
.graph
.remove_edge(self.graph.find_edge(*next_idx, cur_idx).unwrap())
.unwrap();
let new_forward_cost = forward_cost + last_forward_cost;
let new_backward_cost = backward_cost + next_backward_cost;
// add edge from last to next
self.graph.add_edge(last_idx, *next_idx, new_forward_cost);
self.graph.add_edge(*next_idx, last_idx, new_backward_cost);
self.graph.remove_node(cur_idx);
// push the next node
idxs.push((last_idx, *next_idx));
} else {
// don't do anything about nodes with > 2 edges, just push them onto the stack, if there are some
idxs.append(
&mut self
.graph
.neighbors(cur_idx)
.into_iter()
.map(|next_idx| (cur_idx, next_idx))
.collect(),
);
}
}
}
}
impl Debug for ForestMap {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for y in 0..self.map.len_of(Axis(1)) {
for c in self.map.index_axis(Axis(1), y) {
f.write_char(*c)?;
}
writeln!(f)?;
}
Ok(())
}
}
// PROBLEM 1 solution
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
let mut map = ForestMap::from(input);
map.build_graph();
// println!("{:?}", map);
let paths = all_simple_paths::<Vec<_>, _>(&map.graph, map.indexes[&map.start], map.indexes[&map.end], 0, None)
.collect_vec();
let longest = paths.iter().max_by_key(|path| path.len()).unwrap();
longest.len() as u64 - 1
}
fn calc_path_length(map: &ForestMap, path: &Vec<NodeIndex>) -> u64 {
path.iter().tuple_windows().fold(0, |accum, (prev, next)| {
accum + map.graph[map.graph.find_edge(*prev, *next).unwrap()]
})
}
// PROBLEM 2 solution
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
let mut map = ForestMap::from(input);
map.build_graph2();
map.simplify_graph();
let paths = all_simple_paths::<Vec<_>, _>(&map.graph, map.indexes[&map.start], map.indexes[&map.end], 0, None)
.collect_vec();
let longest = paths.iter().max_by_key(|path| calc_path_length(&map, &path)).unwrap();
longest.iter().tuple_windows().fold(0, |accum, (prev, next)| {
accum + map.graph[map.graph.find_edge(*prev, *next).unwrap()]
})
}
#[cfg(test)]
mod tests {
use crate::*;
use std::io::Cursor;
const EXAMPLE: &str = &"#.#####################
#.......#########...###
#######.#########.#.###
###.....#.>.>.###.#.###
###v#####.#v#.###.#.###
###.>...#.#.#.....#...#
###v###.#.#.#########.#
###...#.#.#.......#...#
#####.#.#.#######.#.###
#.....#.#.#.......#...#
#.#####.#.#.#########v#
#.#...#...#...###...>.#
#.#.#v#######v###.###v#
#...#.>.#...>.>.#.###.#
#####v#.#.###v#.#.###.#
#.....#...#...#.#.#...#
#.#########.###.#.#.###
#...###...#...#...#.###
###.###.#.###v#####v###
#...#...#.#.>.>.#.>.###
#.###.###.#.###.#.#v###
#.....###...###...#...#
#####################.#";
#[test]
fn problem1_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem1(c.lines()), 94);
}
#[test]
fn problem2_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem2(c.lines()), 154);
}
}

134
24/Cargo.lock generated Normal file
View File

@ -0,0 +1,134 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
dependencies = [
"memchr",
]
[[package]]
name = "day24"
version = "0.1.0"
dependencies = [
"itertools",
"lazy-regex",
]
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "itertools"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
dependencies = [
"either",
]
[[package]]
name = "lazy-regex"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d12be4595afdf58bd19e4a9f4e24187da2a66700786ff660a418e9059937a4c"
dependencies = [
"lazy-regex-proc_macros",
"once_cell",
"regex",
]
[[package]]
name = "lazy-regex-proc_macros"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44bcd58e6c97a7fcbaffcdc95728b393b8d98933bfadad49ed4097845b57ef0b"
dependencies = [
"proc-macro2",
"quote",
"regex",
"syn",
]
[[package]]
name = "memchr"
version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "proc-macro2"
version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "syn"
version = "2.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b7d0a2c048d661a1a59fcd7355baa232f7ed34e0ee4df2eef3c1c1c0d3852d8"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"

10
24/Cargo.toml Normal file
View File

@ -0,0 +1,10 @@
[package]
name = "day24"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
itertools = "0.12.0"
lazy-regex = "3.1.0"

24
24/cheat.py Normal file
View File

@ -0,0 +1,24 @@
#!/usr/bin/env python3
from z3 import *
rx, ry, rz = Ints('rx ry rz')
rvx, rvy, rvz = Ints('rvx rvy rvz')
t0, t1, t2 = Ints('t0 t1 t2')
answer = Int('answer')
solve(
rx + t0 * rvx == 225415405941969 + t0 * 23,
ry + t0 * rvy == 400648127977931 + t0 * -204,
rz + t0 * rvz == 79201130433258 + t0 * 617,
rx + t1 * rvx == 353783687623292 + t1 * -80,
ry + t1 * rvy == 138575899489956 + t1 * 156,
rz + t1 * rvz == 318416438572569 + t1 * 21,
rx + t2 * rvx == 215751176267772 + t2 * -120,
ry + t2 * rvy == 376619563956940 + t2 * 126,
rz + t2 * rvz == 230133299986253 + t2 * -352,
answer == rx + ry + rz,
)

205
24/src/main.rs Normal file
View File

@ -0,0 +1,205 @@
use itertools::Itertools;
use std::fmt::Debug;
use std::fs::File;
use std::io::{BufRead, BufReader, Lines};
use std::ops::Index;
use std::time::Instant;
// BOILERPLATE
type InputIter = Lines<BufReader<File>>;
fn get_input() -> InputIter {
let f = File::open("input").unwrap();
let br = BufReader::new(f);
br.lines()
}
fn main() {
let start = Instant::now();
let ans1 = problem1(get_input());
let duration = start.elapsed();
println!("Problem 1 solution: {} [{}s]", ans1, duration.as_secs_f64());
let start = Instant::now();
let ans2 = problem2(get_input());
let duration = start.elapsed();
println!("Problem 2 solution: {} [{}s]", ans2, duration.as_secs_f64());
}
// Parse
#[derive(Clone, Copy)]
struct Vec3 {
x: f64,
y: f64,
z: f64,
}
impl Debug for Vec3 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}, {}, {}", self.x, self.y, self.z)
}
}
#[derive(Clone, Copy)]
struct Vec2 {
x: f64,
y: f64,
}
impl Debug for Vec2 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}, {}", self.x, self.y)
}
}
#[derive(Clone)]
struct Hailstone {
pos: Vec3,
vel: Vec3,
}
impl Hailstone {
fn xy_vel_angle(&self) -> f64 {
(self.vel.y / self.vel.x).atan()
}
fn xy_vel_abs(&self) -> f64 {
(self.vel.x.powi(2) + self.vel.y.powi(2)).sqrt()
}
fn xy_slope(&self) -> f64 {
self.vel.y / self.vel.x
}
fn y_crossing(&self) -> f64 {
self.pos.y - self.xy_slope() * self.pos.x
}
fn xy_poi(&self, other: &Hailstone) -> Option<Vec2> {
let our_slope = self.xy_slope();
let other_slope = other.xy_slope();
if our_slope == other_slope || our_slope * other_slope == -1. {
None
} else {
let our_yint = self.y_crossing();
let other_yint = other.y_crossing();
let ratio = (other_yint - our_yint) / (our_slope - other_slope);
Some(Vec2 {
x: ratio,
y: our_slope * ratio + our_yint,
})
}
}
fn xy_point_future(&self, point: &Vec2) -> bool {
// a point will be in the past if the difference between its 'new' position and its 'start' position has a
// different sign than the velocity for any component
let diffs = [point.x - self.pos.x, point.y - self.pos.y];
// for (diff, vel) in diffs.iter().zip([self.vel.x, self.vel.y].iter()) {
// println!(" diff: {:?} vel: {:?} mul: {:?}", diff, vel, diff * vel > 0.);
// }
diffs.iter().zip([self.vel.x, self.vel.y].iter()).any(|(diff, vel)| diff * vel > 0.)
}
}
impl From<&str> for Hailstone {
fn from(value: &str) -> Self {
let (pos, vel) = value.split_once("@").unwrap();
let [px, py, pz] = pos.split(",").map(|s| s.trim().parse::<f64>().unwrap()).collect_vec()[..] else {
panic!()
};
let [vx, vy, vz] = vel.split(",").map(|s| s.trim().parse::<f64>().unwrap()).collect_vec()[..] else {
panic!()
};
Self {
pos: Vec3 { x: px, y: py, z: pz },
vel: Vec3 { x: vx, y: vy, z: vz },
}
}
}
impl Debug for Hailstone {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?} @ {:?}", self.pos, self.vel)
}
}
#[derive(Debug)]
struct Hailstones {
stones: Vec<Hailstone>,
}
impl Hailstones {
fn count_xy_pois(&self, bounds: (f64, f64)) -> u64 {
assert!(bounds.0 < bounds.1);
let mut stones = self.stones.clone();
let mut count = 0;
while let Some(stone) = &stones.pop() {
for other in &stones {
let Some(poi) = stone.xy_poi(other) else { continue };
// println!("intersection: {:?} / {:?} @ {:?}", stone, other, poi);
if poi.x >= bounds.0 && poi.x <= bounds.1 && poi.y >= bounds.0 && poi.y <= bounds.1 && stone.xy_point_future(&poi) && other.xy_point_future(&poi) {
count += 1;
}
}
}
count
}
}
impl Index<usize> for Hailstones {
type Output = Hailstone;
fn index(&self, index: usize) -> &Self::Output {
&self.stones[index]
}
}
impl<T: BufRead> From<Lines<T>> for Hailstones {
fn from(value: Lines<T>) -> Self {
let mut stones = Vec::new();
for line in value {
stones.push(Hailstone::from(line.unwrap().as_str()));
}
Hailstones { stones }
}
}
// PROBLEM 1 solution
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
problem1_impl(input, (200000000000000., 400000000000000.))
}
fn problem1_impl<T: BufRead>(input: Lines<T>, bounds: (f64, f64)) -> u64 {
let stones = Hailstones::from(input);
stones.count_xy_pois(bounds)
}
// PROBLEM 2 solution
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
0
}
#[cfg(test)]
mod tests {
use crate::*;
use std::io::Cursor;
const EXAMPLE: &str = &"19, 13, 30 @ -2, 1, -2
18, 19, 22 @ -1, -1, -2
20, 25, 34 @ -2, -2, -4
12, 31, 28 @ -1, -2, -1
20, 19, 15 @ 1, -5, -3";
#[test]
fn problem1_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem1_impl(c.lines(), (7., 27.)), 2);
}
#[test]
fn problem2_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem2(c.lines()), 0);
}
}

266
25/Cargo.lock generated Normal file
View File

@ -0,0 +1,266 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "console"
version = "0.15.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8"
dependencies = [
"encode_unicode",
"lazy_static",
"libc",
"unicode-width",
"windows-sys",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751"
dependencies = [
"cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e3681d554572a651dda4186cd47240627c3d0114d45a95f6ad27f2f22e7548d"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c"
dependencies = [
"cfg-if",
]
[[package]]
name = "day25"
version = "0.1.0"
dependencies = [
"indicatif",
"itertools",
"petgraph",
"rayon",
]
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "encode_unicode"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "fixedbitset"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "hashbrown"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]]
name = "indexmap"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "indicatif"
version = "0.17.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25"
dependencies = [
"console",
"instant",
"number_prefix",
"portable-atomic",
"unicode-width",
]
[[package]]
name = "instant"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if",
]
[[package]]
name = "itertools"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
dependencies = [
"either",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
[[package]]
name = "number_prefix"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
[[package]]
name = "petgraph"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9"
dependencies = [
"fixedbitset",
"indexmap",
]
[[package]]
name = "portable-atomic"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
[[package]]
name = "rayon"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
]
[[package]]
name = "unicode-width"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
[[package]]
name = "windows-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"

12
25/Cargo.toml Normal file
View File

@ -0,0 +1,12 @@
[package]
name = "day25"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
indicatif = "0.17.7"
itertools = "0.12.0"
petgraph = "0.6.4"
rayon = "1.8.0"

171
25/src/main.rs Normal file
View File

@ -0,0 +1,171 @@
use indicatif::{ProgressBar, ProgressIterator, ProgressStyle};
use itertools::Itertools;
use petgraph::algo::connected_components;
use petgraph::dot::Dot;
use petgraph::prelude::*;
use petgraph::visit::{depth_first_search, DfsEvent, IntoEdgeReferences, IntoNodeIdentifiers, IntoNodeReferences};
use rayon::prelude::*;
use std::collections::HashMap;
use std::fs::File;
use std::io::{BufRead, BufReader, Lines};
use std::time::Instant;
// BOILERPLATE
type InputIter = Lines<BufReader<File>>;
fn get_input() -> InputIter {
let f = File::open("input").unwrap();
let br = BufReader::new(f);
br.lines()
}
fn main() {
let start = Instant::now();
let ans1 = problem1(get_input());
let duration = start.elapsed();
println!("Problem 1 solution: {} [{}s]", ans1, duration.as_secs_f64());
let start = Instant::now();
let ans2 = problem2(get_input());
let duration = start.elapsed();
println!("Problem 2 solution: {} [{}s]", ans2, duration.as_secs_f64());
}
// PARSE
struct Circuit {
graph: UnGraph<String, String>,
nodes: HashMap<String, Node>,
}
#[derive(Debug, Clone)]
struct Node {
targets: Vec<String>,
index: Option<NodeIndex>,
}
impl Node {
fn with_index(mut self, index: NodeIndex) -> Self {
self.index = Some(index);
self
}
}
impl<T: BufRead> From<Lines<T>> for Circuit {
fn from(lines: Lines<T>) -> Self {
let mut graph = UnGraph::default();
let mut nodes = HashMap::new();
for line in lines.map(|v| v.unwrap()) {
let (source, targets_s) = line.split_once(": ").unwrap();
let targets = targets_s.split_whitespace().map(|s| s.to_string()).collect_vec();
nodes.insert(
source.to_string(),
Node {
targets,
index: Some(graph.add_node(source.to_string())),
},
);
}
for (name, node) in nodes.iter_mut() {
for target in &node.targets {
let target_idx = match graph.node_references().find(|n| n.1 == target).map(|n| n.0) {
Some(target) => target,
None => graph.add_node(target.to_string()),
};
graph.add_edge(node.index.unwrap(), target_idx, format!("{}-{}", name, target));
}
}
Self { graph, nodes }
}
}
impl Circuit {
// returns the set of nodes on each side
fn find_critical_edges(&self, removals: usize) -> (Vec<String>, Vec<String>) {
let bar = ProgressBar::new(self.graph.edge_references().combinations(removals).try_len().unwrap() as u64)
.with_style(
ProgressStyle::with_template(
"[{elapsed_precise}/{eta_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {per_sec}",
)
.unwrap(),
);
// for set in self.graph.edge_references().combinations(removals).progress_with(bar) {
let set = [("zcp", "zjm"), ("rsg", "nsk"), ("jks", "rfg")]
.iter()
.flat_map(|(src, dst)| {
self.graph.edge_references().filter(|e| self.graph[e.source()] == *src && self.graph[e.target()] == *dst || self.graph[e.target()] == *src && self.graph[e.source()] == *dst)
}).collect_vec();
let mut local_graph: StableUnGraph<_, _> = self.graph.clone().into();
for wire in &set {
local_graph.remove_edge(wire.id());
}
let local_graph = UnGraph::from(local_graph);
if connected_components(&local_graph) == 2 {
// each edge will have one leg in each component, so return the set of connected nodes
let mut left = Vec::new();
depth_first_search(&local_graph, Some(set[0].source()), |event| {
if let DfsEvent::Discover(n, t) = event {
left.push(local_graph[n].to_string());
}
});
let mut right = Vec::new();
depth_first_search(&local_graph, Some(set[0].target()), |event| {
if let DfsEvent::Discover(n, t) = event {
right.push(local_graph[n].to_string());
}
});
return (left, right);
}
// }
panic!("no cutline found");
}
}
// PROBLEM 1 solution
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
let circuit = Circuit::from(input);
println!("{:?}", Dot::new(&circuit.graph));
let (left, right) = circuit.find_critical_edges(3);
(left.len() * right.len()) as u64
}
// PROBLEM 2 solution
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
0
}
#[cfg(test)]
mod tests {
use crate::*;
use std::io::Cursor;
const EXAMPLE: &str = &"jqt: rhn xhk nvd
rsh: frs pzl lsr
xhk: hfx
cmg: qnr nvd lhk bvb
rhn: xhk bvb hfx
bvb: xhk hfx
pzl: lsr hfx nvd
qnr: nvd
ntq: jqt hfx bvb xhk
nvd: lhk
lsr: lhk
rzs: qnr cmg lsr rsh
frs: qnr lhk lsr";
#[test]
fn problem1_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem1(c.lines()), 54);
}
#[test]
fn problem2_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem2(c.lines()), 0);
}
}