Skip to content

Commit 1a439d6

Browse files
committed
Handle multiple captures in the same move for the same player.
1 parent 3a82e96 commit 1a439d6

File tree

3 files changed

+94
-7
lines changed

3 files changed

+94
-7
lines changed

PGNParser/DraughtsMove.swift

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ public struct DraughtsPieceMove {
2020
let origin: BoardPosition
2121
/// The resting position of the piece after the move is made.
2222
let destination: BoardPosition
23+
/// A list of intermediate positions the piece passed through to get to its destination.
24+
let intermediatePositions: [BoardPosition]
2325
/// A flag to indicate if any opponenets pieces were captured during the move.
2426
let isCapture: Bool
2527

@@ -33,17 +35,20 @@ extension DraughtsPieceMove {
3335
This constructor simplifies the creation of a parser for the move.
3436
It should remain in an extension so that it does not replace the default constructor.
3537

36-
- parameter from: The location of the piece that is to be moved.
38+
- parameter origin: The location of the piece that is to be moved.
3739

38-
- parameter hasCapture: True if the piece captures another piece, false if none are captured.
40+
- parameter isCapture: True if the piece captures another piece, false if none are captured.
3941

40-
- parameter to: The location at which the piece resides after the move.
42+
- parameter intermediates: Any position which the piece moved through between its origin and destination.
43+
44+
- parameter destination: The location at which the piece resides after the move.
4145
*/
42-
init(origin: BoardPosition, isCapture: Bool, destination: BoardPosition) {
46+
init(origin: BoardPosition, isCapture: Bool, intermediates: [BoardPosition], destination: BoardPosition) {
4347

4448
self.origin = origin
45-
self.destination = destination
4649
self.isCapture = isCapture
50+
self.intermediatePositions = intermediates
51+
self.destination = destination
4752
}
4853

4954
}

PGNParser/DraughtsNotationParser.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,19 @@ struct DraughtsNotationParser {
2525

2626
let singlePieceMove = curry(DraughtsPieceMove.init) <^> integerNumber
2727
<*> lowercaseXIsTrue <|> hyphenIsFalse
28+
<*> (integerNumber <* lowercaseX).zeroOneOrMany
2829
<*> integerNumber
2930

3031
let moveRound = numberWithPoint *> twoPlayerTurn(singlePieceMove)
3132

3233
return moveRound.oneOrMany
3334
}
3435

36+
/// A parser which will succeed on a lowercase 'x' and fail on any other input token.
37+
private static let lowercaseX = character(isEqualTo: "x")
38+
3539
/// A parser which resolves lowercase x to a boolean true and fails on all other input strings.
36-
private static let lowercaseXIsTrue = character(isEqualTo: "x").map { _ in return true }
40+
private static let lowercaseXIsTrue = lowercaseX.map { _ in return true }
3741

3842
/// A parser which resolves a hyphen to a boolean false and fails on all other input strings.
3943
private static let hyphenIsFalse = character(isEqualTo: "-").map { _ in return false }

PGNParserTests/DraughtsMoveTests.swift

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import XCTest
1010
@testable import PGNParser
1111

12+
// swiftlint:disable type_body_length
1213
class DraughtsMoveTests: XCTestCase {
1314

1415
func testSingleValidMoveWhiteOnly() {
@@ -26,6 +27,7 @@ class DraughtsMoveTests: XCTestCase {
2627
XCTAssertEqual(9, firstMove.white.origin)
2728
XCTAssertEqual(14, firstMove.white.destination)
2829
XCTAssertEqual(false, firstMove.white.isCapture)
30+
XCTAssertEqual([], firstMove.white.intermediatePositions)
2931

3032
XCTAssertNil(firstMove.black)
3133
}
@@ -52,10 +54,12 @@ class DraughtsMoveTests: XCTestCase {
5254
XCTAssertEqual(9, firstMove.white.origin)
5355
XCTAssertEqual(14, firstMove.white.destination)
5456
XCTAssertEqual(false, firstMove.white.isCapture)
57+
XCTAssertEqual([], firstMove.white.intermediatePositions)
5558

5659
XCTAssertEqual(23, firstMove.black?.origin)
5760
XCTAssertEqual(18, firstMove.black?.destination)
5861
XCTAssertEqual(false, firstMove.black?.isCapture)
62+
XCTAssertEqual([], firstMove.black?.intermediatePositions ?? [0])
5963
}
6064

6165
case .failure(_):
@@ -77,10 +81,12 @@ class DraughtsMoveTests: XCTestCase {
7781
XCTAssertEqual(9, firstMove.white.origin)
7882
XCTAssertEqual(14, firstMove.white.destination)
7983
XCTAssertEqual(false, firstMove.white.isCapture)
84+
XCTAssertEqual([], firstMove.white.intermediatePositions)
8085

8186
XCTAssertEqual(23, firstMove.black?.origin)
8287
XCTAssertEqual(18, firstMove.black?.destination)
8388
XCTAssertEqual(false, firstMove.black?.isCapture)
89+
XCTAssertEqual([], firstMove.black?.intermediatePositions ?? [0])
8490
}
8591

8692
case .failure(_):
@@ -102,20 +108,24 @@ class DraughtsMoveTests: XCTestCase {
102108
XCTAssertEqual(9, firstMove.white.origin)
103109
XCTAssertEqual(14, firstMove.white.destination)
104110
XCTAssertEqual(false, firstMove.white.isCapture)
111+
XCTAssertEqual([], firstMove.white.intermediatePositions)
105112

106113
XCTAssertEqual(23, firstMove.black?.origin)
107114
XCTAssertEqual(18, firstMove.black?.destination)
108115
XCTAssertEqual(false, firstMove.black?.isCapture)
116+
XCTAssertEqual([], firstMove.black?.intermediatePositions ?? [0])
109117
}
110118

111119
if let lastMove = moves.last {
112120
XCTAssertEqual(14, lastMove.white.origin)
113121
XCTAssertEqual(17, lastMove.white.destination)
114122
XCTAssertEqual(false, lastMove.white.isCapture)
123+
XCTAssertEqual([], lastMove.white.intermediatePositions)
115124

116125
XCTAssertEqual(18, lastMove.black?.origin)
117126
XCTAssertEqual(15, lastMove.black?.destination)
118127
XCTAssertEqual(false, lastMove.black?.isCapture)
128+
XCTAssertEqual([], lastMove.black?.intermediatePositions ?? [0])
119129
}
120130

121131
case .failure(_):
@@ -137,16 +147,19 @@ class DraughtsMoveTests: XCTestCase {
137147
XCTAssertEqual(9, firstMove.white.origin)
138148
XCTAssertEqual(14, firstMove.white.destination)
139149
XCTAssertEqual(false, firstMove.white.isCapture)
150+
XCTAssertEqual([], firstMove.white.intermediatePositions)
140151

141152
XCTAssertEqual(23, firstMove.black?.origin)
142153
XCTAssertEqual(18, firstMove.black?.destination)
143154
XCTAssertEqual(false, firstMove.black?.isCapture)
155+
XCTAssertEqual([], firstMove.black?.intermediatePositions ?? [0])
144156
}
145157

146158
if let lastMove = moves.last {
147159
XCTAssertEqual(14, lastMove.white.origin)
148160
XCTAssertEqual(17, lastMove.white.destination)
149161
XCTAssertEqual(false, lastMove.white.isCapture)
162+
XCTAssertEqual([], lastMove.white.intermediatePositions)
150163

151164
XCTAssertNil(lastMove.black)
152165
}
@@ -172,10 +185,68 @@ class DraughtsMoveTests: XCTestCase {
172185
XCTAssertEqual(9, firstMove.white.origin)
173186
XCTAssertEqual(14, firstMove.white.destination)
174187
XCTAssertEqual(true, firstMove.white.isCapture)
188+
XCTAssertEqual([], firstMove.white.intermediatePositions)
175189

176190
XCTAssertEqual(23, firstMove.black?.origin)
177191
XCTAssertEqual(18, firstMove.black?.destination)
178192
XCTAssertEqual(true, firstMove.black?.isCapture)
193+
XCTAssertEqual([], firstMove.black?.intermediatePositions ?? [0])
194+
}
195+
196+
case .failure(_):
197+
XCTFail()
198+
}
199+
}
200+
201+
func testValidMovesWithMultipleCaptures() {
202+
203+
// Both players move and as they do they capture other pieces.
204+
let multipleMoves = "1. 9x14x17 23x18x12"
205+
206+
switch DraughtsMove.parse(fromPortableGameNotation: multipleMoves) {
207+
case .success(let moves, let tail):
208+
209+
XCTAssertEqual(1, moves.count)
210+
XCTAssertEqual("", String(tail))
211+
212+
if let firstMove = moves.first {
213+
XCTAssertEqual(9, firstMove.white.origin)
214+
XCTAssertEqual(17, firstMove.white.destination)
215+
XCTAssertEqual(true, firstMove.white.isCapture)
216+
XCTAssertEqual([14], firstMove.white.intermediatePositions)
217+
218+
XCTAssertEqual(23, firstMove.black?.origin)
219+
XCTAssertEqual(12, firstMove.black?.destination)
220+
XCTAssertEqual(true, firstMove.black?.isCapture)
221+
XCTAssertEqual([18], firstMove.black?.intermediatePositions ?? [])
222+
}
223+
224+
case .failure(_):
225+
XCTFail()
226+
}
227+
}
228+
229+
func testValidMovesWithManyCaptures() {
230+
231+
// Both players move and as they do they capture another pieces.
232+
let multipleMoves = "1. 9x14x17x21 23x18x12x9x6"
233+
234+
switch DraughtsMove.parse(fromPortableGameNotation: multipleMoves) {
235+
case .success(let moves, let tail):
236+
237+
XCTAssertEqual(1, moves.count)
238+
XCTAssertEqual("", String(tail))
239+
240+
if let firstMove = moves.first {
241+
XCTAssertEqual(9, firstMove.white.origin)
242+
XCTAssertEqual(21, firstMove.white.destination)
243+
XCTAssertEqual(true, firstMove.white.isCapture)
244+
XCTAssertEqual([14, 17], firstMove.white.intermediatePositions)
245+
246+
XCTAssertEqual(23, firstMove.black?.origin)
247+
XCTAssertEqual(6, firstMove.black?.destination)
248+
XCTAssertEqual(true, firstMove.black?.isCapture)
249+
XCTAssertEqual([18, 12, 9], firstMove.black?.intermediatePositions ?? [])
179250
}
180251

181252
case .failure(_):
@@ -203,10 +274,12 @@ class DraughtsMoveTests: XCTestCase {
203274
XCTAssertEqual(9, firstMove.white.origin)
204275
XCTAssertEqual(14, firstMove.white.destination)
205276
XCTAssertEqual(false, firstMove.white.isCapture)
277+
XCTAssertEqual([], firstMove.white.intermediatePositions)
206278

207279
XCTAssertEqual(23, firstMove.black?.origin)
208280
XCTAssertEqual(18, firstMove.black?.destination)
209281
XCTAssertEqual(false, firstMove.black?.isCapture)
282+
XCTAssertEqual([], firstMove.black?.intermediatePositions ?? [0])
210283
} else {
211284
XCTFail()
212285
}
@@ -215,15 +288,18 @@ class DraughtsMoveTests: XCTestCase {
215288
XCTAssertEqual(10, moveWithCapture.white.origin)
216289
XCTAssertEqual(19, moveWithCapture.white.destination)
217290
XCTAssertEqual(true, moveWithCapture.white.isCapture)
291+
XCTAssertEqual([], moveWithCapture.white.intermediatePositions)
218292

219293
XCTAssertEqual(23, moveWithCapture.black?.origin)
220294
XCTAssertEqual(16, moveWithCapture.black?.destination)
221295
XCTAssertEqual(true, moveWithCapture.black?.isCapture)
296+
XCTAssertEqual([], moveWithCapture.black?.intermediatePositions ?? [0])
222297

223298
if let lastMove = moves.last {
224299
XCTAssertEqual(18, lastMove.white.origin)
225300
XCTAssertEqual(22, lastMove.white.destination)
226301
XCTAssertEqual(false, lastMove.white.isCapture)
302+
XCTAssertEqual([], lastMove.white.intermediatePositions)
227303

228304
XCTAssertNil(lastMove.black)
229305
} else {
@@ -279,18 +355,19 @@ class DraughtsMoveTests: XCTestCase {
279355

280356
// NOTE: This succeeds because there are valid results at the start of the input sting.
281357
// It may be deemed as unexpected and it may be better to find a way to make this fail.
282-
283358
XCTAssertEqual(1, moves.count)
284359
XCTAssertEqual(" 14x23 27x18", String(tail))
285360

286361
if let firstMove = moves.first {
287362
XCTAssertEqual(9, firstMove.white.origin)
288363
XCTAssertEqual(14, firstMove.white.destination)
289364
XCTAssertEqual(false, firstMove.white.isCapture)
365+
XCTAssertEqual([], firstMove.white.intermediatePositions)
290366

291367
XCTAssertEqual(23, firstMove.black?.origin)
292368
XCTAssertEqual(18, firstMove.black?.destination)
293369
XCTAssertEqual(false, firstMove.black?.isCapture)
370+
XCTAssertEqual([], firstMove.black?.intermediatePositions ?? [0])
294371
}
295372

296373
case .failure(_):
@@ -299,3 +376,4 @@ class DraughtsMoveTests: XCTestCase {
299376
}
300377

301378
}
379+
// swiftlint:enable type_body_length

0 commit comments

Comments
 (0)