diff --git a/AdventOfCode.lpi b/AdventOfCode.lpi
index f6ed2ae..685f159 100644
--- a/AdventOfCode.lpi
+++ b/AdventOfCode.lpi
@@ -125,6 +125,10 @@
         
         
       
+      
+        
+        
+      
     
   
   
diff --git a/AdventOfCode.lpr b/AdventOfCode.lpr
index 039cf72..4f321b0 100644
--- a/AdventOfCode.lpr
+++ b/AdventOfCode.lpr
@@ -27,7 +27,7 @@ uses
   UTrebuchet, UCubeConundrum, UGearRatios, UScratchcards, UGiveSeedFertilizer, UWaitForIt, UCamelCards,
   UHauntedWasteland, UNumberTheory, UMirageMaintenance, UPipeMaze, UCosmicExpansion, UHotSprings, UPointOfIncidence,
   UParabolicReflectorDish, ULensLibrary, UFloorWillBeLava, UClumsyCrucible, ULavaductLagoon, UAplenty,
-  UPulsePropagation, UStepCounter;
+  UPulsePropagation, UStepCounter, USandSlabs;
 
 type
 
@@ -90,6 +90,7 @@ begin
       19: engine.RunAndFree(TAplenty.Create);
       20: engine.RunAndFree(TPulsePropagation.Create);
       21: engine.RunAndFree(TStepCounter.Create);
+      22: engine.RunAndFree(TSandSlabs.Create);
     end;
 
   engine.Free;
