From 9b5de2f13ed62e7b5f45a3466df99ec2aeeee073 Mon Sep 17 00:00:00 2001
From: tristan <tristan@tristans.cloud>
Date: Sat, 9 Dec 2023 15:39:30 +0000
Subject: [PATCH] 2023 day 08

---
 2023/08/example.txt               |   5 +
 2023/08/go.mod                    |   3 +
 2023/08/input.txt                 | 736 ++++++++++++++++++++++++++++++
 2023/08/part1/cmd/main.go         |  39 ++
 2023/08/part1/part1/part1.go      |  55 +++
 2023/08/part1/part1/part1_test.go |  58 +++
 2023/08/part2/cmd/main.go         |  43 ++
 2023/08/part2/example.txt         |  10 +
 2023/08/part2/part2/part2.go      |  84 ++++
 2023/08/part2/part2/part2_test.go |  81 ++++
 10 files changed, 1114 insertions(+)
 create mode 100644 2023/08/example.txt
 create mode 100644 2023/08/go.mod
 create mode 100644 2023/08/input.txt
 create mode 100644 2023/08/part1/cmd/main.go
 create mode 100644 2023/08/part1/part1/part1.go
 create mode 100644 2023/08/part1/part1/part1_test.go
 create mode 100644 2023/08/part2/cmd/main.go
 create mode 100644 2023/08/part2/example.txt
 create mode 100644 2023/08/part2/part2/part2.go
 create mode 100644 2023/08/part2/part2/part2_test.go

