From f9e68fc7420159e03dfd614a9697ebb04c4a4a32 Mon Sep 17 00:00:00 2001 From: Matt Harden Date: Fri, 4 Jul 2025 17:57:33 -0700 Subject: [PATCH] 2023 day 8. --- 2023/08/Cargo.toml | 10 + 2023/08/data/example1 | 9 + 2023/08/data/example2 | 5 + 2023/08/data/example3 | 10 + 2023/08/data/input | 720 ++++++++++++++++++++++++++++++++++++ 2023/08/src/data.rs | 11 + 2023/08/src/direction.rs | 20 + 2023/08/src/instructions.rs | 27 ++ 2023/08/src/main.rs | 14 + 2023/08/src/nodes.rs | 143 +++++++ 2023/08/src/part1/mod.rs | 29 ++ 2023/08/src/part2/mod.rs | 32 ++ 2023/08/src/puzzle.rs | 47 +++ 2023/08/src/state.rs | 63 ++++ Cargo.lock | 31 ++ 15 files changed, 1171 insertions(+) create mode 100644 2023/08/Cargo.toml create mode 100644 2023/08/data/example1 create mode 100644 2023/08/data/example2 create mode 100644 2023/08/data/example3 create mode 100644 2023/08/data/input create mode 100644 2023/08/src/data.rs create mode 100644 2023/08/src/direction.rs create mode 100644 2023/08/src/instructions.rs create mode 100644 2023/08/src/main.rs create mode 100644 2023/08/src/nodes.rs create mode 100644 2023/08/src/part1/mod.rs create mode 100644 2023/08/src/part2/mod.rs create mode 100644 2023/08/src/puzzle.rs create mode 100644 2023/08/src/state.rs diff --git a/2023/08/Cargo.toml b/2023/08/Cargo.toml new file mode 100644 index 0000000..502ae69 --- /dev/null +++ b/2023/08/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "y2023d08" +version = "0.1.0" +edition = "2024" + +[dependencies] +elsa = "1.11.2" +lazy-regex = "3.4.1" +num-integer = "0.1.46" +typed-arena = "2.0.2" diff --git a/2023/08/data/example1 b/2023/08/data/example1 new file mode 100644 index 0000000..59e2d47 --- /dev/null +++ b/2023/08/data/example1 @@ -0,0 +1,9 @@ +RL + +AAA = (BBB, CCC) +BBB = (DDD, EEE) +CCC = (ZZZ, GGG) +DDD = (DDD, DDD) +EEE = (EEE, EEE) +GGG = (GGG, GGG) +ZZZ = (ZZZ, ZZZ) \ No newline at end of file diff --git a/2023/08/data/example2 b/2023/08/data/example2 new file mode 100644 index 0000000..34ffa8a --- /dev/null +++ b/2023/08/data/example2 @@ -0,0 +1,5 @@ +LLR + +AAA = (BBB, BBB) +BBB = (AAA, ZZZ) +ZZZ = (ZZZ, ZZZ) \ No newline at end of file diff --git a/2023/08/data/example3 b/2023/08/data/example3 new file mode 100644 index 0000000..a8e2c98 --- /dev/null +++ b/2023/08/data/example3 @@ -0,0 +1,10 @@ +LR + +11A = (11B, XXX) +11B = (XXX, 11Z) +11Z = (11B, XXX) +22A = (22B, XXX) +22B = (22C, 22C) +22C = (22Z, 22Z) +22Z = (22B, 22B) +XXX = (XXX, XXX) \ No newline at end of file diff --git a/2023/08/data/input b/2023/08/data/input new file mode 100644 index 0000000..6414f20 --- /dev/null +++ b/2023/08/data/input @@ -0,0 +1,720 @@ +LRLLLRRLRRLRRLRRLLRRLRRLLRRRLLRRLRRLRRLRRLRLRLLLLLRRLRRLLRLRRRLLRRLRLLLLLLLRRLRLRRRLRRLRRRLRRLLLRRLLRRRLLRRRLRRLRLRRRLRRRLRLRLLRRRLRRRLRRLLRRRLRLRRLLRLLRRLLRRLRRRLRRLRLRRLLRRRLRRRLRRRLRLRLRLRRRLLRRRLRLRRLLRRLRRLRRLRLLRRLLRRRLRRRLRRLRRLRLLRRLRLRRLRRRLRRRLRRLRLRRRLRRRLRLLLRRLRLLRRRR + +RLP = (BMK, PCM) +JTJ = (TVN, CJQ) +PFR = (MMX, BQC) +JGM = (NDJ, PCV) +LVD = (TCK, PVR) +SVS = (CDL, RNX) +QDF = (XFG, NDX) +TBH = (THM, DBC) +FQK = (TFT, CNF) +THV = (NQD, VNT) +VRL = (HCQ, DPS) +LDM = (CCH, PJB) +GXR = (BRH, XLM) +CKF = (THV, BNQ) +KMH = (TLN, PKX) +SLP = (XSV, VCR) +SXF = (MJC, TJX) +KMJ = (LBC, FKT) +VDX = (MJQ, SGJ) +SHM = (LJT, PBV) +PGX = (KFM, SLD) +VHN = (BBB, SJV) +BFL = (VJG, JJF) +HKX = (HNN, MQK) +SNB = (PFJ, CTB) +GCD = (BKX, DHR) +DQN = (CTP, XDN) +DJP = (GGK, NSF) +KPG = (QCX, PQR) +PGR = (QXN, PGT) +RVG = (QDF, MVK) +BMV = (FFH, FFH) +VCX = (LDH, TQD) +KLC = (DFP, FQD) +JMQ = (JKT, BVH) +KLM = (QJQ, NMB) +TTQ = (GMV, QFC) +XFG = (RCP, DTL) +DDC = (FKG, JGM) +FCD = (SPS, JSS) +LBC = (VDX, NFH) +RXN = (KTP, JMB) +MVH = (NJC, LVJ) +PJB = (RXN, JSB) +PBS = (QGF, LSB) +RCP = (PKL, HSJ) +TBF = (RJP, JHJ) +SFB = (FQH, TMH) +XLC = (BXM, MSF) +TCS = (TGT, GPT) +CPQ = (JXT, FCH) +GDM = (XBM, DDS) +QQQ = (FLS, VCX) +MDT = (TFS, TLV) +PFK = (NPJ, SSR) +KSN = (JXV, DRL) +VBS = (NJC, LVJ) +TCP = (TBK, XXS) +QTT = (FLS, VCX) +FSH = (FBM, XXN) +MND = (KJH, MGC) +DTN = (JNL, CGM) +QXV = (NQJ, FSR) +TRQ = (SNB, QRR) +RCG = (NMR, BRG) +TGB = (KFM, SLD) +QSK = (NNT, TBH) +DDD = (HPQ, XGH) +KHK = (XMS, DXH) +VVC = (CDB, LNP) +KNT = (MTB, QXS) +FBH = (HXT, FBG) +LJB = (TBF, FXQ) +HHH = (RPH, FSB) +KMR = (PFT, VVC) +CFC = (TVV, TVV) +LKC = (LHN, QRL) +PFT = (CDB, LNP) +JXV = (NXS, KVX) +XDN = (BRK, GGS) +NQJ = (KMH, QSS) +FQD = (PVS, JBV) +KJX = (DJV, XPP) +VJP = (HKX, VDR) +BSQ = (MGS, CFK) +VJG = (PSQ, KSD) +NNZ = (GPT, TGT) +CSR = (MFN, NQF) +KFM = (GFN, GMH) +PHR = (TJC, XTJ) +KCL = (JJG, VJL) +VCR = (BML, KCS) +PVM = (BMH, QDN) +HPQ = (LFD, FQM) +MLR = (JKV, NTB) +XGV = (RQL, JKX) +FHB = (NPR, MNG) +DBC = (LDK, TVP) +FSN = (XQR, DBZ) +JJF = (PSQ, KSD) +CLK = (BMH, QDN) +QMH = (BLQ, VKX) +SBH = (SRN, SPX) +MJP = (JXV, DRL) +CTB = (XSF, XKC) +BJK = (RKS, FQB) +CCX = (CND, CND) +DTL = (HSJ, PKL) +NNT = (THM, DBC) +GBR = (XGG, LNN) +AAA = (XRF, DNR) +FLJ = (GNM, MTK) +BXQ = (LNX, GFM) +GMH = (JXC, CSR) +DXL = (SVN, FGV) +XBD = (GHF, HJQ) +PTQ = (SGQ, GVD) +SCJ = (SJV, BBB) +LHN = (LHH, FLK) +HBL = (JVT, MHM) +QLM = (TVJ, SVB) +BRH = (HNK, CTR) +MQJ = (GQR, DDC) +MTM = (KLV, FGJ) +MJQ = (LPR, JFH) +GQJ = (GKT, JKP) +FDC = (KPG, LDQ) +DRN = (SBH, NNV) +JRT = (DJP, JFJ) +LCP = (HPL, KLJ) +SDL = (MST, PDL) +NGM = (DRH, RTK) +TVV = (JSL, JSL) +QRR = (CTB, PFJ) +GGS = (FHF, XSJ) +MKD = (NGR, CDR) +GBM = (MNR, SVS) +SHK = (NMK, KNH) +BKB = (GHB, RVC) +GHH = (CFC, HQT) +CRN = (LBC, FKT) +BCF = (CBQ, NGM) +MQK = (DXL, JQV) +XQK = (TTG, XTV) +PMZ = (FBH, VMT) +LGV = (QMH, QPM) +TMH = (MTM, MVR) +TRM = (DJJ, FJS) +XFS = (XDH, VFP) +QPM = (VKX, BLQ) +CJN = (RXP, KLC) +RBC = (CND, DKZ) +RSP = (TTQ, PQQ) +LVJ = (XXK, XGT) +TTN = (BVH, JKT) +MBS = (KLM, XHN) +PJK = (CCS, TRS) +LDX = (XGM, PBP) +XGB = (NBS, LGV) +QXF = (MLR, MKB) +FFD = (MQF, MQF) +NPR = (BMQ, TPX) +JPN = (TBK, XXS) +FQJ = (MXL, BXP) +QXP = (JNL, CGM) +XBQ = (DDS, XBM) +NNG = (VJL, JJG) +BLQ = (CBN, FDT) +FQB = (XNP, SNR) +DHR = (DCM, SHK) +JVT = (KKR, BXQ) +PCV = (FFD, PDG) +PFJ = (XKC, XSF) +RTB = (RHT, SPG) +BXF = (PGR, CFJ) +DGS = (MKB, MLR) +MTK = (NJL, PQJ) +NMS = (XQJ, TLL) +XFF = (KHK, DRB) +MDR = (HCQ, DPS) +GMD = (XTT, XKV) +FKT = (VDX, NFH) +VNS = (PFR, GMK) +JTB = (BSC, XFF) +XPM = (BSQ, MFD) +QDT = (GQJ, GPK) +LLV = (VCR, XSV) +BQC = (MVH, VBS) +JLL = (CRV, HBL) +SNH = (LJT, PBV) +XGG = (LVD, RCD) +LPR = (JBJ, JBH) +QVC = (BXM, MSF) +NMB = (HQD, FVF) +XST = (GBM, MKH) +VTL = (JXT, FCH) +LDQ = (QCX, PQR) +QKK = (GFP, CVB) +KLV = (JCR, CJV) +GLC = (MNG, NPR) +GHJ = (JTB, KVQ) +XRF = (FTK, JXN) +JGB = (HQS, JSG) +VNT = (NCK, TXF) +PGT = (VVV, JDP) +GFB = (PSM, VLK) +TVP = (RDX, PPL) +VKX = (CBN, FDT) +NMR = (HLQ, NCV) +MGQ = (TPM, MND) +TFT = (DKH, TSX) +CBV = (CCX, CCX) +TJS = (RJK, RMC) +TRX = (XBB, MDP) +MTB = (QMV, RMR) +XSL = (CFC, HQT) +FRK = (SVF, LDX) +QGM = (LDM, GMS) +XGS = (NGR, CDR) +CTP = (GGS, BRK) +JXT = (JCN, KGR) +GFM = (FJQ, TMX) +NST = (MBS, MTX) +NDV = (JJT, KMR) +RDD = (MJP, KSN) +RDX = (JTJ, SKB) +HDF = (TVK, LCP) +DJV = (TRF, TFJ) +SGJ = (JFH, LPR) +BTX = (PHB, PBS) +KMK = (MQB, VLQ) +HXM = (VLK, PSM) +DPS = (QPG, HDC) +RJP = (PGL, SXF) +DKZ = (KCL, NNG) +GHR = (DFN, XTH) +XQJ = (JGB, BLH) +BRG = (NCV, HLQ) +HJQ = (QQQ, QTT) +JBJ = (JRT, HDD) +XSV = (KCS, BML) +CDL = (QDT, NGV) +LQS = (PCL, CKF) +TGH = (SSR, NPJ) +DBK = (MVC, GFJ) +VRX = (FXB, PHR) +RBH = (FFH, DXV) +DRQ = (GDJ, QBV) +GNM = (NJL, PQJ) +NCF = (QRL, LHN) +JQP = (KMR, JJT) +SBN = (TXS, QXB) +CFJ = (PGT, QXN) +KVQ = (XFF, BSC) +NDX = (RCP, DTL) +SRF = (KJX, QJG) +BFD = (LCP, TVK) +PBN = (XKL, TJS) +GRB = (QBV, GDJ) +TGT = (RGF, LQT) +TFJ = (LPT, KMK) +SRT = (FQQ, GBR) +THM = (LDK, TVP) +KVB = (RXP, KLC) +TVJ = (QVS, SQP) +XHN = (NMB, QJQ) +QGF = (JVB, GCD) +RKS = (XNP, SNR) +MFN = (NKF, RCG) +BVH = (QKK, TJL) +CNF = (DKH, TSX) +MSF = (QXP, DTN) +KCS = (LMM, GMD) +XGH = (LFD, FQM) +KJH = (KLD, FNH) +PKM = (BRH, XLM) +VVT = (CFJ, PGR) +XTC = (PHB, PBS) +LQT = (BTJ, DPM) +JKX = (TBP, QGM) +ZZZ = (DNR, XRF) +QXS = (RMR, QMV) +PBP = (KVB, CJN) +TTG = (FDM, FDM) +BML = (LMM, GMD) +RNX = (NGV, QDT) +TPX = (NDB, GHR) +MFS = (SDL, LRQ) +LGG = (LXG, JLL) +MJC = (GVX, JJK) +RRF = (GSK, NMS) +FBM = (HBB, FQJ) +PQQ = (QFC, GMV) +FCH = (KGR, JCN) +PDG = (MQF, FSN) +HLQ = (NGF, FKK) +QQB = (MTB, QXS) +HNN = (JQV, DXL) +TLN = (CNJ, PSX) +NGV = (GPK, GQJ) +LDK = (RDX, PPL) +NJC = (XXK, XGT) +XQA = (TGT, GPT) +JCH = (XBB, MDP) +KMB = (QJG, KJX) +TCK = (VVH, SML) +FKS = (PVM, CLK) +GMS = (CCH, PJB) +LHH = (GRB, DRQ) +JML = (RQL, JKX) +DTP = (TGH, PFK) +BKX = (SHK, DCM) +HPL = (QLM, DPV) +JJG = (VKR, DQN) +PDL = (QNS, BJC) +SLD = (GFN, GMH) +JQV = (SVN, FGV) +FKK = (LLV, SLP) +CTC = (HKX, VDR) +TSX = (FRM, FRK) +FNH = (BTX, XTC) +JMB = (BCF, SKS) +KGR = (QLB, HTP) +TXS = (VRL, MDR) +KNH = (DGS, QXF) +DDS = (VHN, SCJ) +MML = (TPM, MND) +XKB = (SHM, SNH) +RMC = (FRX, JRJ) +XVP = (JSL, LSH) +HXT = (GLC, FHB) +FQM = (MDT, DBH) +PSX = (LRL, GHJ) +VSC = (FXQ, TBF) +JFH = (JBJ, JBH) +SML = (GSN, TVS) +BMK = (XNT, KCD) +VFP = (BXF, VVT) +GHB = (LHM, HDQ) +FDM = (SGQ, SGQ) +FQH = (MTM, MVR) +JJC = (GXR, PKM) +HFM = (PKM, GXR) +LNX = (FJQ, FJQ) +XTT = (VRX, PML) +TQD = (NSN, DDD) +FGV = (XFS, FLT) +FHF = (QXV, NVN) +HDQ = (TTB, RLP) +CQN = (STG, LQS) +VNJ = (SNH, SHM) +DPB = (JTP, PJK) +JCQ = (CNF, TFT) +LPF = (XMB, FBB) +SQP = (JML, XGV) +QSS = (TLN, PKX) +CDB = (SDB, QJS) +GTF = (TRQ, QNQ) +KSD = (BMV, RBH) +JFD = (CCX, RBC) +GSN = (DRN, KKD) +RXP = (DFP, FQD) +RTK = (RDD, DTF) +GPT = (LQT, RGF) +TLV = (LGT, VGM) +FSB = (HGC, TXM) +KKD = (SBH, NNV) +XNT = (LJB, VSC) +LXG = (HBL, CRV) +TTB = (PCM, BMK) +QPG = (KKX, SFB) +RQL = (QGM, TBP) +JNL = (JJC, HFM) +TPM = (KJH, MGC) +KKR = (LNX, GFM) +SVF = (PBP, XGM) +LHM = (RLP, TTB) +JDP = (XQL, NST) +PCM = (KCD, XNT) +TQF = (LDQ, KPG) +NGR = (DTP, NFD) +PPS = (NQN, RHG) +DRL = (KVX, NXS) +MVF = (CPP, FCD) +SSR = (BJK, VCP) +DRH = (RDD, DTF) +NSJ = (FKS, XVL) +CPS = (QDF, MVK) +PML = (PHR, FXB) +DPM = (MML, MGQ) +MJS = (QKF, PMZ) +JSS = (XPM, FLV) +CRV = (JVT, MHM) +QJS = (PKV, RTB) +JHJ = (PGL, SXF) +JSB = (KTP, JMB) +KBN = (JHB, NJZ) +BMJ = (CCK, GTF) +NDJ = (FFD, FFD) +LFD = (MDT, DBH) +NQD = (NCK, TXF) +NSN = (HPQ, XGH) +XKV = (PML, VRX) +SHS = (NBS, LGV) +SRN = (KRJ, DPJ) +QCX = (HHH, LLJ) +MNG = (TPX, BMQ) +TJX = (GVX, JJK) +FVF = (PPS, LFK) +LDH = (DDD, NSN) +MVC = (FHQ, MQJ) +TJL = (GFP, CVB) +JXC = (MFN, NQF) +LJT = (LKC, NCF) +PVS = (XGB, SHS) +VVV = (NST, XQL) +CBQ = (DRH, RTK) +SKS = (NGM, CBQ) +LLJ = (FSB, RPH) +CND = (NNG, KCL) +JSG = (SFL, LCQ) +NJZ = (VMD, PBN) +PKL = (XGS, MKD) +BLH = (JSG, HQS) +JHB = (PBN, VMD) +GQR = (FKG, JGM) +CNJ = (GHJ, LRL) +MVR = (KLV, FGJ) +SPG = (XQC, FJX) +FTK = (SBN, PDJ) +FJB = (FQQ, GBR) +LRL = (KVQ, JTB) +PQJ = (XST, TMM) +NXS = (JHF, SKG) +TFS = (LGT, VGM) +SNR = (RSP, XTB) +DPJ = (VXJ, DBK) +NJL = (XST, TMM) +VCP = (FQB, RKS) +NGF = (LLV, SLP) +KLD = (XTC, BTX) +QJF = (QQB, KNT) +FHQ = (GQR, DDC) +MQB = (VNJ, XKB) +FHG = (LLC, QSK) +VXJ = (GFJ, MVC) +LJV = (TRM, FXH) +TXM = (GFB, HXM) +BMQ = (NDB, GHR) +MXL = (RSB, FHL) +TBK = (HHK, DQQ) +GKT = (LGG, FHR) +DTF = (KSN, MJP) +VDJ = (MTK, GNM) +PCL = (BNQ, THV) +VLK = (XCH, LJV) +BSC = (KHK, DRB) +XTH = (VTL, CPQ) +XXS = (HHK, DQQ) +GMV = (MFS, XLJ) +BMH = (SLL, CTM) +RGF = (BTJ, DPM) +FJS = (DPB, PLN) +QDN = (SLL, CTM) +CVB = (CBV, JFD) +TMX = (XCR, KBN) +XKC = (JQP, NDV) +VGM = (QVC, XLC) +FGT = (SJH, MVF) +DXV = (DQL, MJS) +MGC = (FNH, KLD) +CCS = (JFG, NGD) +STG = (PCL, CKF) +NKF = (BRG, NMR) +HQT = (TVV, XVP) +QJQ = (HQD, FVF) +HQS = (SFL, LCQ) +MGS = (XBD, QHS) +QXB = (MDR, VRL) +GFP = (CBV, CBV) +JVX = (XRF, DNR) +MST = (BJC, QNS) +PBV = (NCF, LKC) +RJS = (MVF, SJH) +NBS = (QMH, QPM) +QHS = (GHF, HJQ) +XQL = (MBS, MTX) +DQQ = (LTJ, BKB) +SJV = (HDF, BFD) +VLQ = (XKB, VNJ) +CBN = (GHH, XSL) +JKT = (QKK, TJL) +MQF = (XQR, XQR) +MVK = (NDX, XFG) +CPP = (SPS, JSS) +FJX = (NSJ, PJD) +SKA = (VMT, FBH) +NDB = (XTH, DFN) +CCH = (RXN, JSB) +VVH = (GSN, TVS) +SVB = (SQP, QVS) +JXN = (PDJ, SBN) +PLN = (JTP, PJK) +DNR = (FTK, JXN) +RVC = (LHM, HDQ) +QKF = (VMT, FBH) +MNS = (GMK, PFR) +QLB = (FSH, MQP) +RHT = (XQC, FJX) +KCD = (VSC, LJB) +DQL = (QKF, QKF) +TBP = (LDM, GMS) +TRS = (NGD, JFG) +JKP = (LGG, FHR) +QXN = (VVV, JDP) +LCQ = (XBQ, GDM) +KLJ = (DPV, QLM) +BFK = (NMS, GSK) +DXH = (RVG, CPS) +JTP = (TRS, CCS) +MNR = (CDL, RNX) +SFL = (XBQ, GDM) +TJC = (MNS, VNS) +SDB = (PKV, RTB) +BBB = (BFD, HDF) +QRG = (TTG, XTV) +XSF = (NDV, JQP) +HQD = (LFK, PPS) +FHR = (JLL, LXG) +FBG = (GLC, FHB) +XCH = (FXH, TRM) +SGQ = (TCS, TCS) +DBZ = (GDD, CQN) +QFC = (MFS, XLJ) +KTP = (SKS, BCF) +HBB = (MXL, BXP) +TLL = (BLH, JGB) +RMR = (CQM, FHG) +BXM = (DTN, QXP) +GDD = (LQS, STG) +DFN = (VTL, CPQ) +DBH = (TLV, TFS) +HNK = (CTC, VJP) +FRX = (RRF, BFK) +XMB = (TGB, PGX) +NTB = (DBB, LPF) +MFD = (MGS, CFK) +JFJ = (GGK, NSF) +GDJ = (FQK, JCQ) +XCR = (JHB, JHB) +LRQ = (PDL, MST) +DPV = (SVB, TVJ) +HDC = (SFB, KKX) +LMM = (XTT, XKV) +SLL = (TRX, JCH) +PSQ = (BMV, RBH) +VJL = (VKR, DQN) +SPX = (DPJ, KRJ) +DBB = (FBB, XMB) +SPS = (FLV, XPM) +FRM = (LDX, SVF) +PKX = (CNJ, PSX) +TMM = (GBM, MKH) +VDR = (MQK, HNN) +MHM = (KKR, BXQ) +TVN = (XQK, QRG) +NSF = (VDJ, FLJ) +FXH = (DJJ, FJS) +FGJ = (JCR, CJV) +SJH = (FCD, CPP) +JJT = (VVC, PFT) +DRB = (DXH, XMS) +LNP = (QJS, SDB) +FKG = (NDJ, PCV) +CQM = (LLC, QSK) +HTP = (FSH, MQP) +FJQ = (XCR, XCR) +CFK = (QHS, XBD) +QJG = (DJV, XPP) +KKX = (FQH, TMH) +LSJ = (TCP, JPN) +QHD = (JPN, TCP) +PVR = (SML, VVH) +XSJ = (NVN, QXV) +QMV = (FHG, CQM) +NFD = (TGH, PFK) +LTJ = (GHB, RVC) +HCQ = (HDC, QPG) +JKV = (LPF, DBB) +BNQ = (NQD, VNT) +GGK = (VDJ, FLJ) +LLC = (NNT, TBH) +SKB = (TVN, CJQ) +KRJ = (DBK, VXJ) +BTJ = (MML, MGQ) +GJM = (CCK, GTF) +JHF = (QHD, LSJ) +GFJ = (FHQ, MQJ) +LFK = (NQN, RHG) +GVD = (TCS, NNZ) +XDH = (VVT, BXF) +NCK = (BMJ, GJM) +QRL = (FLK, LHH) +XKL = (RJK, RMC) +TXF = (BMJ, GJM) +TVS = (DRN, KKD) +FXQ = (JHJ, RJP) +XTB = (PQQ, TTQ) +XBB = (SRT, FJB) +DFP = (JBV, PVS) +NQA = (NNG, KCL) +DKH = (FRM, FRK) +CCK = (TRQ, QNQ) +XTJ = (MNS, VNS) +MDP = (FJB, SRT) +QBV = (JCQ, FQK) +PKV = (RHT, SPG) +CJV = (FDC, TQF) +LSH = (JVX, ZZZ) +JJK = (CRN, KMJ) +LPT = (MQB, VLQ) +RHG = (BFL, SJT) +FLT = (XDH, VFP) +KVX = (JHF, SKG) +FXB = (XTJ, TJC) +HSJ = (XGS, MKD) +PDJ = (TXS, QXB) +XNP = (XTB, RSP) +LGT = (QVC, XLC) +XPP = (TRF, TFJ) +XQR = (CQN, GDD) +NNV = (SRN, SPX) +FLV = (MFD, BSQ) +QVS = (XGV, JML) +JSL = (JVX, JVX) +CQP = (QQB, KNT) +TVK = (KLJ, HPL) +QNS = (JMQ, TTN) +BXP = (FHL, RSB) +JFG = (KMB, SRF) +GMK = (MMX, BQC) +MTX = (KLM, XHN) +CTM = (JCH, TRX) +XBM = (SCJ, VHN) +RCD = (PVR, TCK) +XLJ = (LRQ, SDL) +PPL = (JTJ, SKB) +NQF = (NKF, RCG) +RSB = (FGT, RJS) +NCV = (FKK, NGF) +GHF = (QTT, QQQ) +XVL = (PVM, CLK) +VMD = (XKL, TJS) +CTR = (VJP, CTC) +CGM = (JJC, HFM) +PJD = (XVL, FKS) +LJA = (CQN, GDD) +MKH = (MNR, SVS) +JVB = (DHR, BKX) +HDD = (DJP, JFJ) +XGM = (CJN, KVB) +HHK = (LTJ, BKB) +CJQ = (XQK, QRG) +FHL = (RJS, FGT) +FQQ = (XGG, LNN) +MMX = (MVH, VBS) +FLS = (TQD, LDH) +VMT = (HXT, FBG) +PSM = (LJV, XCH) +JBV = (XGB, SHS) +FSR = (QSS, KMH) +FDT = (GHH, XSL) +LSB = (GCD, JVB) +XMS = (CPS, RVG) +FLK = (GRB, DRQ) +PGL = (TJX, MJC) +JCN = (HTP, QLB) +JRJ = (RRF, BFK) +FFH = (DQL, DQL) +XTV = (FDM, PTQ) +VKR = (XDN, CTP) +XXK = (CQP, QJF) +SKG = (QHD, LSJ) +MQP = (XXN, FBM) +GFN = (JXC, CSR) +BJC = (TTN, JMQ) +NVN = (FSR, NQJ) +PHB = (QGF, LSB) +NPJ = (VCP, BJK) +LNN = (LVD, RCD) +BRK = (FHF, XSJ) +JBH = (HDD, JRT) +XXN = (FQJ, HBB) +SJT = (JJF, VJG) +DCM = (NMK, KNH) +GSK = (XQJ, TLL) +XGT = (CQP, QJF) +RJK = (FRX, JRJ) +DJJ = (PLN, DPB) +TRF = (LPT, KMK) +JCR = (TQF, FDC) +RPH = (TXM, HGC) +MKB = (NTB, JKV) +XQC = (PJD, NSJ) +NGD = (SRF, KMB) +GVX = (KMJ, CRN) +GPK = (GKT, JKP) +SVN = (FLT, XFS) +FBB = (PGX, TGB) +CDR = (DTP, NFD) +HGC = (HXM, GFB) +PQR = (HHH, LLJ) +QNQ = (SNB, QRR) +NVA = (PBN, VMD) +NFH = (SGJ, MJQ) +NMK = (QXF, DGS) +XLM = (CTR, HNK) +NQN = (SJT, BFL) \ No newline at end of file diff --git a/2023/08/src/data.rs b/2023/08/src/data.rs new file mode 100644 index 0000000..bb4684b --- /dev/null +++ b/2023/08/src/data.rs @@ -0,0 +1,11 @@ +#[cfg(test)] +pub const EXAMPLE1: &'static str = include_str!("../data/example1"); + +#[cfg(test)] +pub const EXAMPLE2: &'static str = include_str!("../data/example2"); + +#[cfg(test)] +pub const EXAMPLE3: &'static str = include_str!("../data/example3"); + +#[allow(unused)] +pub const INPUT: &'static str = include_str!("../data/input"); diff --git a/2023/08/src/direction.rs b/2023/08/src/direction.rs new file mode 100644 index 0000000..068d998 --- /dev/null +++ b/2023/08/src/direction.rs @@ -0,0 +1,20 @@ +#[derive(Debug)] +pub struct ParseError; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Direction { + Left, + Right, +} + +impl TryFrom for Direction { + type Error = ParseError; + + fn try_from(value: char) -> Result { + Ok(match value { + 'L' => Direction::Left, + 'R' => Direction::Right, + _ => return Err(ParseError), + }) + } +} diff --git a/2023/08/src/instructions.rs b/2023/08/src/instructions.rs new file mode 100644 index 0000000..ecf543c --- /dev/null +++ b/2023/08/src/instructions.rs @@ -0,0 +1,27 @@ +use std::str::FromStr; + +use crate::direction::{self, Direction}; + +#[derive(Debug)] +pub struct ParseError; + +impl From for ParseError { + fn from(_value: direction::ParseError) -> Self { + Self {} + } +} + +#[derive(Debug)] +pub struct Instructions(pub Box<[Direction]>); + +impl FromStr for Instructions { + type Err = ParseError; + + fn from_str(s: &str) -> Result { + Ok(Self( + s.chars() + .map(Direction::try_from) + .collect::>()?, + )) + } +} diff --git a/2023/08/src/main.rs b/2023/08/src/main.rs new file mode 100644 index 0000000..4af8be9 --- /dev/null +++ b/2023/08/src/main.rs @@ -0,0 +1,14 @@ +mod data; +mod direction; +mod instructions; +mod nodes; +mod part1; +mod part2; +mod puzzle; +mod state; + +fn main() { + use data::INPUT; + println!("Part 1: {}", part1::run(INPUT)); + println!("Part 2: {}", part2::run(INPUT)); +} diff --git a/2023/08/src/nodes.rs b/2023/08/src/nodes.rs new file mode 100644 index 0000000..9c52b27 --- /dev/null +++ b/2023/08/src/nodes.rs @@ -0,0 +1,143 @@ +use elsa::FrozenMap; +use lazy_regex::regex_if; +use std::cell::OnceCell; +use std::collections::HashMap; +use std::default::Default; +use std::fmt::Display; +use std::str::FromStr; + +#[derive(Debug)] +pub struct ParseError; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Name([u8; 3]); + +impl Name { + pub fn is_source(&self) -> bool { + self.0[2] == b'A' + } + + pub fn is_target(&self) -> bool { + self.0[2] == b'Z' + } +} + +impl FromStr for Name { + type Err = ParseError; + + fn from_str(s: &str) -> Result { + if !s.is_ascii() { + return Err(ParseError); + } + Ok(Name(s.as_bytes().try_into().map_err(|_| ParseError)?)) + } +} + +impl Display for Name { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", str::from_utf8(&self.0).unwrap()) + } +} + +#[derive(Debug)] +struct Node { + name: Name, + l: Name, + r: Name, +} + +impl FromStr for Node { + type Err = ParseError; + + fn from_str(s: &str) -> Result { + regex_if!( + r#"(?\w{3}) = \((?\w{3}), (?\w{3})\)"#, + s, + Self { + name: name.parse()?, + l: l.parse()?, + r: r.parse()?, + } + ) + .ok_or(ParseError) + } +} + +#[derive(Debug)] +pub struct Nodes(pub HashMap); + +impl FromStr for Nodes { + type Err = ParseError; + + fn from_str(s: &str) -> Result { + Ok(Nodes( + s.lines() + .map(|s| { + let node: Node = s.parse()?; + Ok((node.name, (node.l, node.r))) + }) + .collect::>()?, + )) + } +} + +#[derive(Debug)] +pub struct CyclicNode<'arena> { + pub name: Name, + pub l: OnceCell>, + pub r: OnceCell>, +} + +impl<'a> CyclicNode<'a> { + fn new(name: Name) -> Self { + Self { + name, + l: Default::default(), + r: Default::default(), + } + } + + pub fn is_target(&'a self) -> bool { + self.name.is_target() + } +} + +impl<'a> FromStr for CyclicNode<'a> { + type Err = ParseError; + + fn from_str(s: &str) -> Result { + let name: Name = s.parse()?; + Ok(CyclicNode::new(name)) + } +} + +pub type CyclicNodeRef<'a> = &'a CyclicNode<'a>; + +pub struct Graph<'a> { + pub nodes: FrozenMap>>, +} + +impl<'arena> Graph<'arena> { + fn link_nodes(&'arena self, name: &Name, l: &Name, r: &Name) { + let v = self.nodes.get(name).unwrap(); + let l = self.nodes.get(l).unwrap(); + let r = self.nodes.get(r).unwrap(); + v.l.set(l).unwrap(); + v.r.set(r).unwrap(); + } + + pub fn new() -> Graph<'arena> { + Graph { + nodes: FrozenMap::new(), + } + } + + pub fn add_nodes(&'arena self, Nodes(links): &Nodes) { + for (&name, _) in links { + self.nodes.insert(name, Box::new(CyclicNode::new(name))); + } + for (name, (l, r)) in links { + self.link_nodes(name, l, r); + } + } +} diff --git a/2023/08/src/part1/mod.rs b/2023/08/src/part1/mod.rs new file mode 100644 index 0000000..1b1613a --- /dev/null +++ b/2023/08/src/part1/mod.rs @@ -0,0 +1,29 @@ +use crate::nodes::Graph; +use crate::puzzle::Puzzle; +use crate::state::State; + +pub fn run(input: &str) -> usize { + let puzzle: Puzzle = input.parse().expect("parse failed"); + let graph = Graph::new(); + puzzle.add_to_graph(&graph); + let start = "AAA".parse().unwrap(); + let target = "ZZZ".parse().unwrap(); + let mut state = State::new(&puzzle.instructions, &graph.nodes[&start]); + state.target_distance(|node| node.name == target) +} + +#[cfg(test)] +mod test { + use super::*; + use crate::data; + + #[test] + fn test1() { + assert_eq!(run(data::EXAMPLE1), 2); + } + + #[test] + fn test2() { + assert_eq!(run(data::EXAMPLE2), 6); + } +} diff --git a/2023/08/src/part2/mod.rs b/2023/08/src/part2/mod.rs new file mode 100644 index 0000000..4eab00e --- /dev/null +++ b/2023/08/src/part2/mod.rs @@ -0,0 +1,32 @@ +use crate::nodes::Graph; +use crate::puzzle::Puzzle; +use crate::state::State; +use num_integer::lcm; + +pub fn run(input: &str) -> usize { + let puzzle: Puzzle = input.parse().expect("parse failed"); + let graph = Graph::new(); + graph.add_nodes(&puzzle.nodes); + puzzle + .nodes + .0 + .keys() + .filter(|name| name.is_source()) + .map(|name| { + let cnode = &graph.nodes[name]; + let mut state = State::new(&puzzle.instructions, &cnode); + state.find(|cnode| cnode.is_target()).unwrap() + }) + .fold(1, lcm) +} + +#[cfg(test)] +mod test { + use super::*; + use crate::data::EXAMPLE3; + + #[test] + fn test1() { + assert_eq!(run(EXAMPLE3), 6); + } +} diff --git a/2023/08/src/puzzle.rs b/2023/08/src/puzzle.rs new file mode 100644 index 0000000..4507e27 --- /dev/null +++ b/2023/08/src/puzzle.rs @@ -0,0 +1,47 @@ +use crate::instructions::Instructions; +use crate::nodes::{Graph, Nodes}; +use std::str::FromStr; + +#[derive(Debug)] +pub struct ParseError; + +impl From<::Err> for ParseError { + fn from(_value: ::Err) -> Self { + Self {} + } +} + +impl From<::Err> for ParseError { + fn from(_value: ::Err) -> Self { + Self {} + } +} + +pub struct Puzzle { + pub instructions: Instructions, + pub nodes: Nodes, +} + +impl Puzzle { + fn new(instructions: Instructions, nodes: Nodes) -> Self { + Self { + instructions, + nodes, + } + } + + pub fn add_to_graph<'arena>(&self, graph: &'arena Graph<'arena>) { + graph.add_nodes(&self.nodes); + } +} + +impl FromStr for Puzzle { + type Err = ParseError; + + fn from_str(s: &str) -> Result { + let (instructions, nodes) = s.split_once("\n\n").ok_or(ParseError)?; + let instructions: Instructions = instructions.parse()?; + let nodes: Nodes = nodes.parse()?; + Ok(Self::new(instructions, nodes)) + } +} diff --git a/2023/08/src/state.rs b/2023/08/src/state.rs new file mode 100644 index 0000000..e85fdda --- /dev/null +++ b/2023/08/src/state.rs @@ -0,0 +1,63 @@ +use crate::direction::Direction; +use crate::instructions::Instructions; +use crate::nodes::CyclicNodeRef; +use crate::nodes::Name; +use std::collections::HashSet; + +#[derive(Debug)] +pub struct State<'a> { + instructions: &'a [Direction], + pos: usize, // position in the instructions list + cnode: CyclicNodeRef<'a>, +} + +impl<'a> State<'a> { + pub fn new(instructions: &'a Instructions, cnode: CyclicNodeRef<'a>) -> Self { + Self { + instructions: &instructions.0, + pos: 0, + cnode, + } + } + + fn step(&mut self) { + let dir = self.instructions[self.pos]; + self.pos = (self.pos + 1) % self.instructions.len(); + use Direction::*; + self.cnode = match dir { + Left => self.cnode.l.get().unwrap(), + Right => self.cnode.r.get().unwrap(), + } + } + + // Step to the next target node and return the # of steps taken. + pub fn target_distance(&mut self, is_target: impl Fn(CyclicNodeRef) -> bool) -> usize { + let mut count = 0usize; + loop { + self.step(); + count += 1; + if is_target(self.cnode) { + break count; + } + } + } + + fn location(&self) -> (usize, Name) { + (self.pos, self.cnode.name) + } + + // Find the distances to each target node, ending when we find a loop. + pub fn find(&mut self, is_target: impl Fn(CyclicNodeRef) -> bool) -> Option { + let mut seen: HashSet<(usize, Name)> = HashSet::new(); + let distance = self.target_distance(&is_target); + loop { + if seen.contains(&self.location()) { + break Some(distance); + } + seen.insert(self.location()); + if self.target_distance(&is_target) != distance { + break None; + } + } + } +} diff --git a/Cargo.lock b/Cargo.lock index 0865149..43b2c17 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -155,6 +155,15 @@ version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7914353092ddf589ad78f25c5c1c21b7f80b0ff8621e7c814c3485b5306da9d" +[[package]] +name = "elsa" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9abf33c656a7256451ebb7d0082c5a471820c31269e49d807c538c252352186e" +dependencies = [ + "stable_deref_trait", +] + [[package]] name = "enum-iterator" version = "2.1.0" @@ -583,6 +592,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "strsim" version = "0.10.0" @@ -671,6 +686,12 @@ dependencies = [ "winnow", ] +[[package]] +name = "typed-arena" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" + [[package]] name = "unicode-ident" version = "1.0.18" @@ -745,6 +766,16 @@ dependencies = [ "parse-display", ] +[[package]] +name = "y2023d08" +version = "0.1.0" +dependencies = [ + "elsa", + "lazy-regex", + "num-integer", + "typed-arena", +] + [[package]] name = "y2024d01" version = "0.1.0"