diff --git a/src/Game/Knight.php b/src/Game/Knight.php
new file mode 100644
index 0000000..11ea38d
--- /dev/null
+++ b/src/Game/Knight.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace Game;
+
+class Knight extends Piece {
+
+    public function getName(): string {
+        return "Knight";
+    }
+
+    public function getShort(): string {
+        return "N";
+    }
+
+    public function getMoveCandidateMap(FieldBitMap $occupied, FieldBitMap $captureable, FieldBitMap $threatened): FieldBitMap {
+        $result = FieldBitMap::empty();
+
+        for ($i = 0; $i < 8; $i++) {
+            $fileOffset = ($i % 2 + 1) * ($i < 4 ? 1 : -1);
+            $rankOffset = (($i + 1) % 2 + 1) * (($i & 2) == 0 ? 1 : -1);
+
+            $candidate = new Position(
+                $this->position->file + $fileOffset,
+                $this->position->rank + $rankOffset
+            );
+            if ($candidate->isValid() && !$occupied->has($candidate)) {
+                $result->add($candidate);
+            }
+        }
+
+        return $result;
+    }
+}
\ No newline at end of file
diff --git a/tests/Game/KnightTest.php b/tests/Game/KnightTest.php
new file mode 100644
index 0000000..42913bf
--- /dev/null
+++ b/tests/Game/KnightTest.php
@@ -0,0 +1,93 @@
+<?php
+declare(strict_types=1);
+
+namespace Game;
+
+use PHPUnit\Framework\TestCase;
+
+final class KnightTest extends TestCase {
+
+    public function testMoves_unobstructed() {
+        $subject = new Knight(new Position(
+            3, 4
+        ), Side::WHITE);
+
+        $result = $subject->getMoveCandidateMap(FieldBitMap::empty(), FieldBitMap::empty(), FieldBitMap::empty());
+
+        $this->assertTrue(
+            $result->equals(new FieldBitMap([
+                new Position(4, 2),
+                new Position(4, 6),
+                new Position(2, 2),
+                new Position(2, 6),
+                new Position(5, 3),
+                new Position(5, 5),
+                new Position(1, 3),
+                new Position(1, 5),
+            ]))
+        );
+    }
+
+    public function testMoves_obstructed() {
+        $subject = new Knight(new Position(
+            3, 1
+        ), Side::WHITE);
+
+        $result = $subject->getMoveCandidateMap(
+            new FieldBitMap([
+                new Position(2, 3),
+                new Position(5, 2)
+            ]),
+            new FieldBitMap([
+                new Position(4, 3)
+            ]),
+            FieldBitMap::empty()
+        );
+
+        $this->assertTrue(
+            $result->equals(new FieldBitMap([
+                new Position(4, 3),
+                new Position(1, 2),
+                new Position(1, 0),
+                new Position(5, 0)
+            ]))
+        );
+    }
+
+    public function testCaptureable() {
+        $subject = new Knight(
+            new Position(5, 6),
+            Side::WHITE,
+        );
+
+        $this->assertTrue(
+            $subject->getCaptureableMap(true)->equals(new FieldBitMap([
+                new Position(5, 6)
+            ]))
+        );
+    }
+
+    public function testCaptureMap() {
+        $subject = new Knight(new Position(
+            5, 2
+        ), Side::WHITE);
+
+        $result = $subject->getCaptureMap(
+            new FieldBitMap([
+                new Position(6, 4),
+                new Position(4, 4),
+                new Position(3, 1),
+                new Position(7, 1),
+            ])
+        );
+
+        $this->assertTrue(
+            $result->equals(new FieldBitMap([
+                new Position(3, 3),
+                new Position(7, 3),
+                new Position(6, 0),
+                new Position(4, 0),
+            ]))
+        );
+    }
+}
\ No newline at end of file