diff --git a/2023/08/example.txt b/2023/08/example.txt
new file mode 100644
index 0000000..7d1b58d
--- /dev/null
+++ b/2023/08/example.txt
@@ -0,0 +1,5 @@
+LLR
+
+AAA = (BBB, BBB)
+BBB = (AAA, ZZZ)
+ZZZ = (ZZZ, ZZZ)
diff --git a/2023/08/go.mod b/2023/08/go.mod
new file mode 100644
index 0000000..0e117d4
--- /dev/null
+++ b/2023/08/go.mod
@@ -0,0 +1,3 @@
+module git.tristans.cloud/tristan/aoc/2023/08
+
+go 1.21.4
diff --git a/2023/08/input.txt b/2023/08/input.txt
new file mode 100644
index 0000000..2cf69b2
--- /dev/null
+++ b/2023/08/input.txt
@@ -0,0 +1,736 @@
+LRLRRRLRRLRRRLRRRLLLLLRRRLRLRRLRLRLRRLRRLRRRLRLRLRRLLRLRRLRRLRRLRRRLLRRRLRRRLRRLRLLLRRLRRRLRLRRLRRRLRRLRLLLRRRLRRLRRLRRRLRRRLRRRLRLRLRLRRRLRRRLLLRRLLRRRLRLRLRRRLRRRLRRLRRRLRLRLLRRRLRLRRLRLRLRRLLLRRRLRRRLRRLRRLRLRRLLRRLRRRLRRRLLRRRLRRLRLLRRLRLRRLLRRRLLLLRRLRRRLRLRRLLRLLRRRLLRRLLRRRLRRRLRRLLRLRLLRRLLRLLLRRRR
+
+FCG = (PLG, GXC)
+PQT = (SQK, GHP)
+NVS = (TPQ, PPB)
+CTR = (SXS, KCV)
+FNM = (KHG, FLD)
+TFH = (CLD, CLD)
+MLQ = (QQL, JVK)
+LQR = (TFH, RRM)
+QKX = (VPR, BHD)
+QQL = (VBD, CPM)
+QST = (HPC, DFJ)
+QBX = (HPH, BTM)
+TVB = (SHJ, GMF)
+HJN = (CGJ, QXT)
+PGV = (RXT, DQP)
+TPN = (TQR, LJR)
+BHV = (TGL, GJH)
+DDF = (XLH, TDQ)
+XMK = (XVN, RJP)
+HXH = (JVM, CVB)
+SFX = (VNH, BFS)
+TRF = (HVJ, DKF)
+GGQ = (NNP, PBL)
+KQX = (VXK, FSF)
+KGL = (QSJ, CBM)
+CLC = (MCJ, JGQ)
+GFF = (JBG, TVR)
+FQT = (CMR, CKG)
+HVJ = (FXB, GBP)
+KTF = (PKG, JCL)
+BLF = (QQL, JVK)
+TVN = (QNN, DPQ)
+KVX = (XNV, CJF)
+GDK = (DVX, RKS)
+CHS = (VCL, PSG)
+LFS = (SNS, NTG)
+NCR = (LFV, DRN)
+NLJ = (RCR, XCF)
+SRD = (LGT, PRS)
+FQC = (GBX, RGQ)
+SJH = (CKG, CMR)
+BQP = (PPP, HGT)
+LJR = (FPV, FQD)
+XTP = (PDT, MTX)
+RDG = (JRL, MFF)
+TFF = (MJQ, MVB)
+NGS = (BDG, KHS)
+GXT = (QSJ, CBM)
+VVD = (HJJ, RNK)
+FRL = (CHS, JNV)
+RQS = (CFM, SKM)
+PRS = (VTG, PRR)
+FCD = (VTN, DJQ)
+PDT = (DST, DST)
+MND = (MFV, GDR)
+GJG = (TMJ, HPJ)
+JSL = (DDN, JPZ)
+DNX = (RHD, LKM)
+BGM = (GFC, JTD)
+BPC = (BMN, BKQ)
+PPB = (BFT, PVH)
+VRK = (TCL, JDS)
+MBT = (CBB, JMM)
+JGQ = (GGQ, DFH)
+BGP = (MGH, VHG)
+GGR = (TCX, QLG)
+NCD = (XDT, SSK)
+TDQ = (VPK, HCF)
+PND = (BKV, THD)
+RJP = (KRV, FQN)
+MFZ = (MRL, MFK)
+TCM = (RMM, CLB)
+SDF = (LTS, RVQ)
+KGS = (CTR, SJQ)
+SFK = (SDF, HPF)
+DJC = (MKF, PNR)
+DPF = (KBH, QQX)
+VQJ = (FVB, XPK)
+CGX = (MLQ, BLF)
+GBS = (DJC, XXQ)
+JSV = (RMR, QFK)
+KHS = (HSQ, PTD)
+MKG = (NLJ, MHQ)
+CXQ = (BDG, KHS)
+RRQ = (FCT, CDT)
+JSK = (LBC, CPT)
+VSM = (RGM, VMR)
+NNP = (SRD, VNQ)
+PRR = (GMX, KQX)
+HNP = (GQJ, DGS)
+PKB = (KBL, HXJ)
+MFS = (RMK, JBX)
+RMM = (MFG, BPS)
+PJM = (SFK, HLG)
+BCH = (SRK, LSG)
+VNQ = (LGT, PRS)
+LTC = (TJX, DQD)
+CKG = (NJG, GMJ)
+RPS = (RXT, DQP)
+CCV = (DPB, JQT)
+JDF = (CFF, XFB)
+LQX = (KBD, TVD)
+VLL = (FMK, LCD)
+RTB = (NTG, SNS)
+FBF = (QTH, QFV)
+CBB = (VJK, BHB)
+BCL = (LJR, TQR)
+JDS = (SPS, HRL)
+HHB = (RHD, LKM)
+PLG = (RBV, NJD)
+RNB = (VMR, RGM)
+CJT = (HJJ, RNK)
+JVK = (VBD, CPM)
+HXJ = (JTL, PFD)
+JGX = (XMV, RHF)
+FGT = (LFR, JSK)
+MJQ = (BVP, BXM)
+DFJ = (MRS, LTC)
+GNG = (DPB, JQT)
+VCA = (XFL, JSV)
+KLR = (LTN, BXH)
+VGN = (DGS, GQJ)
+DVX = (JXX, XSH)
+BTN = (PFP, FRL)
+DTM = (DVD, FCF)
+SVN = (PBF, GXR)
+RCC = (BTB, CBD)
+GMJ = (FCH, CSP)
+DVS = (FSV, TVP)
+RLG = (QML, XGD)
+CVV = (HHB, DNX)
+NRG = (NFG, DNL)
+PQC = (RNG, KTF)
+KGG = (DMD, HQG)
+JNV = (PSG, VCL)
+SNS = (CGX, MBK)
+FRA = (MFK, MRL)
+BPN = (QQX, KBH)
+HCQ = (XMK, JDV)
+RDD = (JGB, VHQ)
+HTF = (VLX, RQV)
+XFH = (DPH, RGG)
+LBC = (JTV, DTK)
+RBP = (LLL, MND)
+GDV = (SHJ, GMF)
+MJV = (JGV, JGV)
+BQR = (FPK, HSS)
+XDT = (BTN, FXF)
+NGM = (NTF, BDN)
+HPP = (FQX, FGT)
+JPC = (JHC, PPH)
+GCV = (CCS, TCM)
+VHQ = (PBR, GVP)
+MDD = (GGR, LXC)
+HSQ = (CKV, KGS)
+PPP = (DLP, HPP)
+SNA = (BKK, FNM)
+VLS = (DPH, RGG)
+JGB = (PBR, GVP)
+FPK = (RGS, XKQ)
+LVB = (XMC, MNX)
+FDP = (PFR, GFP)
+NKS = (PCH, TJR)
+VLX = (TLH, KFD)
+DLT = (HGV, XJR)
+MBK = (MLQ, BLF)
+RNG = (PKG, PKG)
+BKV = (DXB, GDQ)
+LLL = (GDR, MFV)
+LNN = (MVG, CKJ)
+FXH = (TCM, CCS)
+TPQ = (BFT, PVH)
+MRH = (CJR, TFF)
+MVG = (BQP, DXC)
+LLN = (VTN, DJQ)
+MLS = (THD, BKV)
+HFR = (HNP, VGN)
+XNF = (PVL, TPD)
+JQT = (DVS, PST)
+LNH = (FHQ, SPG)
+MBR = (LQR, VPM)
+KNV = (SKG, CKF)
+JCV = (XMV, RHF)
+JQJ = (TCQ, BGM)
+SQF = (NRG, HDK)
+FHF = (NNQ, PKB)
+BDG = (HSQ, PTD)
+PBT = (JRL, MFF)
+LNS = (TCQ, BGM)
+PTD = (CKV, KGS)
+LHX = (GRM, XJQ)
+NXN = (SFX, NQC)
+VSZ = (XFS, DHL)
+DPG = (JTS, VBG)
+QTH = (DLT, JNS)
+PMG = (TCL, JDS)
+CCD = (FCG, GPJ)
+SSP = (HTF, MNV)
+STX = (HPC, DFJ)
+KSR = (GKB, TSX)
+KDP = (XGR, LLK)
+NNQ = (HXJ, KBL)
+TPM = (PDT, PDT)
+SJQ = (KCV, SXS)
+PJH = (BPN, DPF)
+JBG = (FPN, XBP)
+XQQ = (TLR, TLR)
+XJL = (FTX, DXR)
+RXT = (RMD, KVX)
+CLD = (NTK, NTK)
+SDM = (MRH, TGC)
+RQV = (TLH, KFD)
+NDL = (VDT, VDT)
+CCT = (QFV, QTH)
+LXL = (VGH, FRM)
+TJR = (KGL, GXT)
+VBD = (RSG, NCR)
+XBG = (BHV, NPX)
+JTS = (QDH, CST)
+BFT = (JDF, MXT)
+XLH = (HCF, VPK)
+XBP = (GCV, FXH)
+JPF = (RNG, KTF)
+HHN = (TMJ, HPJ)
+FSV = (GMC, HJN)
+XFB = (JRN, RDD)
+JPZ = (FNM, BKK)
+BBP = (KDP, KKM)
+TGL = (TGX, XRF)
+JCL = (JDT, JDP)
+RRS = (XPK, FVB)
+NSB = (MCJ, JGQ)
+DMD = (GFG, FDP)
+FFX = (VBG, JTS)
+XRF = (XFH, VLS)
+LSG = (MFX, CVC)
+BJZ = (FNK, QKX)
+DLP = (FGT, FQX)
+JDV = (RJP, XVN)
+HDK = (DNL, NFG)
+GQP = (NQC, SFX)
+NFG = (PMG, VRK)
+CSP = (GDP, NDV)
+BKK = (FLD, KHG)
+SCK = (KKM, KDP)
+DXR = (SCK, BBP)
+JVG = (PBT, RDG)
+GSV = (QTK, JNT)
+QXT = (BPC, NXQ)
+GDQ = (LFJ, KSF)
+NHG = (VVD, CJT)
+DHL = (LNH, RPP)
+NFS = (JGV, MFZ)
+FCS = (BTB, CBD)
+JGH = (VHP, RMG)
+VQC = (GDC, VKQ)
+PCH = (KGL, GXT)
+LGT = (PRR, VTG)
+PFV = (QCC, VSZ)
+QQX = (FTV, LBD)
+QFV = (JNS, DLT)
+SCN = (RVX, RBP)
+QRH = (BCL, TPN)
+GBX = (VRH, CPG)
+NTF = (RBK, RRN)
+NDQ = (MBR, RDL)
+KCP = (GFF, DDG)
+JBX = (JPF, PQC)
+GMF = (KLR, VTD)
+JVM = (TXL, XTR)
+BLC = (MNJ, VFF)
+VTH = (TGC, MRH)
+JHB = (BPN, DPF)
+TMJ = (PND, MLS)
+PFH = (HSS, FPK)
+TDG = (KJV, QTR)
+TGC = (TFF, CJR)
+TVD = (CCT, FBF)
+TKX = (FCT, CDT)
+BKS = (HLG, SFK)
+CGJ = (NXQ, BPC)
+QHN = (SKG, CKF)
+CVJ = (DMD, HQG)
+BMN = (JLD, NHG)
+GRP = (NDL, NDL)
+FTX = (BBP, SCK)
+DXC = (PPP, HGT)
+NJD = (XSP, KSC)
+CST = (RRQ, TKX)
+SVH = (JHC, PPH)
+JTV = (GCM, RLG)
+BXJ = (DVD, FCF)
+HCV = (XDT, SSK)
+RDL = (LQR, VPM)
+PSG = (MFS, MFC)
+MVQ = (DRH, QBX)
+XCF = (FVM, VVS)
+DXB = (KSF, LFJ)
+GHN = (JGH, FGR)
+KLJ = (LSL, QJJ)
+DFH = (NNP, PBL)
+CDL = (XBG, FGD)
+VFF = (KGR, FCJ)
+KBD = (FBF, CCT)
+NJL = (JGH, FGR)
+VHG = (MBT, XPT)
+GKB = (GJB, JKK)
+KQP = (CXQ, NGS)
+VSX = (GDC, VKQ)
+RHX = (CDL, RGD)
+NTK = (QCC, QCC)
+KFD = (SKK, HGC)
+ZZZ = (VFF, MNJ)
+JLK = (HPL, MDF)
+BTB = (PFT, CCD)
+HQG = (GFG, FDP)
+FVM = (VHC, NGC)
+CVD = (NQP, FXM)
+VGH = (CCV, GNG)
+LSV = (NLJ, MHQ)
+PVX = (KGG, CVJ)
+THL = (NJL, GHN)
+KKM = (XGR, LLK)
+XGR = (LRT, DFP)
+RRN = (TPM, XTP)
+GBP = (GTD, QSL)
+KJV = (MDD, QKK)
+TBN = (TLV, JLK)
+LDG = (TPN, BCL)
+JLD = (CJT, VVD)
+DKN = (CXQ, NGS)
+DGV = (HCV, NCD)
+HRL = (QHN, KNV)
+RRM = (CLD, TQC)
+RJS = (MGH, VHG)
+MFF = (QTV, GJF)
+PNR = (BTT, TVN)
+JKK = (RVV, XPG)
+XKQ = (PQT, FJD)
+RMK = (JPF, PQC)
+LCD = (RJS, BGP)
+QTK = (GJG, HHN)
+TBK = (CVB, JVM)
+VTG = (GMX, KQX)
+GFG = (PFR, GFP)
+RSG = (LFV, LFV)
+VCS = (PCH, TJR)
+LJC = (QXF, XQR)
+QHD = (XFM, LQM)
+TSD = (PPB, TPQ)
+RHP = (MXX, MTC)
+GLZ = (JSV, XFL)
+NXD = (VQJ, RRS)
+MTJ = (QJJ, LSL)
+XQH = (NQP, FXM)
+GLK = (XCX, NDQ)
+LSL = (NVS, TSD)
+QGC = (LHX, LHT)
+RHF = (GHM, TBT)
+TQR = (FQD, FPV)
+QSC = (XCX, NDQ)
+RKS = (JXX, XSH)
+HGC = (DCG, GDD)
+XNV = (VQG, PVR)
+CCS = (RMM, CLB)
+RVR = (SJH, FQT)
+JCM = (BKS, PJM)
+GHM = (FCS, RCC)
+PFT = (FCG, GPJ)
+XDJ = (DTS, JVG)
+RVQ = (FQC, PLV)
+LQM = (RMH, XDJ)
+CKV = (SJQ, CTR)
+HPH = (TLX, RQG)
+MTC = (FFX, DPG)
+CKJ = (BQP, DXC)
+MXT = (CFF, XFB)
+VHT = (MQG, GTH)
+QJJ = (TSD, NVS)
+HNA = (DHL, XFS)
+PVS = (TDG, SLR)
+FPV = (GMD, GTK)
+DDG = (TVR, JBG)
+BHD = (LLN, FCD)
+LDF = (NNQ, PKB)
+CKF = (XXD, MJL)
+TJF = (NRG, HDK)
+JDT = (BLC, BLC)
+GXR = (MGP, FNX)
+BPS = (XNF, MXR)
+FXB = (QSL, GTD)
+TLH = (HGC, SKK)
+PKV = (NCD, HCV)
+CBM = (NXD, GTR)
+JNT = (GJG, HHN)
+TCQ = (JTD, GFC)
+MRS = (TJX, DQD)
+FBP = (VGN, HNP)
+HPC = (MRS, LTC)
+TJX = (PKV, DGV)
+LFV = (MJV, MJV)
+XVM = (SQF, TJF)
+VVF = (PGV, RPS)
+XPG = (DGF, DJK)
+RGD = (XBG, FGD)
+GMC = (QXT, CGJ)
+CFM = (VVF, QPN)
+SPS = (QHN, KNV)
+JTL = (MNM, PTS)
+MNM = (SXH, BDT)
+RGG = (LNS, JQJ)
+FPN = (GCV, FXH)
+XJR = (KCP, HJS)
+FQN = (LQX, MVF)
+CFF = (JRN, RDD)
+KSC = (QGH, LPG)
+BHB = (DMR, JTX)
+TBT = (RCC, FCS)
+SKM = (VVF, QPN)
+PVR = (PVS, PQQ)
+QDN = (XMK, JDV)
+JXX = (STX, QST)
+TXL = (LKK, NSD)
+XTR = (NSD, LKK)
+VKQ = (HXH, TBK)
+FQX = (JSK, LFR)
+FRM = (CCV, GNG)
+AAA = (MNJ, VFF)
+BKQ = (NHG, JLD)
+DPQ = (MGT, LXL)
+KXL = (VQC, VSX)
+RVV = (DGF, DGF)
+TQC = (NTK, PFV)
+NPX = (GJH, TGL)
+MGH = (XPT, MBT)
+VMR = (HGG, RHX)
+DCG = (CLF, GBS)
+CVB = (TXL, XTR)
+HPJ = (MLS, PND)
+NNR = (GDV, TVB)
+RVX = (MND, LLL)
+BVL = (SKM, CFM)
+RFS = (XQR, QXF)
+TVR = (XBP, FPN)
+VTD = (LTN, BXH)
+GPC = (XHK, BMP)
+QGH = (RNB, VSM)
+GDR = (PJH, JHB)
+RMG = (KXL, TBV)
+PST = (TVP, FSV)
+GPJ = (GXC, PLG)
+LBD = (KTN, GVD)
+LLK = (LRT, DFP)
+KGR = (MVQ, LKQ)
+MQG = (XJL, NHN)
+QXF = (NSH, STD)
+GMD = (PFH, BQR)
+BFS = (KGN, QHD)
+RCR = (FVM, VVS)
+RPP = (SPG, FHQ)
+FMK = (BGP, RJS)
+FGR = (RMG, VHP)
+SXH = (RTB, LFS)
+GVD = (GRP, TCF)
+TGX = (XFH, VLS)
+JTD = (NCJ, TBN)
+MFV = (PJH, JHB)
+HGV = (KCP, HJS)
+HSS = (XKQ, RGS)
+PTS = (BDT, SXH)
+XSP = (LPG, QGH)
+TCX = (PVX, XVF)
+LKQ = (QBX, DRH)
+FSF = (LSV, MKG)
+XJQ = (RHP, NXM)
+NHN = (FTX, DXR)
+GQJ = (KQP, DKN)
+TLX = (XQH, CVD)
+HJJ = (VCS, NKS)
+GTR = (VQJ, RRS)
+RHD = (FJR, RVR)
+VXK = (LSV, MKG)
+MNX = (HBT, CLM)
+PPH = (LVB, MRB)
+GDP = (LCB, GDK)
+DJQ = (BVL, RQS)
+FCJ = (MVQ, LKQ)
+VHC = (VHT, BSP)
+PVL = (PPR, SSP)
+LCB = (DVX, RKS)
+PFD = (MNM, PTS)
+MVF = (KBD, TVD)
+DJK = (XQQ, TVF)
+QSL = (GSV, KCF)
+NXM = (MXX, MTC)
+TJT = (CKJ, MVG)
+GTH = (NHN, XJL)
+SQK = (SPP, JCM)
+DPH = (LNS, JQJ)
+QPN = (RPS, PGV)
+SRK = (MFX, CVC)
+RFJ = (GKB, TSX)
+QLG = (XVF, PVX)
+QTV = (HLR, VLL)
+PLV = (GBX, RGQ)
+GHR = (SVH, JPC)
+XMV = (GHM, TBT)
+PKG = (JDT, JDT)
+MGP = (RPG, QGC)
+QDH = (TKX, RRQ)
+HJR = (RBP, RVX)
+NDV = (LCB, GDK)
+KRV = (MVF, LQX)
+NTM = (TJF, SQF)
+NXQ = (BKQ, BMN)
+HFF = (VDT, JSL)
+SLR = (QTR, KJV)
+DDN = (BKK, FNM)
+LHT = (XJQ, GRM)
+DST = (QKX, FNK)
+PPR = (MNV, HTF)
+JHC = (LVB, MRB)
+MXX = (DPG, FFX)
+VDT = (DDN, DDN)
+XMC = (HBT, CLM)
+TPD = (SSP, PPR)
+BSP = (GTH, MQG)
+NJG = (FCH, CSP)
+MGT = (VGH, FRM)
+NCJ = (TLV, JLK)
+VCN = (NSB, CLC)
+CBD = (CCD, PFT)
+NQB = (XHK, BMP)
+MFG = (XNF, MXR)
+XFL = (RMR, QFK)
+KCF = (JNT, QTK)
+JTX = (MKB, NGM)
+NTG = (MBK, CGX)
+XCX = (MBR, RDL)
+HGT = (HPP, DLP)
+FJD = (GHP, SQK)
+FXF = (PFP, FRL)
+GFC = (TBN, NCJ)
+NSD = (HFR, FBP)
+HJS = (DDG, GFF)
+MNV = (RQV, VLX)
+VRH = (MTJ, KLJ)
+GJH = (XRF, TGX)
+JMM = (BHB, VJK)
+BDN = (RBK, RRN)
+JDP = (BLC, ZZZ)
+GJK = (HHB, DNX)
+TCF = (NDL, HFF)
+JGV = (MFK, MRL)
+LPG = (VSM, RNB)
+KBH = (FTV, LBD)
+CDT = (NXN, GQP)
+QKK = (GGR, LXC)
+RMD = (CJF, XNV)
+MKF = (TVN, BTT)
+RBV = (XSP, KSC)
+RMH = (JVG, DTS)
+RPG = (LHT, LHX)
+FCH = (NDV, GDP)
+HPF = (LTS, RVQ)
+DRH = (BTM, HPH)
+PFR = (RJR, DBR)
+RJR = (SVN, VXJ)
+MFK = (HCQ, QDN)
+RGQ = (VRH, CPG)
+BSX = (GHN, NJL)
+TLR = (XFL, JSV)
+KCV = (NQF, BCH)
+PJC = (JGX, JCV)
+XVF = (KGG, CVJ)
+DKF = (GBP, FXB)
+SKG = (XXD, MJL)
+MVB = (BVP, BXM)
+MJL = (GJK, CVV)
+NSH = (VCN, VXF)
+DVD = (BCQ, PJC)
+VPR = (LLN, FCD)
+HLG = (SDF, HPF)
+FLD = (BXJ, DTM)
+SKK = (GDD, DCG)
+NQF = (SRK, LSG)
+QNN = (LXL, MGT)
+DMR = (MKB, NGM)
+FVB = (NNR, PBH)
+MXR = (TPD, PVL)
+CLF = (DJC, XXQ)
+FCT = (GQP, NXN)
+RPA = (QKX, FNK)
+MHQ = (XCF, RCR)
+XPK = (PBH, NNR)
+RBK = (TPM, TPM)
+LVX = (TDQ, XLH)
+RMR = (SDM, VTH)
+PBF = (FNX, MGP)
+GCM = (QML, XGD)
+KSF = (BSX, THL)
+XXD = (GJK, CVV)
+DBR = (VXJ, SVN)
+THD = (DXB, GDQ)
+LFR = (LBC, CPT)
+LKK = (FBP, HFR)
+GFP = (RJR, DBR)
+RNK = (VCS, NKS)
+PBL = (VNQ, SRD)
+FXM = (DDF, LVX)
+KGN = (XFM, LQM)
+VPK = (GPC, NQB)
+JNS = (HGV, XJR)
+GTK = (BQR, PFH)
+FTV = (KTN, GVD)
+MCJ = (DFH, GGQ)
+MDF = (LJC, RFS)
+JRL = (GJF, QTV)
+MFC = (RMK, JBX)
+VVS = (NGC, VHC)
+TVF = (TLR, GLZ)
+FCF = (BCQ, PJC)
+SXS = (NQF, BCH)
+GDC = (HXH, TBK)
+XXQ = (MKF, PNR)
+SPG = (GMR, TRF)
+GVP = (LNN, TJT)
+PQQ = (SLR, TDG)
+HBT = (QRH, LDG)
+XGD = (KSR, RFJ)
+QML = (KSR, RFJ)
+LKM = (RVR, FJR)
+DRN = (MJV, NFS)
+MKB = (NTF, BDN)
+GHP = (JCM, SPP)
+XPT = (JMM, CBB)
+KTN = (GRP, TCF)
+LRT = (SCN, HJR)
+BVP = (QKT, GHR)
+FNK = (VPR, BHD)
+GRM = (RHP, NXM)
+BXM = (QKT, GHR)
+CPM = (RSG, NCR)
+MRL = (HCQ, QDN)
+VQG = (PVS, PQQ)
+STD = (VXF, VCN)
+DQD = (DGV, PKV)
+TSX = (GJB, JKK)
+GTD = (KCF, GSV)
+FNX = (RPG, QGC)
+BXH = (LDF, FHF)
+CVC = (GLK, QSC)
+TLV = (MDF, HPL)
+DNL = (PMG, VRK)
+FQD = (GTK, GMD)
+CPG = (MTJ, KLJ)
+SPP = (BKS, PJM)
+GJB = (RVV, XPG)
+SSK = (FXF, BTN)
+PVH = (MXT, JDF)
+XVN = (FQN, KRV)
+LFJ = (BSX, THL)
+VNH = (KGN, QHD)
+HLR = (FMK, LCD)
+KBL = (JTL, PFD)
+BTM = (TLX, RQG)
+DPB = (PST, DVS)
+LTS = (FQC, PLV)
+RQG = (CVD, XQH)
+PBR = (LNN, TJT)
+CLB = (MFG, BPS)
+XFM = (RMH, XDJ)
+CJR = (MJQ, MVB)
+VTN = (BVL, RQS)
+VBG = (QDH, CST)
+GMR = (HVJ, DKF)
+KHG = (BXJ, DTM)
+GDD = (CLF, GBS)
+QTR = (QKK, MDD)
+VCL = (MFS, MFC)
+TBV = (VQC, VSX)
+BCQ = (JCV, JGX)
+NGC = (VHT, BSP)
+GJF = (HLR, VLL)
+FGD = (NPX, BHV)
+RGS = (PQT, FJD)
+VXJ = (PBF, GXR)
+FJR = (SJH, FQT)
+VXF = (CLC, NSB)
+HCF = (GPC, NQB)
+DTK = (RLG, GCM)
+MRB = (XMC, MNX)
+MFX = (QSC, GLK)
+GMX = (VXK, FSF)
+BDT = (LFS, RTB)
+JRN = (VHQ, JGB)
+SHJ = (KLR, VTD)
+TCL = (HRL, SPS)
+CPT = (DTK, JTV)
+VPM = (TFH, RRM)
+DQP = (RMD, KVX)
+CJF = (VQG, PVR)
+PBH = (GDV, TVB)
+LXC = (QLG, TCX)
+FHQ = (GMR, TRF)
+BMP = (XVM, NTM)
+XHK = (NTM, XVM)
+NQP = (LVX, DDF)
+TVP = (GMC, HJN)
+XFS = (LNH, RPP)
+RGM = (RHX, HGG)
+NQC = (VNH, BFS)
+GXC = (RBV, NJD)
+XQR = (NSH, STD)
+BTT = (QNN, DPQ)
+PFP = (CHS, JNV)
+QKT = (SVH, JPC)
+QCC = (DHL, XFS)
+LTN = (LDF, FHF)
+CMR = (GMJ, NJG)
+DGF = (XQQ, XQQ)
+MTX = (DST, BJZ)
+QSJ = (GTR, NXD)
+DFP = (HJR, SCN)
+MNJ = (FCJ, KGR)
+XSH = (QST, STX)
+VHP = (KXL, TBV)
+DGS = (KQP, DKN)
+HPL = (LJC, RFS)
+DTS = (RDG, PBT)
+VJK = (JTX, DMR)
+CLM = (LDG, QRH)
+QFK = (SDM, VTH)
+HGG = (CDL, RGD)
diff --git a/2023/08/part1/cmd/main.go b/2023/08/part1/cmd/main.go
new file mode 100644
index 0000000..f22a28a
--- /dev/null
+++ b/2023/08/part1/cmd/main.go
@@ -0,0 +1,39 @@
+
+package main
+
+import (
+	"bufio"
+	"os"
+	"fmt"
+	"git.tristans.cloud/tristan/aoc/2023/08/part1/part1"
+	"strings"
+)
+
+func main() {
+	reader := bufio.NewReader(os.Stdin);
+	ins, err := reader.ReadString('\n');
+	if err != nil {
+		fmt.Println("Halted too soon!")
+		return
+	}
+	ins = strings.Trim(ins, "\n")
+	_, err = reader.ReadString('\n');
+	if err != nil {
+		fmt.Println("Halted too soon!")
+		return
+	}
+	net := part1.Network{}
+	for {
+		line, err := reader.ReadString('\n');
+		if err != nil {
+			break
+		}
+		part1.AddToNet(net, line)
+	}
+	steps, err := part1.StepsToSolve(ins, net)
+	if (err != nil) {
+		fmt.Println(err)
+	}
+	fmt.Println(steps)
+}
+
diff --git a/2023/08/part1/part1/part1.go b/2023/08/part1/part1/part1.go
new file mode 100644
index 0000000..d20eded
--- /dev/null
+++ b/2023/08/part1/part1/part1.go
@@ -0,0 +1,55 @@
+
+package part1
+
+import (
+	"regexp"
+	"errors"
+	"fmt"
+)
+
+type Network = map[string][2]string
+const start = "AAA"
+const Goal = "ZZZ"
+
+func GetValues(in string) (src, left, right string, err error) {
+	re := regexp.MustCompile(`[A-Z1-9]{3}`)
+	out := re.FindAllString(in, -1)
+	if (len(out) != 3) {
+		return "", "", "", errors.New("must be three values")
+	}
+	src = out[0];
+	left = out[1];
+	right = out[2];
+	return
+}
+
+func AddToNet(net Network, line string) error {
+	src, left, right, err := GetValues(line)
+	if err != nil {
+		return err
+	}
+	net[src] = [2]string{left, right}
+	return nil
+}
+
+func StepsToSolve(ins string, net Network) (int, error) {
+	current := start;
+	steps := 0;
+	i := 0
+	for {
+		if current == Goal {
+			return steps, nil
+		}
+		steps ++
+		switch ins[i  % len(ins)] {
+		case 'L':
+			current = net[current][0]
+		case 'R':
+			current = net[current][1]
+		default:
+			return 0, fmt.Errorf("instructions unclear! Must only be 'R' and 'L', but got %v at [%v]", ins[i], i)
+		}
+		i++
+	}
+}
+
diff --git a/2023/08/part1/part1/part1_test.go b/2023/08/part1/part1/part1_test.go
new file mode 100644
index 0000000..335e065
--- /dev/null
+++ b/2023/08/part1/part1/part1_test.go
@@ -0,0 +1,58 @@
+
+package part1
+
+import (
+	"testing"
+	"reflect"
+)
+
+func TestGetValues(t *testing.T) {
+	src, left, right, err := GetValues("AAA = (BBB, CCC)\n")
+	equal(t, err, nil)
+	equal(t, src, "AAA")
+	equal(t, left, "BBB")
+	equal(t, right, "CCC")
+}
+
+func TestAddToNet(t *testing.T) {
+	net := Network{}
+	err := AddToNet(net, "AAA = (BBB, CCC)")
+	equal(t, err, nil)
+	equal(t, net["AAA"], [2]string{"BBB","CCC"})
+}
+
+func TestStepsToSolveWithBadDirections(t *testing.T) {
+	_, err := StepsToSolve("AB", Network{})
+	if err == nil {
+		t.Errorf("wanted an error")
+	}
+}
+
+func TestStepsToSolve(t *testing.T) {
+	net := Network{
+		"AAA": [2]string{"BBB","CCC"},
+		"CCC": [2]string{"ZZZ","AAA"},
+		"ZZZ": [2]string{"ZZZ","ZZZ"},
+	}
+	steps, err := StepsToSolve("RL", net)
+	equal(t, err, nil)
+	equal(t, steps, 2)
+}
+
+func TestStepsToSolveWithLoop(t *testing.T) {
+	net := Network{
+		"AAA": [2]string{"BBB","BBB"},
+		"BBB": [2]string{"AAA","ZZZ"},
+		"ZZZ": [2]string{"ZZZ","ZZZ"},
+	}
+	steps, err := StepsToSolve("LLR", net)
+	equal(t, err, nil)
+	equal(t, steps, 6)
+}
+
+func equal(t *testing.T, actual any, target any) {
+	if !reflect.DeepEqual(actual, target) {
+		t.Errorf("expected '%v', got '%v'", target, actual)
+	}
+}
+
diff --git a/2023/08/part2/cmd/main.go b/2023/08/part2/cmd/main.go
new file mode 100644
index 0000000..076590f
--- /dev/null
+++ b/2023/08/part2/cmd/main.go
@@ -0,0 +1,43 @@
+
+package main
+
+import (
+	"bufio"
+	"os"
+	"fmt"
+	"git.tristans.cloud/tristan/aoc/2023/08/part2/part2"
+	"git.tristans.cloud/tristan/aoc/2023/08/part1/part1"
+	"strings"
+)
+
+func main() {
+	reader := bufio.NewReader(os.Stdin);
+	ins, err := reader.ReadString('\n');
+	if err != nil {
+		fmt.Println("Halted too soon!")
+		return
+	}
+	ins = strings.Trim(ins, "\n")
+	_, err = reader.ReadString('\n');
+	if err != nil {
+		fmt.Println("Halted too soon!")
+		return
+	}
+	net := part1.Network{}
+	for {
+		line, err := reader.ReadString('\n');
+		if err != nil {
+			break
+		}
+		err = part1.AddToNet(net, line)
+		if err != nil {
+			fmt.Println(err, line, net)
+		}
+	}
+	steps, err := part2.StepsToSolveAll(ins, net)
+	if (err != nil) {
+		fmt.Println(err)
+	}
+	fmt.Println(steps)
+}
+
diff --git a/2023/08/part2/example.txt b/2023/08/part2/example.txt
new file mode 100644
index 0000000..5b3fa58
--- /dev/null
+++ b/2023/08/part2/example.txt
@@ -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)
diff --git a/2023/08/part2/part2/part2.go b/2023/08/part2/part2/part2.go
new file mode 100644
index 0000000..59a781c
--- /dev/null
+++ b/2023/08/part2/part2/part2.go
@@ -0,0 +1,84 @@
+
+package part2
+
+import (
+	"fmt"
+	"git.tristans.cloud/tristan/aoc/2023/08/part1/part1"
+)
+
+type Network = part1.Network
+const start = 'A'
+const goal = 'Z'
+
+func StepsToSolveAll(ins string, net Network) (int, error) {
+	current := GetStarts(net)
+	res := 1
+	for _,start := range current {
+		_, steps, err := StepsToSolve(ins, net, start)
+		if err != nil {
+			return 0, err
+		}
+		// I expected I'd have to do a bit more than that!
+		res = LCM(res, steps)
+	}
+
+	return res, nil
+}
+
+func GetStarts(net Network) (current []string) {
+	for key := range net {
+		if key[2] == start {
+			current = append(current, key)
+		}
+	}
+	return
+}
+
+func IsDone(state []string) bool {
+	for _,key := range state {
+		if key[2] != goal {
+			return false
+		}
+	}
+	return true
+}
+
+func StepsToSolve(ins string, net Network, start string) (int, int, error) {
+	current := start
+	steps := 0;
+	history := []string{current}
+	for {
+		switch ins[steps  % len(ins)] {
+		case 'L':
+			current = net[current][0]
+		case 'R':
+			current = net[current][1]
+		default:
+			return 0, 0, fmt.Errorf("instructions unclear! Must only be 'R' and 'L', but got '%c' at [%v]", ins[steps], steps)
+		}
+		steps ++
+		for step, state := range history {
+			if state == current && step == steps % len(ins) {
+				return step, steps-step, nil
+			}
+		}
+		history = append(history, current)
+	}
+
+}
+
+// greatest common divisor (GCD) via Euclidean algorithm
+func GCD(a, b int) int {
+	for b != 0 {
+		t := b
+		b = a % b
+		a = t
+	}
+	return a
+}
+
+// find Least Common Multiple (LCM) via GCD
+func LCM(a, b int, integers ...int) int {
+	return a * b / GCD(a, b)
+}
+
diff --git a/2023/08/part2/part2/part2_test.go b/2023/08/part2/part2/part2_test.go
new file mode 100644
index 0000000..3db6fbb
--- /dev/null
+++ b/2023/08/part2/part2/part2_test.go
@@ -0,0 +1,81 @@
+
+package part2
+
+import (
+	"testing"
+	"reflect"
+	"sort"
+)
+
+func TestGetStarts(t *testing.T) {
+	net := Network{
+		"11A": [2]string{"11B", "XXX"},
+		"11B": [2]string{"XXX", "11Z"},
+		"11Z": [2]string{"11B", "XXX"},
+		"22A": [2]string{"22B", "XXX"},
+		"22B": [2]string{"22C", "22C"},
+		"22C": [2]string{"22Z", "22Z"},
+		"22Z": [2]string{"22B", "22B"},
+		"XXX": [2]string{"XXX", "XXX"},
+	}
+	starts := GetStarts(net);
+	sort.Strings(starts)
+	equal(t, starts, []string{"11A", "22A"})
+}
+
+func TestIsDone(t *testing.T) {
+	equal(t, IsDone([]string{"ABZ", "11Z"}), true)
+	equal(t, IsDone([]string{"ABC", "11Z"}), false)
+}
+
+func TestStepsToSolveAll(t *testing.T) {
+	net := Network{
+		"11A": [2]string{"11B", "XXX"},
+		"11B": [2]string{"XXX", "11Z"},
+		"11Z": [2]string{"11B", "XXX"},
+		"22A": [2]string{"22B", "XXX"},
+		"22B": [2]string{"22C", "22C"},
+		"22C": [2]string{"22Z", "22Z"},
+		"22Z": [2]string{"22B", "22B"},
+		"XXX": [2]string{"XXX", "XXX"},
+	}
+	steps, err := StepsToSolveAll("LR", net)
+	equal(t, err, nil)
+	equal(t, steps, 6)
+}
+
+func TestStepsToSolve(t *testing.T) {
+	net := Network{
+		"11A": [2]string{"11B", "XXX"},
+		"11B": [2]string{"XXX", "11Z"},
+		"11Z": [2]string{"11B", "XXX"},
+		"22A": [2]string{"22B", "XXX"},
+		"22B": [2]string{"22C", "22C"},
+		"22C": [2]string{"22Z", "22Z"},
+		"22Z": [2]string{"22B", "22B"},
+		"XXX": [2]string{"XXX", "XXX"},
+	}
+	start, steps, err := StepsToSolve("RL", net, "11B")
+	equal(t, err, nil)
+	equal(t, start, 0)
+	equal(t, steps, 2)
+	start, steps, err = StepsToSolve("LR", net, "11A")
+	equal(t, err, nil)
+	equal(t, start, 1)
+	equal(t, steps, 2)
+	start, steps, err = StepsToSolve("LR", net, "22A")
+	equal(t, err, nil)
+	equal(t, start, 1)
+	equal(t, steps, 6)
+}
+
+func TestLCM(t *testing.T) {
+	equal(t, LCM(2,3), 6)
+}
+
+func equal(t *testing.T, actual any, target any) {
+	if !reflect.DeepEqual(actual, target) {
+		t.Errorf("expected '%v', got '%v'", target, actual)
+	}
+}
+