diff --git a/src/AdventOfCode/Day18.cs b/src/AdventOfCode/Day18.cs
new file mode 100644
index 0000000..0efc5cb
--- /dev/null
+++ b/src/AdventOfCode/Day18.cs
@@ -0,0 +1,111 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using AdventOfCode.Utilities;
+
+namespace AdventOfCode
+{
+ ///
+ /// Solver for Day 18
+ ///
+ public class Day18
+ {
+ public int Part1(string[] input)
+ {
+ HashSet points = new();
+ Point2D current = (0, 0);
+
+ points.Add(current);
+
+ foreach (string line in input)
+ {
+ Bearing direction = line[0] switch
+ {
+ 'U' => Bearing.North,
+ 'D' => Bearing.South,
+ 'R' => Bearing.East,
+ 'L' => Bearing.West,
+ _ => throw new ArgumentOutOfRangeException()
+ };
+
+ int steps = line.Numbers().First();
+
+ foreach (Point2D next in Dig(current, direction, steps))
+ {
+ points.Add(next);
+ current = next;
+ }
+ }
+
+ int minX = points.MinBy(p => p.X).X;
+ int maxX = points.MaxBy(p => p.X).X;
+ int minY = points.MinBy(p => p.Y).Y;
+ int maxY = points.MaxBy(p => p.Y).Y;
+
+ StringBuilder s = new();
+
+ for (int y = minY; y <= maxY; y++)
+ {
+ s.Append($"{y:D4}: ");
+
+ for (int x = minX; x <= maxX; x++)
+ {
+ s.Append(points.Contains((x, y)) ? '#' : '.');
+ }
+
+ s.AppendLine();
+ }
+
+ Debug.WriteLine(s.ToString());
+
+ // from visual inspection, flood fill will work instead of the parity checking style from a previous day
+
+ Point2D point = (Enumerable.Range(minX, maxX - minX).First(x => points.Contains((x, 0)) && !points.Contains((x + 1, 0))) + 1, 0);
+
+ Queue queue = new();
+ queue.Enqueue(point);
+
+ while (queue.Count > 0)
+ {
+ point = queue.Dequeue();
+
+ if (points.Contains(point))
+ {
+ continue;
+ }
+
+ points.Add(point);
+
+ foreach (Point2D next in point.Adjacent4().Where(n => !points.Contains(n)))
+ {
+ queue.Enqueue(next);
+ }
+ }
+
+ return points.Count;
+ }
+
+ public int Part2(string[] input)
+ {
+ foreach (string line in input)
+ {
+ throw new NotImplementedException("Part 2 not implemented");
+ }
+
+ return 0;
+ }
+
+ private static IEnumerable Dig(Point2D start, Bearing direction, int steps)
+ {
+ Point2D current = start;
+
+ for (int i = 0; i < steps; i++)
+ {
+ current = current.Move(direction);
+ yield return current;
+ }
+ }
+ }
+}
diff --git a/src/AdventOfCode/inputs/day18.txt b/src/AdventOfCode/inputs/day18.txt
new file mode 100644
index 0000000..cf56975
Binary files /dev/null and b/src/AdventOfCode/inputs/day18.txt differ
diff --git a/tests/AdventOfCode.Tests/Day18Tests.cs b/tests/AdventOfCode.Tests/Day18Tests.cs
new file mode 100644
index 0000000..c8f8dec
--- /dev/null
+++ b/tests/AdventOfCode.Tests/Day18Tests.cs
@@ -0,0 +1,65 @@
+using System.IO;
+using Xunit;
+using Xunit.Abstractions;
+
+
+namespace AdventOfCode.Tests
+{
+ public class Day18Tests
+ {
+ private readonly ITestOutputHelper output;
+ private readonly Day18 solver;
+
+ public Day18Tests(ITestOutputHelper output)
+ {
+ this.output = output;
+ this.solver = new Day18();
+ }
+
+ private static string[] GetRealInput()
+ {
+ string[] input = File.ReadAllLines("inputs/day18.txt");
+ return input;
+ }
+
+ private static string[] GetSampleInput()
+ {
+ return new string[]
+ {
+
+ };
+ }
+
+ [Fact]
+ public void Part1_RealInput_ProducesCorrectResponse()
+ {
+ var expected = 46394;
+
+ var result = solver.Part1(GetRealInput());
+ output.WriteLine($"Day 18 - Part 1 - {result}");
+
+ Assert.Equal(expected, result);
+ }
+
+ [Fact]
+ public void Part2_SampleInput_ProducesCorrectResponse()
+ {
+ var expected = -1;
+
+ var result = solver.Part2(GetSampleInput());
+
+ Assert.Equal(expected, result);
+ }
+
+ [Fact]
+ public void Part2_RealInput_ProducesCorrectResponse()
+ {
+ var expected = -1;
+
+ var result = solver.Part2(GetRealInput());
+ output.WriteLine($"Day 18 - Part 2 - {result}");
+
+ Assert.Equal(expected, result);
+ }
+ }
+}