Просмотр исходного кода

go-counting: add new exercise (#1351)

* go-counting: add new exercise

* go-counting: add new exercise

* go-counting: add new exercise

* go-counting: add new exercise

* go-counting: add exercise

* go-counting: add new exercise
jssander 8 лет назад
Родитель
Сommit
1e2d3b8209

+ 13
- 0
config.json Просмотреть файл

@@ -1091,6 +1091,19 @@
1091 1091
     },
1092 1092
     {
1093 1093
       "core": false,
1094
+      "difficulty": 7,
1095
+      "slug": "go-counting",
1096
+      "topics": [
1097
+        "algorithms",
1098
+        "loops",
1099
+        "conditionals",
1100
+        "games"
1101
+      ],
1102
+      "unlocked_by": "scrabble-score",
1103
+      "uuid": "2e760ae2-fadd-4d31-9639-c4554e2826e9"
1104
+    },
1105
+    {
1106
+      "core": false,
1094 1107
       "difficulty": 8,
1095 1108
       "slug": "ocr-numbers",
1096 1109
       "topics": [

+ 152
- 0
exercises/go-counting/.meta/src/reference/java/GoCounting.java Просмотреть файл

@@ -0,0 +1,152 @@
1
+import java.awt.Point;
2
+import java.util.ArrayList;
3
+import java.util.HashMap;
4
+import java.util.HashSet;
5
+import java.util.Set;
6
+
7
+class GoCounting {
8
+	private Player[][] board;
9
+	
10
+	GoCounting (String boardString) {
11
+		String[] lines = boardString.split("\n");
12
+		
13
+		board = new Player[lines[0].length()][lines.length];
14
+		
15
+		for (int i = 0; i < lines.length; i++) {
16
+			for (int j = 0; j < lines[i].length(); j++) {
17
+				if (lines[i].charAt(j) == 'B') {
18
+					board[j][i] = Player.BLACK;
19
+				} else if (lines[i].charAt(j) == 'W') {
20
+					board[j][i] = Player.WHITE;
21
+				} else {
22
+					board[j][i] = Player.NONE;
23
+				}
24
+			}
25
+		}
26
+	}
27
+	
28
+	private ArrayList<Point> getAdjacent(Point p) {
29
+		ArrayList<Point> adjacent = new ArrayList<>();
30
+		if (p.x > 0) {
31
+			adjacent.add(new Point(p.x - 1, p.y));
32
+		}
33
+		if (p.x < board.length - 1) {
34
+			adjacent.add(new Point(p.x + 1, p.y)); 
35
+		}
36
+		if (p.y > 0) {
37
+			adjacent.add(new Point(p.x, p.y - 1));
38
+		}
39
+		if (p.y < board[0].length - 1) {
40
+			adjacent.add(new Point(p.x, p.y + 1));
41
+		}
42
+		return adjacent;
43
+	}
44
+	
45
+	Player getTerritoryOwner(int x, int y) {
46
+		
47
+		if (x < 0 || x >= board.length || y < 0 || y >= board[0].length) {
48
+			throw new IllegalArgumentException("Invalid coordinate");
49
+		}
50
+		
51
+		if (board[x][y] == Player.BLACK || board[x][y] == Player.WHITE) {
52
+			return Player.NONE;
53
+		}
54
+		
55
+		ArrayList<Point> visited = new ArrayList<>();
56
+		ArrayList<Point> edges = new ArrayList<>();
57
+		ArrayList<Point> territory = new ArrayList<>();
58
+		
59
+		ArrayList<Point> tovisit = new ArrayList<>();
60
+		
61
+		tovisit.add(new Point(x, y));
62
+		
63
+		while (tovisit.size() > 0) {
64
+			Point current = tovisit.remove(0);
65
+			
66
+			if (board[current.x][current.y] == Player.NONE) {
67
+				visited.add(current);
68
+				territory.add(current);
69
+				
70
+				ArrayList<Point> adjacent = getAdjacent(current);
71
+				
72
+				for (Point p : adjacent) {
73
+					if (!visited.contains(p) && !tovisit.contains(p)) {
74
+						tovisit.add(p);
75
+					}
76
+				}
77
+			} else {
78
+				edges.add(current);
79
+			}
80
+		}
81
+		
82
+		if (edges.size() == 0) {
83
+			return Player.NONE;
84
+		}
85
+		
86
+		Player owner = board[edges.get(0).x][edges.get(0).y];
87
+		for (int i = 0; i < edges.size(); i++) {
88
+			if (owner != board[edges.get(i).x][edges.get(i).y]) {
89
+				owner = Player.NONE;
90
+			}
91
+		}
92
+		
93
+		return owner;
94
+	}
95
+	
96
+	Set<Point> getTerritory(int x, int y) {
97
+		
98
+		if (x < 0 || x >= board.length || y < 0 || y >= board[0].length) {
99
+			throw new IllegalArgumentException("Invalid coordinate");
100
+		}
101
+		
102
+		ArrayList<Point> visited = new ArrayList<>();
103
+		HashSet<Point> territory = new HashSet<>();
104
+		
105
+		ArrayList<Point> tovisit = new ArrayList<>();
106
+		
107
+		tovisit.add(new Point(x, y));
108
+		
109
+		while (tovisit.size() > 0) {
110
+			Point current = tovisit.remove(0);
111
+			
112
+			if (board[current.x][current.y] == Player.NONE) {
113
+				visited.add(current);
114
+				territory.add(current);
115
+				
116
+				ArrayList<Point> adjacent = getAdjacent(current);
117
+				
118
+				for (Point p : adjacent) {
119
+					if (!visited.contains(p) && !tovisit.contains(p)) {
120
+						tovisit.add(p);
121
+					}
122
+				}
123
+			}
124
+		}
125
+		
126
+		return territory;
127
+	}
128
+	
129
+	HashMap<String, Set<Point>> getTerritories() {
130
+		HashMap<String, Set<Point>> territories = new HashMap<String, Set<Point>>();
131
+		
132
+		territories.put("WHITE", new HashSet<Point>());
133
+		territories.put("BLACK", new HashSet<Point>());
134
+		territories.put("NONE", new HashSet<Point>());
135
+		
136
+		for (int i = 0; i < board[0].length; i++) {
137
+			for (int j = 0; j < board.length; j++) {
138
+				if (board[j][i] == Player.NONE) {
139
+					if (getTerritoryOwner(j, i) == Player.NONE) {
140
+						territories.get("NONE").add(new Point(j, i));
141
+					} else if (getTerritoryOwner(j, i) == Player.BLACK) {
142
+						territories.get("BLACK").add(new Point(j, i));
143
+					} else {
144
+						territories.get("WHITE").add(new Point(j, i));
145
+					}
146
+				}
147
+			}
148
+		}
149
+		
150
+		return territories;
151
+	}
152
+}

+ 3
- 0
exercises/go-counting/.meta/src/reference/java/Player.java Просмотреть файл

@@ -0,0 +1,3 @@
1
+enum Player {
2
+    NONE, BLACK, WHITE
3
+}

+ 2
- 0
exercises/go-counting/.meta/version Просмотреть файл

@@ -0,0 +1,2 @@
1
+1.0.0
2
+

+ 50
- 0
exercises/go-counting/README.md Просмотреть файл

@@ -0,0 +1,50 @@
1
+# Go Counting
2
+
3
+Count the scored points on a Go board.
4
+
5
+In the game of go (also known as baduk, igo, cờ vây and wéiqí) points
6
+are gained by completely encircling empty intersections with your
7
+stones. The encircled intersections of a player are known as its
8
+territory.
9
+
10
+Write a function that determines the territory of each player. You may
11
+assume that any stones that have been stranded in enemy territory have
12
+already been taken off the board.
13
+
14
+Write a function that determines the territory which includes a specified coordinate.
15
+
16
+Multiple empty intersections may be encircled at once and for encircling
17
+only horizontal and vertical neighbours count. In the following diagram
18
+the stones which matter are marked "O" and the stones that don't are
19
+marked "I" (ignored).  Empty spaces represent empty intersections.
20
+
21
+```text
22
++----+
23
+|IOOI|
24
+|O  O|
25
+|O OI|
26
+|IOI |
27
++----+
28
+```
29
+
30
+To be more precise an empty intersection is part of a player's territory
31
+if all of its neighbours are either stones of that player or empty
32
+intersections that are part of that player's territory.
33
+
34
+For more information see
35
+[wikipedia](https://en.wikipedia.org/wiki/Go_%28game%29) or [Sensei's
36
+Library](http://senseis.xmp.net/).
37
+
38
+# Running the tests
39
+
40
+You can run all the tests for an exercise by entering
41
+
42
+```sh
43
+$ gradle test
44
+```
45
+
46
+in your terminal.
47
+
48
+## Submitting Incomplete Solutions
49
+
50
+It's possible to submit an incomplete solution so you can see how others have completed the exercise.

+ 18
- 0
exercises/go-counting/build.gradle Просмотреть файл

@@ -0,0 +1,18 @@
1
+apply plugin: "java"
2
+apply plugin: "eclipse"
3
+apply plugin: "idea"
4
+
5
+repositories {
6
+    mavenCentral()
7
+}
8
+
9
+dependencies {
10
+    testCompile "junit:junit:4.12"
11
+}
12
+
13
+test {
14
+    testLogging {
15
+        exceptionFormat = 'full'
16
+        events = ["passed", "failed", "skipped"]
17
+    }
18
+}

+ 0
- 0
exercises/go-counting/src/main/java/.keep Просмотреть файл


+ 3
- 0
exercises/go-counting/src/main/java/Player.java Просмотреть файл

@@ -0,0 +1,3 @@
1
+enum Player {
2
+    NONE, BLACK, WHITE
3
+}

+ 178
- 0
exercises/go-counting/src/test/java/GoCountingTest.java Просмотреть файл

@@ -0,0 +1,178 @@
1
+import org.junit.Ignore;
2
+import org.junit.Test;
3
+
4
+import java.awt.Point;
5
+import java.util.HashMap;
6
+import java.util.HashSet;
7
+import java.util.Set;
8
+
9
+import static org.junit.Assert.assertEquals;
10
+
11
+import org.junit.Rule;
12
+import org.junit.rules.ExpectedException;
13
+
14
+public class GoCountingTest {
15
+	
16
+	@Rule
17
+	public ExpectedException expectedException = ExpectedException.none(); 
18
+	
19
+	String board5x5 = "  B  \n" +
20
+	                  " B B \n" +
21
+	                  "B W B\n" +
22
+	                  " W W \n" +
23
+	                  "  W  ";
24
+	
25
+	@Test
26
+	public void blackCorner5x5BoardTest() {
27
+		GoCounting gocounting = new GoCounting(board5x5);
28
+		
29
+		Set<Point> territory = new HashSet<>();
30
+		territory.add(new Point(0, 0));
31
+		territory.add(new Point(0, 1));
32
+		territory.add(new Point(1, 0));
33
+		
34
+		assertEquals(Player.BLACK, gocounting.getTerritoryOwner(0, 1));
35
+		assertEquals(territory, gocounting.getTerritory(0, 1));
36
+	}
37
+	
38
+	@Ignore("Remove to run test")
39
+	@Test
40
+	public void whiteCenter5x5BoardTest() {
41
+		GoCounting gocounting = new GoCounting(board5x5);
42
+		
43
+		Set<Point> territory = new HashSet<>();
44
+		territory.add(new Point(2, 3));
45
+		
46
+		assertEquals(Player.WHITE, gocounting.getTerritoryOwner(2, 3));
47
+		assertEquals(territory, gocounting.getTerritory(2, 3));
48
+	}
49
+	
50
+	@Ignore("Remove to run test")
51
+	@Test
52
+	public void openCorner5x5BoardTest() {
53
+		GoCounting gocounting = new GoCounting(board5x5);
54
+		
55
+		Set<Point> territory = new HashSet<>();
56
+		territory.add(new Point(0, 3));
57
+		territory.add(new Point(0, 4));
58
+		territory.add(new Point(1, 4));
59
+		
60
+		assertEquals(Player.NONE, gocounting.getTerritoryOwner(1, 4));
61
+		assertEquals(territory, gocounting.getTerritory(1, 4));
62
+	}
63
+	
64
+	@Ignore("Remove to run test")
65
+	@Test
66
+	public void stoneNotTerritory5x5Board() {
67
+		GoCounting gocounting = new GoCounting(board5x5);
68
+		
69
+		Set<Point> territory = new HashSet<>();
70
+		
71
+		assertEquals(Player.NONE, gocounting.getTerritoryOwner(1, 1));
72
+		assertEquals(territory, gocounting.getTerritory(1, 1));
73
+	}
74
+	
75
+	@Ignore("Remove to run test")
76
+	@Test
77
+	public void invalidXTooLow5x5Board() {
78
+		GoCounting gocounting = new GoCounting(board5x5);
79
+		
80
+		expectedException.expect(IllegalArgumentException.class);
81
+		expectedException.expectMessage("Invalid coordinate");
82
+		
83
+		gocounting.getTerritory(-1, 1);
84
+	}
85
+	
86
+	@Ignore("Remove to run test")
87
+	@Test
88
+	public void invalidXTooHigh5x5Board() {
89
+		GoCounting gocounting = new GoCounting(board5x5);
90
+		
91
+		expectedException.expect(IllegalArgumentException.class);
92
+		expectedException.expectMessage("Invalid coordinate");
93
+		
94
+		gocounting.getTerritory(5, 1);
95
+	}
96
+	
97
+	@Ignore("Remove to run test")
98
+	@Test
99
+	public void invalidYTooLow5x5Board() {
100
+		GoCounting gocounting = new GoCounting(board5x5);
101
+		
102
+		expectedException.expect(IllegalArgumentException.class);
103
+		expectedException.expectMessage("Invalid coordinate");
104
+		
105
+		gocounting.getTerritory(1, -1);
106
+	}
107
+	
108
+	@Ignore("Remove to run test")
109
+	@Test
110
+	public void invalidYTooHigh5x5Board() {
111
+		GoCounting gocounting = new GoCounting(board5x5);
112
+		
113
+		expectedException.expect(IllegalArgumentException.class);
114
+		expectedException.expectMessage("Invalid coordinate");
115
+		
116
+		gocounting.getTerritory(1, 5);
117
+	}
118
+	
119
+	@Ignore("Remove to run test")
120
+	@Test
121
+	public void oneTerritoryIsWholeBoardTest() {
122
+		GoCounting gocounting = new GoCounting(" ");
123
+		
124
+		HashMap<String, Set<Point>> territories = new HashMap<>();
125
+		Set<Point> blackTerritory = new HashSet<>();
126
+		Set<Point> whiteTerritory = new HashSet<>();
127
+		Set<Point> noneTerritory = new HashSet<>();
128
+		noneTerritory.add(new Point(0, 0));
129
+		
130
+		territories.put("BLACK", blackTerritory);
131
+		territories.put("WHITE", whiteTerritory);
132
+		territories.put("NONE", noneTerritory);
133
+		
134
+		assertEquals(territories, gocounting.getTerritories());
135
+	}
136
+	
137
+	@Ignore("Remove to run test")
138
+	@Test
139
+	public void twoTerritoryRectangularBoardTest() {
140
+		GoCounting gocounting = new GoCounting(" BW \n BW ");
141
+		
142
+		Set<Point> blackTerritory = new HashSet<>();
143
+		blackTerritory.add(new Point(0, 0));
144
+		blackTerritory.add(new Point(0, 1));
145
+		
146
+		Set<Point> whiteTerritory = new HashSet<>();
147
+		whiteTerritory.add(new Point(3, 0));
148
+		whiteTerritory.add(new Point(3, 1));
149
+		
150
+		Set<Point> noneTerritory = new HashSet<>();
151
+		
152
+		HashMap<String, Set<Point>> territories = new HashMap<>();
153
+		territories.put("BLACK", blackTerritory);
154
+		territories.put("WHITE", whiteTerritory);
155
+		territories.put("NONE", noneTerritory);
156
+		
157
+		assertEquals(territories, gocounting.getTerritories());
158
+	}
159
+	
160
+	@Ignore("Remove to run test")
161
+	@Test
162
+	public void twoRegionRectangularBoardTest() {
163
+		GoCounting gocounting = new GoCounting(" B ");
164
+		
165
+		HashMap<String, Set<Point>> territories = new HashMap<>();
166
+		Set<Point> blackTerritory = new HashSet<>();
167
+		blackTerritory.add(new Point(0, 0));
168
+		blackTerritory.add(new Point(2, 0));
169
+		Set<Point> whiteTerritory = new HashSet<>();
170
+		Set<Point> noneTerritory = new HashSet<>();
171
+		
172
+		territories.put("BLACK", blackTerritory);
173
+		territories.put("WHITE", whiteTerritory);
174
+		territories.put("NONE", noneTerritory);
175
+		
176
+		assertEquals(territories, gocounting.getTerritories());
177
+	}
178
+}

+ 1
- 0
exercises/settings.gradle Просмотреть файл

@@ -31,6 +31,7 @@ include 'flatten-array'
31 31
 include 'food-chain'
32 32
 include 'forth'
33 33
 include 'gigasecond'
34
+include 'go-counting'
34 35
 include 'grade-school'
35 36
 include 'hamming'
36 37
 include 'hexadecimal'