diff --git a/src/dev/kske/chess/pgn/PGNDatabase.java b/src/dev/kske/chess/pgn/PGNDatabase.java
new file mode 100644
index 0000000..61ccdc6
--- /dev/null
+++ b/src/dev/kske/chess/pgn/PGNDatabase.java
@@ -0,0 +1,29 @@
+package dev.kske.chess.pgn;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Scanner;
+
+import dev.kske.chess.exception.ChessException;
+
+/**
+ * Project: Chess
+ * File: PGNDatabase.java
+ * Created: 4 Oct 2019
+ * Author: Kai S. K. Engelbart
+ */
+public class PGNDatabase {
+
+ private final List games = new ArrayList<>();
+
+ public void load(File pgnFile) {
+ try (Scanner sc = new Scanner(pgnFile)) {
+ while (sc.hasNext())
+ games.add(PGNGame.parse(sc));
+ } catch (FileNotFoundException | ChessException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/dev/kske/chess/pgn/PGNGame.java b/src/dev/kske/chess/pgn/PGNGame.java
new file mode 100644
index 0000000..e800a3c
--- /dev/null
+++ b/src/dev/kske/chess/pgn/PGNGame.java
@@ -0,0 +1,77 @@
+package dev.kske.chess.pgn;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.regex.MatchResult;
+import java.util.regex.Pattern;
+
+import dev.kske.chess.board.Board;
+import dev.kske.chess.exception.ChessException;
+
+/**
+ * Project: Chess
+ * File: PGNGame.java
+ * Created: 22 Sep 2019
+ * Author: Kai S. K. Engelbart
+ */
+public class PGNGame {
+
+ private final Map tagPairs = new HashMap<>(7);
+ private final Board board = new Board();
+
+ public static PGNGame parse(Scanner sc) throws ChessException {
+ PGNGame game = new PGNGame();
+
+ MatchResult matchResult;
+ Pattern tagPairPattern = Pattern.compile("\\[(\\w+) \"(.*)\"]"),
+ movePattern = Pattern.compile("\\d+\\. (\\S+)\\s(\\S+)"),
+ nagPattern = Pattern.compile("(\\$\\d{1,3})*"),
+ terminationMarkerPattern = Pattern.compile("1-0|0-1|1\\/2-1\\/2|\\*");
+
+ // Parse tag pairs
+ while (true) {
+ if (sc.findInLine(tagPairPattern) != null) {
+ matchResult = sc.match();
+ if (matchResult.groupCount() == 2) game.setTag(matchResult.group(0), matchResult.group(1));
+ else break;
+ } else break;
+ sc.nextLine();
+ }
+
+ // Parse movetext
+ while (true) {
+ // Skip NAG (Numeric Annotation Glyph)
+ sc.skip(nagPattern);
+
+ // TODO: Parse RAV (Recursive Annotation Variation)
+
+ sc.findWithinHorizon(movePattern, 20);
+ matchResult = sc.match();
+ if (matchResult.groupCount() > 0) for (int i = 1; i < matchResult.groupCount() + 1; i++) {
+ game.board.move(matchResult.group(i));
+ System.out.println(game.board.toFEN());
+ }
+ else break;
+ }
+
+ // Parse game termination marker
+ if (sc.hasNext(terminationMarkerPattern)) {
+ sc.next(terminationMarkerPattern);
+ } else throw new ChessException("Game termination marker expected");
+
+ return game;
+ }
+
+ public String getTag(String tagName) {
+ return tagPairs.get(tagName);
+ }
+
+ public boolean hasTag(String tagName) {
+ return tagPairs.containsKey(tagName);
+ }
+
+ public void setTag(String tagName, String tagValue) {
+ tagPairs.put(tagName, tagValue);
+ }
+}