diff --git a/solvers/USandSlabs.pas b/solvers/USandSlabs.pas
new file mode 100644
index 0000000..959ee03
--- /dev/null
+++ b/solvers/USandSlabs.pas
@@ -0,0 +1,218 @@
+{
+  Solutions to the Advent Of Code.
+  Copyright (C) 2023  Stefan Müller
+
+  This program is free software: you can redistribute it and/or modify it under
+  the terms of the GNU General Public License as published by the Free Software
+  Foundation, either version 3 of the License, or (at your option) any later
+  version.
+
+  This program is distributed in the hope that it will be useful, but WITHOUT
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  this program.  If not, see .
+}
+
+unit USandSlabs;
+
+{$mode ObjFPC}{$H+}
+
+interface
+
+uses
+  Classes, SysUtils, Generics.Collections, Generics.Defaults, USolver;
+
+const
+  CGroundSize = 10;
+
+type
+
+  { TBrick }
+
+  TBrick = class
+  private
+    FX1, FY1, FZ1, FX2, FY2, FZ2: Integer;
+    FIsDisintegratable: Boolean;
+  public
+    property X1: Integer read FX1;
+    property Y1: Integer read FY1;
+    property Z1: Integer read FZ1;
+    property X2: Integer read FX2;
+    property Y2: Integer read FY2;
+    property Z2: Integer read FZ2;
+    property IsDisintegratable: Boolean read FIsDisintegratable write FIsDisintegratable;
+    constructor Create(const AX1, AY1, AZ1, AX2, AY2, AZ2: Integer);
+    constructor Create(const ALine: string);
+    procedure SetZ1(const AValue: Integer);
+  end;
+
+  { TBrickComparer }
+
+  TBrickComparer = class(TInterfacedObject, specialize IComparer)
+  public
+    function Compare(constref ALeft, ARight: TBrick): Integer; overload;
+  end;
+
+  TBricks = specialize TObjectList;
+
+  { TGroundTile }
+
+  TGroundTile = record
+    Height: Integer;
+    TopBrick: TBrick;
+  end;
+
+  TGround = array[0..CGroundSize - 1, 0.. CGroundSize - 1] of TGroundTile;
+
+  { TSandSlabs }
+
+  TSandSlabs = class(TSolver)
+  private
+    FBricks: TBricks;
+    FGround: TGround;
+    procedure InitGround;
+    procedure StackBrick(const ABrick: TBrick);
+  public
+    constructor Create;
+    destructor Destroy; override;
+    procedure ProcessDataLine(const ALine: string); override;
+    procedure Finish; override;
+    function GetDataFileName: string; override;
+    function GetPuzzleName: string; override;
+  end;
+
+implementation
+
+{ TBrick }
+
+constructor TBrick.Create(const AX1, AY1, AZ1, AX2, AY2, AZ2: Integer);
+begin
+  FX1 := AX1;
+  FY1 := AY1;
+  FZ1 := AZ1;
+  FX2 := AX2;
+  FY2 := AY2;
+  FZ2 := AZ2;
+  FIsDisintegratable := True;
+end;
+
+constructor TBrick.Create(const ALine: string);
+var
+  split: TStringArray;
+begin
+  split := ALine.Split([',', '~']);
+  Create(StrToInt(split[0]), StrToInt(split[1]), StrToInt(split[2]), StrToInt(split[3]), StrToInt(split[4]),
+    StrToInt(split[5]));
+end;
+
+procedure TBrick.SetZ1(const AValue: Integer);
+begin
+  Inc(FZ2, AValue - FZ1);
+  FZ1 := AValue;
+end;
+
+{ TBrickComparer }
+
+function TBrickComparer.Compare(constref ALeft, ARight: TBrick): Integer;
+begin
+  Result := ALeft.FZ1 - ARight.FZ1;
+end;
+
+{ TSandSlabs }
+
+procedure TSandSlabs.InitGround;
+var
+  i, j: Integer;
+begin
+  for i := 0 to CGroundSize - 1 do
+    for j := 0 to CGroundSize - 1 do
+    begin
+      FGround[i, j].Height := 0;
+      FGround[i, j].TopBrick := nil;
+    end;
+end;
+
+procedure TSandSlabs.StackBrick(const ABrick: TBrick);
+var
+  supports: TBricks;
+  i, j, max: Integer;
+begin
+  Inc(FPart1);
+
+  // Checks height and supports for this brick.
+  supports := TBricks.Create(False);
+  max := 0;
+  for i := ABrick.X1 to ABrick.X2 do
+    for j := ABrick.Y1 to ABrick.Y2 do
+      if max <= FGround[i, j].Height then
+      begin
+        if max < FGround[i, j].Height then
+        begin
+          max := FGround[i, j].Height;
+          supports.Clear;
+        end;
+        if (FGround[i, j].TopBrick <> nil) and not supports.Contains(FGround[i, j].TopBrick) then
+          supports.Add(FGround[i, j].TopBrick);
+      end;
+
+  // Updates disintegration flag.
+  if supports.Count = 1 then
+  begin
+    if supports[0].IsDisintegratable then
+    begin
+      supports[0].IsDisintegratable := False;
+      Dec(FPart1);
+    end;
+  end;
+  supports.Free;
+
+  // Adjusts height and write brick to ground.
+  ABrick.SetZ1(max + 1);
+  for i := ABrick.X1 to ABrick.X2 do
+    for j := ABrick.Y1 to ABrick.Y2 do
+    begin
+      FGround[i, j].Height := ABrick.Z2;
+      FGround[i, j].TopBrick := ABrick;
+    end;
+end;
+
+constructor TSandSlabs.Create;
+begin
+  FBricks := TBricks.Create(TBrickComparer.Create);
+end;
+
+destructor TSandSlabs.Destroy;
+begin
+  FBricks.Free;
+  inherited Destroy;
+end;
+
+procedure TSandSlabs.ProcessDataLine(const ALine: string);
+begin
+  FBricks.Add(TBrick.Create(ALine));
+end;
+
+procedure TSandSlabs.Finish;
+var
+  brick: TBrick;
+begin
+  FBricks.Sort;
+  InitGround;
+  for brick in FBricks do
+    StackBrick(brick);
+end;
+
+function TSandSlabs.GetDataFileName: string;
+begin
+  Result := 'sand_slabs.txt';
+end;
+
+function TSandSlabs.GetPuzzleName: string;
+begin
+  Result := 'Day 22: Sand Slabs';
+end;
+
+end.
+
diff --git a/tests/AdventOfCodeFPCUnit.lpi b/tests/AdventOfCodeFPCUnit.lpi
index 64daaed..84b736b 100644
--- a/tests/AdventOfCodeFPCUnit.lpi
+++ b/tests/AdventOfCodeFPCUnit.lpi
@@ -128,6 +128,10 @@
         
         
       
