diff --git a/2023/25/2023-25.run.xml b/2023/25/2023-25.run.xml
new file mode 100644
index 0000000..de3d754
--- /dev/null
+++ b/2023/25/2023-25.run.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/2023/25/index.test.ts b/2023/25/index.test.ts
new file mode 100644
index 0000000..bdace18
--- /dev/null
+++ b/2023/25/index.test.ts
@@ -0,0 +1,27 @@
+import { describe, expect, it } from 'bun:test';
+import { solveFirst, solveSecond } from './index.ts';
+
+describe('2023-25', () => {
+ it('first', () => {
+ const testInput = `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`;
+ expect(solveFirst(testInput)).toBe(54);
+ });
+ it('second', () => {
+ // TODO: add second input
+ const testInput = ``;
+ // TODO: add second solution
+ expect(solveSecond(testInput)).toBe(0);
+ });
+});
diff --git a/2023/25/index.ts b/2023/25/index.ts
new file mode 100644
index 0000000..c7b3518
--- /dev/null
+++ b/2023/25/index.ts
@@ -0,0 +1,85 @@
+import { multBy } from '../../utils/array.ts';
+import { randomInt } from '../../utils/random.ts';
+
+function trySolve(
+ nodes: { node: string; weight: number }[],
+ edges: [string, string][]
+) {
+ while (nodes.length > 2) {
+ const randEdgeIdx = randomInt(0, edges.length);
+ const [u, v] = edges.splice(randEdgeIdx, 1)[0];
+
+ const vertexU = nodes.find((n) => n.node === u)!;
+ const vertexVIdx = nodes.findIndex((n) => n.node === v);
+ const vertexV = nodes.splice(vertexVIdx, 1)[0];
+
+ const oldNodes = [u, v];
+ const mergedName = `(${u}:${v})`;
+
+ const newEdges = edges
+ .map(
+ (e) =>
+ [
+ oldNodes.includes(e[0]) ? mergedName : e[0],
+ oldNodes.includes(e[1]) ? mergedName : e[1]
+ ] as [string, string]
+ )
+ .filter((e) => e[0] !== e[1]);
+ edges.length = 0;
+ edges.push(...newEdges);
+
+ vertexU.node = mergedName;
+ vertexU.weight += vertexV.weight;
+ }
+ const edgeLength = edges.length;
+
+ const mult = multBy(nodes, (n) => n.weight);
+
+ return { edgeLength, mult };
+}
+
+export function solveFirst(input: string): number {
+ const regex = /^(\w*): (.*)$/gm;
+ const match = input.matchAll(regex);
+
+ const { edges, nodesSet } = Array.from(
+ match,
+ (m) => [m[1], m[2].split(' ')] as [string, string[]]
+ ).reduce(
+ ({ edges, nodesSet }, line) => {
+ nodesSet.add(line[0]);
+ line[1].forEach((n) => {
+ nodesSet.add(n);
+ edges.push([line[0], n]);
+ });
+
+ return { edges, nodesSet };
+ },
+ {
+ edges: [] as [string, string][],
+ nodesSet: new Set()
+ }
+ );
+
+ let edgeLength, mult;
+ let i = 0;
+ do {
+ i++;
+ const result = trySolve(
+ Array.from(nodesSet, (n) => ({ node: n, weight: 1 })),
+ [...edges]
+ );
+ edgeLength = result.edgeLength;
+ mult = result.mult;
+ } while (edgeLength > 3);
+
+ console.log(`That took ${i} tries`);
+ console.log(mult);
+ return mult;
+}
+
+export function solveSecond(input: string): number {
+ // TODO: add code
+
+ return 0;
+}
diff --git a/2023/25/run.ts b/2023/25/run.ts
new file mode 100644
index 0000000..88783cd
--- /dev/null
+++ b/2023/25/run.ts
@@ -0,0 +1,12 @@
+import { join } from 'path';
+import { solveFirst, solveSecond } from './index.ts';
+
+const input = await Bun.file(join(__dirname, 'input.txt')).text();
+
+const firstAnswer = solveFirst(input);
+
+console.log(firstAnswer);
+
+const secondAnswer = solveSecond(input);
+
+console.log(secondAnswer);