Skip to content

Commit 7ab07bc

Browse files
author
Xiangyu Sun
committed
1. code style improvements and remove duplicated code
2. added a new feature that allows the game generate the record of a full game
1 parent 5de4052 commit 7ab07bc

17 files changed

+140
-164
lines changed

.swiftpm/xcode/xcuserdata/sunxiangyu.xcuserdatad/xcschemes/xcschememanagement.plist

+13
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,18 @@
1010
<integer>0</integer>
1111
</dict>
1212
</dict>
13+
<key>SuppressBuildableAutocreation</key>
14+
<dict>
15+
<key>BowlingKit</key>
16+
<dict>
17+
<key>primary</key>
18+
<true/>
19+
</dict>
20+
<key>BowlingKitTests</key>
21+
<dict>
22+
<key>primary</key>
23+
<true/>
24+
</dict>
25+
</dict>
1326
</dict>
1427
</plist>

Sources/BowlingKit/Frame.swift

+19-36
Original file line numberDiff line numberDiff line change
@@ -11,43 +11,30 @@ import Foundation
1111
public final class Frame {
1212

1313
public static let maxiumPinsCount: UInt = 10
14+
public var state: FrameState
1415

15-
private(set) var ballKnockedDownRecord = [UInt]()
16+
public var calcualtedScore: UInt { state.calcualtedScore(self) }
1617

17-
public var state: FrameState
18+
public var isCompleted: Bool { state.isFrameCompleted(self) }
1819

19-
weak var scoringFrame: Frame?
20+
public var isCompletelyScored: Bool { state.canBeScored(self) }
2021

21-
let lastFrame: Bool
22-
23-
public var calcualtedScore: UInt {
24-
return state.calcualtedScore(self)
25-
}
22+
weak var scoringFrame: Frame?
2623

27-
public var isCompleted: Bool {
28-
return state.isFrameCompleted(self)
29-
}
24+
let isLastFrame: Bool
3025

31-
public var isCompletelyScored: Bool {
32-
return state.canBeScored(self)
33-
}
26+
var pinsLeft: UInt { Frame.maxiumPinsCount - (ballKnockedDownRecord.first ?? 0) }
3427

35-
var pinsLeft: UInt {
36-
return Frame.maxiumPinsCount - (ballKnockedDownRecord.first ?? 0)
37-
}
28+
private(set) var ballKnockedDownRecord = [UInt]()
3829

3930
public init(lastFrame: Bool = false) {
40-
self.lastFrame = lastFrame
31+
self.isLastFrame = lastFrame
4132
self.state = EmptyState()
4233
}
4334

44-
public func addPinsKnockedDown(_ count: UInt) {
45-
state.addPinsKnockedDown(count, frame: self)
46-
}
35+
public func addPinsKnockedDown(_ count: UInt) { state.addPinsKnockedDown(count, frame: self) }
4736

48-
func addBallKnockedDownRecord(count: UInt) {
49-
ballKnockedDownRecord.append(count)
50-
}
37+
func addBallKnockedDownRecord(count: UInt) { ballKnockedDownRecord.append(count) }
5138

5239
func getNextBallKnockedDownRecord(count: Int) -> [UInt] {
5340
guard let scoringFrame = scoringFrame, count != 0 else { return [] }
@@ -68,19 +55,15 @@ public final class Frame {
6855
return allFrames
6956
}
7057

71-
func getFirstBallRolledState() -> FrameState {
72-
return FirstBallRolledState()
73-
}
58+
func getFirstBallRolledState() -> FrameState { FirstBallRolledState() }
7459

75-
func getStrikeState() -> FrameState {
76-
return lastFrame ? FinalFrameStrikeState() : StrikeState()
77-
}
60+
func getStrikeState() -> FrameState { isLastFrame ? FinalFrameStrikeState() : StrikeState() }
7861

79-
func getSpareState() -> FrameState {
80-
return lastFrame ? FinalFrameSpareState() : SpareState()
81-
}
62+
func getSpareState() -> FrameState { isLastFrame ? FinalFrameSpareState() : SpareState() }
8263

83-
func getMissedState() -> FrameState {
84-
return MissedState()
85-
}
64+
func getMissedState() -> FrameState { MissedState() }
65+
}
66+
67+
extension Frame : CustomDebugStringConvertible {
68+
public var debugDescription: String { ballKnockedDownRecord.debugDescription }
8669
}

Sources/BowlingKit/FrameState/EmptyState.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import Foundation
1010

11-
public final class EmptyState: FrameState {
11+
public struct EmptyState: FrameState {
1212
public func addPinsKnockedDown(_ count: UInt, frame: Frame) {
1313
frame.state = count == Frame.maxiumPinsCount ? frame.getStrikeState() : frame.getFirstBallRolledState()
1414
frame.state.addPinsKnockedDown(count, frame: frame)

Sources/BowlingKit/FrameState/FirstBallRolledState.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import Foundation
1010

11-
public final class FirstBallRolledState: FrameState {
11+
public struct FirstBallRolledState: FrameState {
1212
public func addPinsKnockedDown(_ count: UInt, frame: Frame) {
1313
if frame.ballKnockedDownRecord.count == 0 {
1414
frame.addBallKnockedDownRecord(count: count)

Sources/BowlingKit/FrameState/FrameState.swift

+18-28
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import Foundation
1111
public protocol FrameState {
1212
var maximumBallCount: UInt { get }
1313
var ballsRequiredForScoring: UInt { get }
14-
1514
func isFrameCompleted(_ frame: Frame) -> Bool
1615
func canBeScored(_ frame: Frame) -> Bool
1716
func calcualtedScore(_ frame: Frame) -> UInt
@@ -21,37 +20,25 @@ public protocol FrameState {
2120

2221
public extension FrameState {
2322

24-
func isFrameCompleted(_ frame: Frame) -> Bool {
25-
return false
26-
}
23+
func isFrameCompleted(_ frame: Frame) -> Bool { false }
2724

28-
var maximumBallCount: UInt {
29-
return 2
30-
}
25+
var maximumBallCount: UInt { 2 }
3126

32-
var ballsRequiredForScoring: UInt {
33-
return maximumBallCount
34-
}
27+
var ballsRequiredForScoring: UInt { maximumBallCount }
3528

36-
func calcualtedScore(_ frame: Frame) -> UInt {
37-
return ballsForScoring(frame)?.sum() ?? 0
38-
}
29+
func calcualtedScore(_ frame: Frame) -> UInt { ballsForScoring(frame)?.sum() ?? 0 }
3930

40-
func canBeScored(_ frame: Frame) -> Bool {
41-
return ballsForScoring(frame)?.count == ballsRequiredForScoring
42-
}
31+
func canBeScored(_ frame: Frame) -> Bool { ballsForScoring(frame)?.count == ballsRequiredForScoring }
4332

44-
func ballsForScoring(_ frame: Frame) -> [UInt]? {
45-
return frame.ballKnockedDownRecord
46-
}
33+
func ballsForScoring(_ frame: Frame) -> [UInt]? { frame.ballKnockedDownRecord }
4734
}
4835

4936
public protocol CompleteFrameState: FrameState {}
5037
public extension CompleteFrameState {
51-
func isFrameCompleted(_ frame: Frame) -> Bool{
52-
return true
53-
}
38+
var ballsRequiredForScoring: UInt { 3 }
5439

40+
func isFrameCompleted(_ frame: Frame) -> Bool { true }
41+
5542
func addPinsKnockedDown(_ count: UInt, frame: Frame) {
5643
guard frame.ballKnockedDownRecord.count < maximumBallCount else { return }
5744
frame.addBallKnockedDownRecord(count: count)
@@ -60,16 +47,19 @@ public extension CompleteFrameState {
6047

6148
public protocol FinalFrameState: FrameState {}
6249
public extension FinalFrameState {
63-
var maximumBallCount: UInt {
64-
return 3
65-
}
50+
var maximumBallCount: UInt { 3 }
6651

67-
func isFrameCompleted(_ frame: Frame) -> Bool{
68-
return canBeScored(frame)
69-
}
52+
func isFrameCompleted(_ frame: Frame) -> Bool { canBeScored(frame) }
7053

7154
func addPinsKnockedDown(_ count: UInt, frame: Frame) {
7255
guard !canBeScored(frame) else { return }
7356
frame.addBallKnockedDownRecord(count: count)
7457
}
7558
}
59+
60+
public struct MissedState: CompleteFrameState {
61+
public var ballsRequiredForScoring: UInt { maximumBallCount }
62+
}
63+
64+
public struct FinalFrameStrikeState: FinalFrameState {}
65+
public struct FinalFrameSpareState: FinalFrameState {}

Sources/BowlingKit/FrameState/LastFrameStrikeState.swift

-14
This file was deleted.

Sources/BowlingKit/FrameState/MissedState.swift

-11
This file was deleted.

Sources/BowlingKit/FrameState/SpareState.swift

+1-5
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import Foundation
1010

11-
public final class SpareState: CompleteFrameState {
11+
public struct SpareState: CompleteFrameState {
1212
public func ballsForScoring(_ frame: Frame) -> [UInt]? {
1313
var frames = frame.ballKnockedDownRecord
1414
let firstBallOfNextFrame = frame.getNextBallKnockedDownRecord(count: 1)
@@ -17,8 +17,4 @@ public final class SpareState: CompleteFrameState {
1717
}
1818
return frames
1919
}
20-
21-
public var ballsRequiredForScoring: UInt {
22-
return 3
23-
}
2420
}

Sources/BowlingKit/FrameState/StrikeState.swift

+3-12
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@
88

99
import Foundation
1010

11-
public final class StrikeState: CompleteFrameState {
12-
11+
public struct StrikeState: CompleteFrameState {
12+
public var maximumBallCount: UInt { 1 }
13+
1314
public func ballsForScoring(_ frame: Frame) -> [UInt]? {
1415

1516
var frames = frame.ballKnockedDownRecord
@@ -21,14 +22,4 @@ public final class StrikeState: CompleteFrameState {
2122

2223
return frames
2324
}
24-
25-
26-
public var ballsRequiredForScoring: UInt {
27-
return 3
28-
}
29-
30-
public func addPinsKnockedDown(_ count: UInt, frame: Frame) {
31-
guard frame.ballKnockedDownRecord.count == 0 else { return }
32-
frame.addBallKnockedDownRecord(count: count)
33-
}
3425
}

Sources/BowlingKit/Game.swift

+37-17
Original file line numberDiff line numberDiff line change
@@ -8,46 +8,67 @@
88

99
import Foundation
1010

11+
enum GameError : Error {
12+
case gameFinished
13+
}
14+
1115
public struct Game {
16+
1217
public static let maximumFrameCount: UInt = 10
1318

1419
public private(set) var frames = [Frame]()
1520

16-
public var completelyScoredFames: [Frame] {
17-
return frames.filter{ $0.isCompletelyScored }
18-
}
21+
public var completelyScoredFames: [Frame] { frames.filter{ $0.isCompletelyScored } }
1922

2023
public var nextBallFrameNumber: UInt {
2124
let currentFrameDelta: UInt = (frames.last?.isCompleted ?? false) ? 1 : 0
2225
return min(Game.maximumFrameCount, UInt(frames.count) + currentFrameDelta)
2326
}
2427

25-
public var isGameover: Bool {
26-
return completelyScoredFames.count >= Game.maximumFrameCount
27-
}
28+
public var isGameover: Bool { completelyScoredFames.count >= Game.maximumFrameCount }
2829

29-
public init() {
30+
public init() {}
31+
32+
public mutating func rolledWith(pinsKnockedDown: UInt) throws {
33+
guard !isGameover else { throw GameError.gameFinished }
34+
35+
if let newFrame = makeNextFrame() {
36+
frames.append(newFrame)
37+
}
3038

39+
frames.last?.addPinsKnockedDown(pinsKnockedDown)
3140
}
3241

33-
public mutating func rolledWith(pinsKnockedDown: UInt) {
34-
guard !isGameover else { return }
35-
42+
func makeNextFrame() -> Frame? {
3643
if frames.isEmpty || frames.last?.isCompleted == true {
37-
let newFrame = Frame(lastFrame: frames.count == 9)
44+
let newFrame = Frame(lastFrame: frames.count == Game.maximumFrameCount - 1 )
3845

39-
if frames.last?.state is StrikeState || frames.last?.state is SpareState{
46+
if frames.last?.state is StrikeState || frames.last?.state is SpareState {
4047
frames.last?.scoringFrame = newFrame
4148
}
4249

43-
frames.append(newFrame)
50+
return newFrame
4451
}
4552

46-
frames.last?.addPinsKnockedDown(pinsKnockedDown)
53+
return nil
54+
}
55+
56+
public mutating func rolledWith(pinsKnockedDownSequence: [UInt]) throws {
57+
try pinsKnockedDownSequence.forEach{ try self.rolledWith(pinsKnockedDown: $0) }
4758
}
4859

49-
public mutating func rolledWith(pinsKnockedDownSequence: [UInt]) {
50-
pinsKnockedDownSequence.forEach{ self.rolledWith(pinsKnockedDown: $0) }
60+
mutating func rollNextBall() throws {
61+
if let lastFrame = frames.last, !lastFrame.isCompleted {
62+
try rolledWith(pinsKnockedDown: UInt.random(in: 0...lastFrame.pinsLeft))
63+
} else {
64+
try rolledWith(pinsKnockedDown: UInt.random(in: 0...10))
65+
}
66+
}
67+
68+
public mutating func generateFullGame() throws {
69+
while !isGameover {
70+
try rollNextBall()
71+
}
5172
}
5273
}
5374

@@ -62,4 +83,3 @@ extension ArraySlice where Element == Frame {
6283
return map{ $0.calcualtedScore }
6384
}
6485
}
65-

0 commit comments

Comments
 (0)