+      
+        
+        
+      
     
   
   
diff --git a/tests/AdventOfCodeFPCUnit.lpr b/tests/AdventOfCodeFPCUnit.lpr
index f7c9310..427b53c 100644
--- a/tests/AdventOfCodeFPCUnit.lpr
+++ b/tests/AdventOfCodeFPCUnit.lpr
@@ -8,7 +8,7 @@ uses
   UHauntedWastelandTestCases, UMirageMaintenanceTestCases, UPipeMazeTestCases, UCosmicExpansionTestCases,
   UHotSpringsTestCases, UPointOfIncidenceTestCases, UParabolicReflectorDishTestCases, ULensLibraryTestCases,
   UFloorWillBeLavaTestCases, UClumsyCrucibleTestCases, ULavaductLagoonTestCases, UAplentyTestCases,
-  UPulsePropagationTestCases, UStepCounterTestCases;
+  UPulsePropagationTestCases, UStepCounterTestCases, USandSlabsTestCases;
 
 {$R *.res}
 
diff --git a/tests/USandSlabsTestCases.pas b/tests/USandSlabsTestCases.pas
new file mode 100644
index 0000000..d8add09
--- /dev/null
+++ b/tests/USandSlabsTestCases.pas
@@ -0,0 +1,78 @@
+{
+  Solutions to the Advent Of Code.
+  Copyright (C) 2023  Stefan Müller
+
+  This program is free software: you can redistribute it and/or modify it under
+  the terms of the GNU General Public License as published by the Free Software
+  Foundation, either version 3 of the License, or (at your option) any later
+  version.
+
+  This program is distributed in the hope that it will be useful, but WITHOUT
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  this program.  If not, see .
+}
+
+unit USandSlabsTestCases;
+
+{$mode ObjFPC}{$H+}
+
+interface
+
+uses
+  Classes, SysUtils, fpcunit, testregistry, USolver, UBaseTestCases, USandSlabs;
+
+type
+
+  { TSandSlabsFullDataTestCase }
+
+  TSandSlabsFullDataTestCase = class(TEngineBaseTest)
+  protected
+    function CreateSolver: ISolver; override;
+  published
+    procedure TestPart1;
+  end;
+
+  { TSandSlabsExampleTestCase }
+
+  TSandSlabsExampleTestCase = class(TExampleEngineBaseTest)
+  protected
+    function CreateSolver: ISolver; override;
+  published
+    procedure TestPart1;
+  end;
+
+implementation
+
+{ TSandSlabsFullDataTestCase }
+
+function TSandSlabsFullDataTestCase.CreateSolver: ISolver;
+begin
+  Result := TSandSlabs.Create;
+end;
+
+procedure TSandSlabsFullDataTestCase.TestPart1;
+begin
+  AssertEquals(389, FSolver.GetResultPart1);
+end;
+
+{ TSandSlabsExampleTestCase }
+
+function TSandSlabsExampleTestCase.CreateSolver: ISolver;
+begin
+  Result := TSandSlabs.Create;
+end;
+
+procedure TSandSlabsExampleTestCase.TestPart1;
+begin
+  AssertEquals(5, FSolver.GetResultPart1);
+end;
+
+initialization
+
+  RegisterTest(TSandSlabsFullDataTestCase);
+  RegisterTest(TSandSlabsExampleTestCase);
+end.
+