From ae17e16fcdf77355cdad386033e62c40f599a417 Mon Sep 17 00:00:00 2001 From: Phat Tang Date: Fri, 15 Jul 2022 09:51:57 +0700 Subject: [PATCH 1/9] sgf-parsing: add problem to config file --- config.json | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/config.json b/config.json index 7fefd1ea2..e713e88d4 100644 --- a/config.json +++ b/config.json @@ -1879,6 +1879,23 @@ "conditionals-if" ], "difficulty": 8 + }, + { + "slug": "sgf-parsing", + "name": "SGF Parsing", + "uuid": "0d6325d1-c0a3-456e-9a92-cea0559e82ed", + "practices": ["refactoring", "trees", "switch-case", "conditionals-if", "strings"], + "prerequisites": [ + "trees", + "maps", + "strings", + "chars", + "conditionals-if", + "lists", + "for-loops", + "exception_handling" + ], + "difficulty": 7 } ], "foregone": [] From b2dde3d0033bab217bf919d260850f2c20c5e231 Mon Sep 17 00:00:00 2001 From: Phat Tang Date: Fri, 15 Jul 2022 09:53:06 +0700 Subject: [PATCH 2/9] sgf-parsing: create problem submodule --- exercises/practice/sgf-parsing/build.gradle | 24 +++++++++++++++++++++ exercises/settings.gradle | 1 + 2 files changed, 25 insertions(+) create mode 100644 exercises/practice/sgf-parsing/build.gradle diff --git a/exercises/practice/sgf-parsing/build.gradle b/exercises/practice/sgf-parsing/build.gradle new file mode 100644 index 000000000..8bd005d42 --- /dev/null +++ b/exercises/practice/sgf-parsing/build.gradle @@ -0,0 +1,24 @@ +apply plugin: "java" +apply plugin: "eclipse" +apply plugin: "idea" + +// set default encoding to UTF-8 +compileJava.options.encoding = "UTF-8" +compileTestJava.options.encoding = "UTF-8" + +repositories { + mavenCentral() +} + +dependencies { + testImplementation "junit:junit:4.13" + testImplementation "org.assertj:assertj-core:3.15.0" +} + +test { + testLogging { + exceptionFormat = 'full' + showStandardStreams = true + events = ["passed", "failed", "skipped"] + } +} diff --git a/exercises/settings.gradle b/exercises/settings.gradle index a4ba370e7..5428c6fd7 100644 --- a/exercises/settings.gradle +++ b/exercises/settings.gradle @@ -118,6 +118,7 @@ include 'practice:series' include 'practice:sieve' include 'practice:simple-cipher' include 'practice:simple-linked-list' +include 'practice:sgf-parsing' include 'practice:space-age' include 'practice:spiral-matrix' include 'practice:strain' From bc525e1ae0ed447eec4598c9061dd738b044caeb Mon Sep 17 00:00:00 2001 From: Phat Tang Date: Fri, 15 Jul 2022 09:54:02 +0700 Subject: [PATCH 3/9] sgf-parsing: generate problem template using configlet --- .../sgf-parsing/.docs/instructions.md | 66 +++++++++++++++++++ .../practice/sgf-parsing/.meta/config.json | 15 +++++ .../practice/sgf-parsing/.meta/tests.toml | 49 ++++++++++++++ 3 files changed, 130 insertions(+) create mode 100644 exercises/practice/sgf-parsing/.docs/instructions.md create mode 100644 exercises/practice/sgf-parsing/.meta/config.json create mode 100644 exercises/practice/sgf-parsing/.meta/tests.toml diff --git a/exercises/practice/sgf-parsing/.docs/instructions.md b/exercises/practice/sgf-parsing/.docs/instructions.md new file mode 100644 index 000000000..7fc8e7f37 --- /dev/null +++ b/exercises/practice/sgf-parsing/.docs/instructions.md @@ -0,0 +1,66 @@ +# Instructions + +Parsing a Smart Game Format string. + +[SGF](https://en.wikipedia.org/wiki/Smart_Game_Format) is a standard format for +storing board game files, in particular go. + +SGF is a fairly simple format. An SGF file usually contains a single +tree of nodes where each node is a property list. The property list +contains key value pairs, each key can only occur once but may have +multiple values. + +An SGF file may look like this: + +```text +(;FF[4]C[root]SZ[19];B[aa];W[ab]) +``` + +This is a tree with three nodes: + +- The top level node has three properties: FF\[4\] (key = "FF", value + = "4"), C\[root\](key = "C", value = "root") and SZ\[19\] (key = + "SZ", value = "19"). (FF indicates the version of SGF, C is a + comment and SZ is the size of the board.) + - The top level node has a single child which has a single property: + B\[aa\]. (Black plays on the point encoded as "aa", which is the + 1-1 point). + - The B\[aa\] node has a single child which has a single property: + W\[ab\]. + +As you can imagine an SGF file contains a lot of nodes with a single +child, which is why there's a shorthand for it. + +SGF can encode variations of play. Go players do a lot of backtracking +in their reviews (let's try this, doesn't work, let's try that) and SGF +supports variations of play sequences. For example: + +```text +(;FF[4](;B[aa];W[ab])(;B[dd];W[ee])) +``` + +Here the root node has two variations. The first (which by convention +indicates what's actually played) is where black plays on 1-1. Black was +sent this file by his teacher who pointed out a more sensible play in +the second child of the root node: `B[dd]` (4-4 point, a very standard +opening to take the corner). + +A key can have multiple values associated with it. For example: + +```text +(;FF[4];AB[aa][ab][ba]) +``` + +Here `AB` (add black) is used to add three black stones to the board. + +There are a few more complexities to SGF (and parsing in general), which +you can mostly ignore. You should assume that the input is encoded in +UTF-8, the tests won't contain a charset property, so don't worry about +that. Furthermore you may assume that all newlines are unix style (`\n`, +no `\r` or `\r\n` will be in the tests) and that no optional whitespace +between properties, nodes, etc will be in the tests. + +The exercise will have you parse an SGF string and return a tree +structure of properties. You do not need to encode knowledge about the +data types of properties, just use the rules for the +[text](http://www.red-bean.com/sgf/sgf4.html#text) type everywhere. diff --git a/exercises/practice/sgf-parsing/.meta/config.json b/exercises/practice/sgf-parsing/.meta/config.json new file mode 100644 index 000000000..a79393333 --- /dev/null +++ b/exercises/practice/sgf-parsing/.meta/config.json @@ -0,0 +1,15 @@ +{ + "authors": [], + "files": { + "solution": [ + "src/main/java/SgfParsing.java" + ], + "test": [ + "src/test/java/SgfParsingTest.java" + ], + "example": [ + ".meta/src/reference/java/SgfParsing.java" + ] + }, + "blurb": "Parsing a Smart Game Format string." +} diff --git a/exercises/practice/sgf-parsing/.meta/tests.toml b/exercises/practice/sgf-parsing/.meta/tests.toml new file mode 100644 index 000000000..c17626345 --- /dev/null +++ b/exercises/practice/sgf-parsing/.meta/tests.toml @@ -0,0 +1,49 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[2668d5dc-109f-4f71-b9d5-8d06b1d6f1cd] +description = "empty input" + +[84ded10a-94df-4a30-9457-b50ccbdca813] +description = "tree with no nodes" + +[0a6311b2-c615-4fa7-800e-1b1cbb68833d] +description = "node without tree" + +[8c419ed8-28c4-49f6-8f2d-433e706110ef] +description = "node without properties" + +[8209645f-32da-48fe-8e8f-b9b562c26b49] +description = "single node tree" + +[6c995856-b919-4c75-8fd6-c2c3c31b37dc] +description = "multiple properties" + +[a771f518-ec96-48ca-83c7-f8d39975645f] +description = "properties without delimiter" + +[6c02a24e-6323-4ed5-9962-187d19e36bc8] +description = "all lowercase property" + +[8772d2b1-3c57-405a-93ac-0703b671adc1] +description = "upper and lowercase property" + +[a759b652-240e-42ec-a6d2-3a08d834b9e2] +description = "two nodes" + +[cc7c02bc-6097-42c4-ab88-a07cb1533d00] +description = "two child trees" + +[724eeda6-00db-41b1-8aa9-4d5238ca0130] +description = "multiple property values" + +[11c36323-93fc-495d-bb23-c88ee5844b8c] +description = "escaped property" From 4b0632062ae32aac217792eb4cc1f126146aedde Mon Sep 17 00:00:00 2001 From: Phat Tang Date: Fri, 15 Jul 2022 09:55:06 +0700 Subject: [PATCH 4/9] sgf-parsing: add starter implementation and test cases --- .../sgf-parsing/src/main/java/SgfNode.java | 60 ++++++++ .../sgf-parsing/src/main/java/SgfParsing.java | 10 ++ .../src/main/java/SgfParsingException.java | 7 + .../src/test/java/SgfParsingTest.java | 136 ++++++++++++++++++ 4 files changed, 213 insertions(+) create mode 100644 exercises/practice/sgf-parsing/src/main/java/SgfNode.java create mode 100644 exercises/practice/sgf-parsing/src/main/java/SgfParsing.java create mode 100644 exercises/practice/sgf-parsing/src/main/java/SgfParsingException.java create mode 100644 exercises/practice/sgf-parsing/src/test/java/SgfParsingTest.java diff --git a/exercises/practice/sgf-parsing/src/main/java/SgfNode.java b/exercises/practice/sgf-parsing/src/main/java/SgfNode.java new file mode 100644 index 000000000..85fc19dbb --- /dev/null +++ b/exercises/practice/sgf-parsing/src/main/java/SgfNode.java @@ -0,0 +1,60 @@ +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +public class SgfNode { + + private Map> properties; + private List children; + + public SgfNode() { + properties = new HashMap<>(); + children = new ArrayList<>(); + } + + public SgfNode(Map> properties) { + this.properties = properties; + children = new ArrayList<>(); + } + + public SgfNode(Map> properties, List children) { + this.properties = properties; + this.children = children; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + SgfNode sgfNode = (SgfNode) o; + return properties.equals(sgfNode.properties) && children.equals(sgfNode.children); + } + + @Override + public int hashCode() { + return Objects.hash(properties, children); + } + + @Override + public String toString() { + return "SgfNode{" + + "properties=" + properties + + ", children=" + children + + '}'; + } + + public void appendChild(SgfNode node) { + children.add(node); + } + + public void setProperties(Map> properties) { + this.properties = properties; + } + +} diff --git a/exercises/practice/sgf-parsing/src/main/java/SgfParsing.java b/exercises/practice/sgf-parsing/src/main/java/SgfParsing.java new file mode 100644 index 000000000..6178f1beb --- /dev/null +++ b/exercises/practice/sgf-parsing/src/main/java/SgfParsing.java @@ -0,0 +1,10 @@ +/* + +Since this exercise has a difficulty of > 4 it doesn't come +with any starter implementation. +This is so that you get to practice creating classes and methods +which is an important part of programming in Java. + +Please remove this comment when submitting your solution. + +*/ diff --git a/exercises/practice/sgf-parsing/src/main/java/SgfParsingException.java b/exercises/practice/sgf-parsing/src/main/java/SgfParsingException.java new file mode 100644 index 000000000..6d1ad23f5 --- /dev/null +++ b/exercises/practice/sgf-parsing/src/main/java/SgfParsingException.java @@ -0,0 +1,7 @@ +public class SgfParsingException extends Exception { + + public SgfParsingException(String message) { + super(message); + } + +} diff --git a/exercises/practice/sgf-parsing/src/test/java/SgfParsingTest.java b/exercises/practice/sgf-parsing/src/test/java/SgfParsingTest.java new file mode 100644 index 000000000..25301285f --- /dev/null +++ b/exercises/practice/sgf-parsing/src/test/java/SgfParsingTest.java @@ -0,0 +1,136 @@ +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import java.util.List; +import java.util.Map; + +import org.junit.Ignore; +import org.junit.Test; + +public class SgfParsingTest { + + @Test + public void emptyInput() { + String input = ""; + assertThrows("tree missing", + SgfParsingException.class, + () -> new SgfParsing().parse(input)); + } + + @Test + @Ignore("Remove to run test") + public void treeWithNoNodes() { + String input = "()"; + assertThrows("tree with no nodes", + SgfParsingException.class, + () -> new SgfParsing().parse(input)); + } + + @Test + @Ignore("Remove to run test") + public void nodeWithoutTree() { + String input = ";"; + assertThrows("tree missing", + SgfParsingException.class, + () -> new SgfParsing().parse(input)); + } + + @Test + @Ignore("Remove to run test") + public void nodeWithoutProperties() throws SgfParsingException { + String input = "(;)"; + SgfNode expected = new SgfNode(); + SgfNode actual = new SgfParsing().parse(input); + assertEquals(expected, actual); + } + + @Test + @Ignore("Remove to run test") + public void singleNodeTree() throws SgfParsingException { + String input = "(;A[B])"; + SgfNode expected = new SgfNode(Map.of("A", List.of("B"))); + SgfNode actual = new SgfParsing().parse(input); + assertEquals(expected, actual); + } + + @Test + @Ignore("Remove to run test") + public void multipleProperties() throws SgfParsingException { + String input = "(;A[b]C[d])"; + SgfNode expected = new SgfNode(Map.of("A", List.of("b"), + "C", List.of("d"))); + SgfNode actual = new SgfParsing().parse(input); + assertEquals(expected, actual); + } + + @Test + @Ignore("Remove to run test") + public void propertiesWithoutDelimiter() { + String input = "(;A)"; + assertThrows("properties without delimiter", + SgfParsingException.class, + () -> new SgfParsing().parse(input)); + } + + @Test + @Ignore("Remove to run test") + public void allLowercaseProperty() { + String input = "(;a[b])"; + assertThrows("property must be in uppercase", + SgfParsingException.class, + () -> new SgfParsing().parse(input)); + } + + @Test + @Ignore("Remove to run test") + public void upperAndLowercaseProperty() { + String input = "(;Aa[b])"; + assertThrows("property must be in uppercase", + SgfParsingException.class, + () -> new SgfParsing().parse(input)); + } + + @Test + @Ignore("Remove to run test") + public void twoNodes() throws SgfParsingException { + String input = "(;A[B];B[C])"; + SgfNode expected = new SgfNode(Map.of("A", List.of("B")), + List.of( + new SgfNode(Map.of("B", List.of("C"))) + )); + SgfNode actual = new SgfParsing().parse(input); + assertEquals(expected, actual); + } + + @Test + @Ignore("Remove to run test") + public void twoChildTrees() throws SgfParsingException { + String input = "(;A[B](;B[C])(;C[D]))"; + SgfNode expected = new SgfNode(Map.of("A", List.of("B")), + List.of( + new SgfNode(Map.of("B", List.of("C"))), + new SgfNode(Map.of("C", List.of("D"))) + )); + SgfNode actual = new SgfParsing().parse(input); + assertEquals(expected, actual); + } + + @Test + @Ignore("Remove to run test") + public void multiplePropertyValues() throws SgfParsingException { + String input = "(;A[b][c][d])"; + SgfNode expected = new SgfNode(Map.of("A", List.of("b", "c", "d"))); + SgfNode actual = new SgfParsing().parse(input); + assertEquals(expected, actual); + } + + @Test + @Ignore("Remove to run test") + public void escapedProperty() throws SgfParsingException { + String input = "(;A[\\]b\nc\nd\t\te \n\\]])"; + SgfNode expected = new SgfNode(Map.of("A", List.of("]b\nc\nd e \n]"))); + SgfNode actual = new SgfParsing().parse(input); + assertEquals(expected, actual); + } + +} From ca3ed75fe6dbeb8413a69b07b71ca0bb18ce84fe Mon Sep 17 00:00:00 2001 From: Phat Tang Date: Fri, 15 Jul 2022 09:55:35 +0700 Subject: [PATCH 5/9] sgf-parsing: add reference implementation --- .../.meta/src/reference/java/SgfNode.java | 60 +++++++++ .../.meta/src/reference/java/SgfParsing.java | 123 ++++++++++++++++++ .../reference/java/SgfParsingException.java | 7 + 3 files changed, 190 insertions(+) create mode 100644 exercises/practice/sgf-parsing/.meta/src/reference/java/SgfNode.java create mode 100644 exercises/practice/sgf-parsing/.meta/src/reference/java/SgfParsing.java create mode 100644 exercises/practice/sgf-parsing/.meta/src/reference/java/SgfParsingException.java diff --git a/exercises/practice/sgf-parsing/.meta/src/reference/java/SgfNode.java b/exercises/practice/sgf-parsing/.meta/src/reference/java/SgfNode.java new file mode 100644 index 000000000..85fc19dbb --- /dev/null +++ b/exercises/practice/sgf-parsing/.meta/src/reference/java/SgfNode.java @@ -0,0 +1,60 @@ +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +public class SgfNode { + + private Map> properties; + private List children; + + public SgfNode() { + properties = new HashMap<>(); + children = new ArrayList<>(); + } + + public SgfNode(Map> properties) { + this.properties = properties; + children = new ArrayList<>(); + } + + public SgfNode(Map> properties, List children) { + this.properties = properties; + this.children = children; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + SgfNode sgfNode = (SgfNode) o; + return properties.equals(sgfNode.properties) && children.equals(sgfNode.children); + } + + @Override + public int hashCode() { + return Objects.hash(properties, children); + } + + @Override + public String toString() { + return "SgfNode{" + + "properties=" + properties + + ", children=" + children + + '}'; + } + + public void appendChild(SgfNode node) { + children.add(node); + } + + public void setProperties(Map> properties) { + this.properties = properties; + } + +} diff --git a/exercises/practice/sgf-parsing/.meta/src/reference/java/SgfParsing.java b/exercises/practice/sgf-parsing/.meta/src/reference/java/SgfParsing.java new file mode 100644 index 000000000..94fa34553 --- /dev/null +++ b/exercises/practice/sgf-parsing/.meta/src/reference/java/SgfParsing.java @@ -0,0 +1,123 @@ +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class SgfParsing { + + public SgfNode parse(String input) throws SgfParsingException { + checkIfTreeHasAnyNode(input); + checkIfTreeExists(input); + SgfNode tree = new SgfNode(); + parseFromIndex(input, 2, tree); + return tree; + } + + private void checkIfTreeHasAnyNode(String input) throws SgfParsingException { + if ("()".equals(input)) { + throw new SgfParsingException("tree with no nodes"); + } + } + + private void checkIfTreeExists(String input) throws SgfParsingException { + if (input.isEmpty() || !input.startsWith("(;")) { + throw new SgfParsingException("tree missing"); + } + } + + private int parseFromIndex(String input, int index, SgfNode root) throws SgfParsingException { + StringBuilder buffer = new StringBuilder(); + Map> properties = new HashMap<>(); + String key = null; + while (index < input.length()) { + switch (input.charAt(index)) { + case '(': + index = addNewChild(input, index, root); + break; + case ')': + break; + case '[': + key = loadKeyFromBuffer(buffer, properties); + break; + case ']': + properties.get(key).add(popStringFromBuffer(buffer)); + if (input.charAt(index + 1) == ')') { + root.setProperties(properties); + return index + 1; + } + index = examineNextNode(input, index, root, properties); + break; + default: + index = appendCharToBuffer(input, index, buffer); + } + ++index; + } + checkIfThereAreDelimiters(buffer); + return index; + } + + private int addNewChild(String input, int index, SgfNode root) throws SgfParsingException { + SgfNode node = new SgfNode(); + root.appendChild(node); + return parseFromIndex(input, index + 2, node); + } + + private String loadKeyFromBuffer(StringBuilder buffer, Map> properties) + throws SgfParsingException { + String key = popStringFromBuffer(buffer); + checkIfKeyUppercase(key); + properties.put(key, new ArrayList<>()); + return key; + } + + private String popStringFromBuffer(StringBuilder stringBuilder) { + String toReturn = stringBuilder.toString(); + stringBuilder.setLength(0); + return toReturn; + } + + private void checkIfKeyUppercase(String key) throws SgfParsingException { + if (!isUppercase(key)) { + throw new SgfParsingException("property must be in uppercase"); + } + } + + private boolean isUppercase(String key) { + return key.equals(key.toUpperCase()); + } + + private int examineNextNode(String input, int index, SgfNode root, Map> properties) + throws SgfParsingException { + char nextChar = input.charAt(index + 1); + if (nextChar == '[') { + ++index; + } else if (nextChar == '(' || nextChar == ';') { + root.setProperties(properties); + if (nextChar == '(') { + index = addNewChild(input, index + 1, root); + } else { + index = addNewChild(input, index, root); + } + } + return index; + } + + private int appendCharToBuffer(String input, int index, StringBuilder buffer) { + char character = input.charAt(index); + while (character == '\\') { + character = input.charAt(++index); + } + if (character == '\t') { + character = ' '; + } + buffer.append(character); + return index; + } + + private void checkIfThereAreDelimiters(StringBuilder buffer) throws SgfParsingException { + if (!buffer.isEmpty()) { + throw new SgfParsingException("properties without delimiter"); + } + } + +} diff --git a/exercises/practice/sgf-parsing/.meta/src/reference/java/SgfParsingException.java b/exercises/practice/sgf-parsing/.meta/src/reference/java/SgfParsingException.java new file mode 100644 index 000000000..6d1ad23f5 --- /dev/null +++ b/exercises/practice/sgf-parsing/.meta/src/reference/java/SgfParsingException.java @@ -0,0 +1,7 @@ +public class SgfParsingException extends Exception { + + public SgfParsingException(String message) { + super(message); + } + +} From 33a219d74c3e9583ec05e5c3da9bc63767ec43d5 Mon Sep 17 00:00:00 2001 From: Phat Tang Date: Fri, 15 Jul 2022 10:11:26 +0700 Subject: [PATCH 6/9] sgf-parsing: update instruction doc by putting each sentence in a separate line --- .../sgf-parsing/.docs/instructions.md | 54 +++++++------------ 1 file changed, 20 insertions(+), 34 deletions(-) diff --git a/exercises/practice/sgf-parsing/.docs/instructions.md b/exercises/practice/sgf-parsing/.docs/instructions.md index 7fc8e7f37..cffc012fd 100644 --- a/exercises/practice/sgf-parsing/.docs/instructions.md +++ b/exercises/practice/sgf-parsing/.docs/instructions.md @@ -5,10 +5,9 @@ Parsing a Smart Game Format string. [SGF](https://en.wikipedia.org/wiki/Smart_Game_Format) is a standard format for storing board game files, in particular go. -SGF is a fairly simple format. An SGF file usually contains a single -tree of nodes where each node is a property list. The property list -contains key value pairs, each key can only occur once but may have -multiple values. +SGF is a fairly simple format. +An SGF file usually contains a single tree of nodes where each node is a property list. +The property list contains key value pairs, each key can only occur once but may have multiple values. An SGF file may look like this: @@ -18,32 +17,24 @@ An SGF file may look like this: This is a tree with three nodes: -- The top level node has three properties: FF\[4\] (key = "FF", value - = "4"), C\[root\](key = "C", value = "root") and SZ\[19\] (key = - "SZ", value = "19"). (FF indicates the version of SGF, C is a - comment and SZ is the size of the board.) - - The top level node has a single child which has a single property: - B\[aa\]. (Black plays on the point encoded as "aa", which is the - 1-1 point). - - The B\[aa\] node has a single child which has a single property: - W\[ab\]. +- The top level node has three properties: FF\[4\] (key = "FF", value = "4"), C\[root\](key = "C", value = "root") and SZ\[19\] (key = "SZ", value = "19"). + (FF indicates the version of SGF, C is a comment and SZ is the size of the board.) + - The top level node has a single child which has a single property: B\[aa\]. + (Black plays on the point encoded as "aa", which is the 1-1 point). + - The B\[aa\] node has a single child which has a single property: W\[ab\]. -As you can imagine an SGF file contains a lot of nodes with a single -child, which is why there's a shorthand for it. +As you can imagine an SGF file contains a lot of nodes with a single child, which is why there's a shorthand for it. -SGF can encode variations of play. Go players do a lot of backtracking -in their reviews (let's try this, doesn't work, let's try that) and SGF -supports variations of play sequences. For example: +SGF can encode variations of play. Go players do a lot of backtracking in their reviews (let's try this, doesn't work, let's try that) and SGF supports variations of play sequences. +For example: ```text (;FF[4](;B[aa];W[ab])(;B[dd];W[ee])) ``` -Here the root node has two variations. The first (which by convention -indicates what's actually played) is where black plays on 1-1. Black was -sent this file by his teacher who pointed out a more sensible play in -the second child of the root node: `B[dd]` (4-4 point, a very standard -opening to take the corner). +Here the root node has two variations. +The first (which by convention indicates what's actually played) is where black plays on 1-1. +Black was sent this file by his teacher who pointed out a more sensible play in the second child of the root node: `B[dd]` (4-4 point, a very standard opening to take the corner). A key can have multiple values associated with it. For example: @@ -53,14 +44,9 @@ A key can have multiple values associated with it. For example: Here `AB` (add black) is used to add three black stones to the board. -There are a few more complexities to SGF (and parsing in general), which -you can mostly ignore. You should assume that the input is encoded in -UTF-8, the tests won't contain a charset property, so don't worry about -that. Furthermore you may assume that all newlines are unix style (`\n`, -no `\r` or `\r\n` will be in the tests) and that no optional whitespace -between properties, nodes, etc will be in the tests. - -The exercise will have you parse an SGF string and return a tree -structure of properties. You do not need to encode knowledge about the -data types of properties, just use the rules for the -[text](http://www.red-bean.com/sgf/sgf4.html#text) type everywhere. +There are a few more complexities to SGF (and parsing in general), which you can mostly ignore. +You should assume that the input is encoded in UTF-8, the tests won't contain a charset property, so don't worry about that. +Furthermore you may assume that all newlines are unix style (`\n`, no `\r` or `\r\n` will be in the tests) and that no optional whitespace between properties, nodes, etc will be in the tests. + +The exercise will have you parse an SGF string and return a tree structure of properties. +You do not need to encode knowledge about the data types of properties, just use the rules for the [text](http://www.red-bean.com/sgf/sgf4.html#text) type everywhere. From b443d313ba0b78f3b13eac8e78883cdc861deef8 Mon Sep 17 00:00:00 2001 From: Phat Tang Date: Sun, 17 Jul 2022 19:55:35 +0700 Subject: [PATCH 7/9] sgf-parsing: fix config metadata --- config.json | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/config.json b/config.json index e713e88d4..aa6db50ac 100644 --- a/config.json +++ b/config.json @@ -1884,16 +1884,13 @@ "slug": "sgf-parsing", "name": "SGF Parsing", "uuid": "0d6325d1-c0a3-456e-9a92-cea0559e82ed", - "practices": ["refactoring", "trees", "switch-case", "conditionals-if", "strings"], + "practices": [], "prerequisites": [ - "trees", - "maps", "strings", "chars", "conditionals-if", "lists", - "for-loops", - "exception_handling" + "for-loops" ], "difficulty": 7 } From 2760eb253b76566b33b7dec6b316cd273f82324f Mon Sep 17 00:00:00 2001 From: Phat Tang Date: Sun, 17 Jul 2022 19:56:12 +0700 Subject: [PATCH 8/9] sgf-parsing: add author --- exercises/practice/sgf-parsing/.meta/config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/practice/sgf-parsing/.meta/config.json b/exercises/practice/sgf-parsing/.meta/config.json index a79393333..d48991d02 100644 --- a/exercises/practice/sgf-parsing/.meta/config.json +++ b/exercises/practice/sgf-parsing/.meta/config.json @@ -1,5 +1,5 @@ { - "authors": [], + "authors": ["tlphat"], "files": { "solution": [ "src/main/java/SgfParsing.java" From 5b6776eb835510fc20314b0944d15c525125da52 Mon Sep 17 00:00:00 2001 From: Phat Tang Date: Mon, 18 Jul 2022 09:32:16 +0700 Subject: [PATCH 9/9] sgf-parsing: fix !isEmpty() to length() != 0 --- .../sgf-parsing/.meta/src/reference/java/SgfParsing.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/practice/sgf-parsing/.meta/src/reference/java/SgfParsing.java b/exercises/practice/sgf-parsing/.meta/src/reference/java/SgfParsing.java index 94fa34553..304019c2e 100644 --- a/exercises/practice/sgf-parsing/.meta/src/reference/java/SgfParsing.java +++ b/exercises/practice/sgf-parsing/.meta/src/reference/java/SgfParsing.java @@ -115,7 +115,7 @@ private int appendCharToBuffer(String input, int index, StringBuilder buffer) { } private void checkIfThereAreDelimiters(StringBuilder buffer) throws SgfParsingException { - if (!buffer.isEmpty()) { + if (buffer.length() != 0) { throw new SgfParsingException("properties without delimiter"); } }