Skip to content

Commit 5de4052

Browse files
author
Xiangyu Sun
committed
Initial Commit
0 parents  commit 5de4052

25 files changed

+727
-0
lines changed

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.DS_Store
2+
/.build
3+
/Packages
4+
/*.xcodeproj

.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>IDEDidComputeMac32BitWarning</key>
6+
<true/>
7+
</dict>
8+
</plist>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>SchemeUserState</key>
6+
<dict>
7+
<key>BowlingKit.xcscheme_^#shared#^_</key>
8+
<dict>
9+
<key>orderHint</key>
10+
<integer>0</integer>
11+
</dict>
12+
</dict>
13+
</dict>
14+
</plist>

Package.swift

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// swift-tools-version:5.1
2+
// The swift-tools-version declares the minimum version of Swift required to build this package.
3+
4+
import PackageDescription
5+
6+
let package = Package(
7+
name: "BowlingKit",
8+
products: [
9+
// Products define the executables and libraries produced by a package, and make them visible to other packages.
10+
.library(
11+
name: "BowlingKit",
12+
targets: ["BowlingKit"]),
13+
],
14+
dependencies: [
15+
// Dependencies declare other packages that this package depends on.
16+
// .package(url: /* package url */, from: "1.0.0"),
17+
],
18+
targets: [
19+
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
20+
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
21+
.target(
22+
name: "BowlingKit",
23+
dependencies: []),
24+
.testTarget(
25+
name: "BowlingKitTests",
26+
dependencies: ["BowlingKit"]),
27+
]
28+
)

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# BowlingKit
2+
3+
A description of this package.

Sources/BowlingKit/Frame.swift

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
//
2+
// Frame.swift
3+
// Bowlingure
4+
//
5+
// Created by 孙翔宇 on 4/17/19.
6+
// Copyright © 2019 孙翔宇. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
public final class Frame {
12+
13+
public static let maxiumPinsCount: UInt = 10
14+
15+
private(set) var ballKnockedDownRecord = [UInt]()
16+
17+
public var state: FrameState
18+
19+
weak var scoringFrame: Frame?
20+
21+
let lastFrame: Bool
22+
23+
public var calcualtedScore: UInt {
24+
return state.calcualtedScore(self)
25+
}
26+
27+
public var isCompleted: Bool {
28+
return state.isFrameCompleted(self)
29+
}
30+
31+
public var isCompletelyScored: Bool {
32+
return state.canBeScored(self)
33+
}
34+
35+
var pinsLeft: UInt {
36+
return Frame.maxiumPinsCount - (ballKnockedDownRecord.first ?? 0)
37+
}
38+
39+
public init(lastFrame: Bool = false) {
40+
self.lastFrame = lastFrame
41+
self.state = EmptyState()
42+
}
43+
44+
public func addPinsKnockedDown(_ count: UInt) {
45+
state.addPinsKnockedDown(count, frame: self)
46+
}
47+
48+
func addBallKnockedDownRecord(count: UInt) {
49+
ballKnockedDownRecord.append(count)
50+
}
51+
52+
func getNextBallKnockedDownRecord(count: Int) -> [UInt] {
53+
guard let scoringFrame = scoringFrame, count != 0 else { return [] }
54+
55+
var allFrames = scoringFrame.ballKnockedDownRecord
56+
57+
let ballsKnockDownNeeded = allFrames.count - count
58+
59+
if ballsKnockDownNeeded >= 0 {
60+
return Array(allFrames[..<count])
61+
}
62+
63+
guard let scoringFrameAfterNext = scoringFrame.scoringFrame else { return allFrames }
64+
let ballsMissed = abs(ballsKnockDownNeeded)
65+
66+
67+
allFrames.append(contentsOf: Array(scoringFrameAfterNext.ballKnockedDownRecord[..<ballsMissed]))
68+
return allFrames
69+
}
70+
71+
func getFirstBallRolledState() -> FrameState {
72+
return FirstBallRolledState()
73+
}
74+
75+
func getStrikeState() -> FrameState {
76+
return lastFrame ? FinalFrameStrikeState() : StrikeState()
77+
}
78+
79+
func getSpareState() -> FrameState {
80+
return lastFrame ? FinalFrameSpareState() : SpareState()
81+
}
82+
83+
func getMissedState() -> FrameState {
84+
return MissedState()
85+
}
86+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//
2+
// EmptyState.swift
3+
// Bowlingure
4+
//
5+
// Created by 孙翔宇 on 4/24/19.
6+
// Copyright © 2019 孙翔宇. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
public final class EmptyState: FrameState {
12+
public func addPinsKnockedDown(_ count: UInt, frame: Frame) {
13+
frame.state = count == Frame.maxiumPinsCount ? frame.getStrikeState() : frame.getFirstBallRolledState()
14+
frame.state.addPinsKnockedDown(count, frame: frame)
15+
}
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//
2+
// FirstBallRolledState.swift
3+
// Bowlingure
4+
//
5+
// Created by 孙翔宇 on 4/24/19.
6+
// Copyright © 2019 孙翔宇. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
public final class FirstBallRolledState: FrameState {
12+
public func addPinsKnockedDown(_ count: UInt, frame: Frame) {
13+
if frame.ballKnockedDownRecord.count == 0 {
14+
frame.addBallKnockedDownRecord(count: count)
15+
} else if count == frame.pinsLeft {
16+
frame.state = frame.getSpareState()
17+
frame.state.addPinsKnockedDown(count, frame: frame)
18+
} else {
19+
frame.state = frame.getMissedState()
20+
frame.state.addPinsKnockedDown(count, frame: frame)
21+
}
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//
2+
// FrameState.swift
3+
// Bowlingure
4+
//
5+
// Created by 孙翔宇 on 4/24/19.
6+
// Copyright © 2019 孙翔宇. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
public protocol FrameState {
12+
var maximumBallCount: UInt { get }
13+
var ballsRequiredForScoring: UInt { get }
14+
15+
func isFrameCompleted(_ frame: Frame) -> Bool
16+
func canBeScored(_ frame: Frame) -> Bool
17+
func calcualtedScore(_ frame: Frame) -> UInt
18+
func ballsForScoring(_ frame: Frame) -> [UInt]?
19+
func addPinsKnockedDown(_ count: UInt, frame: Frame)
20+
}
21+
22+
public extension FrameState {
23+
24+
func isFrameCompleted(_ frame: Frame) -> Bool {
25+
return false
26+
}
27+
28+
var maximumBallCount: UInt {
29+
return 2
30+
}
31+
32+
var ballsRequiredForScoring: UInt {
33+
return maximumBallCount
34+
}
35+
36+
func calcualtedScore(_ frame: Frame) -> UInt {
37+
return ballsForScoring(frame)?.sum() ?? 0
38+
}
39+
40+
func canBeScored(_ frame: Frame) -> Bool {
41+
return ballsForScoring(frame)?.count == ballsRequiredForScoring
42+
}
43+
44+
func ballsForScoring(_ frame: Frame) -> [UInt]? {
45+
return frame.ballKnockedDownRecord
46+
}
47+
}
48+
49+
public protocol CompleteFrameState: FrameState {}
50+
public extension CompleteFrameState {
51+
func isFrameCompleted(_ frame: Frame) -> Bool{
52+
return true
53+
}
54+
55+
func addPinsKnockedDown(_ count: UInt, frame: Frame) {
56+
guard frame.ballKnockedDownRecord.count < maximumBallCount else { return }
57+
frame.addBallKnockedDownRecord(count: count)
58+
}
59+
}
60+
61+
public protocol FinalFrameState: FrameState {}
62+
public extension FinalFrameState {
63+
var maximumBallCount: UInt {
64+
return 3
65+
}
66+
67+
func isFrameCompleted(_ frame: Frame) -> Bool{
68+
return canBeScored(frame)
69+
}
70+
71+
func addPinsKnockedDown(_ count: UInt, frame: Frame) {
72+
guard !canBeScored(frame) else { return }
73+
frame.addBallKnockedDownRecord(count: count)
74+
}
75+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//
2+
// FinalFrameStrikeState.swift
3+
// Bowlingure
4+
//
5+
// Created by 孙翔宇 on 4/24/19.
6+
// Copyright © 2019 孙翔宇. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
public final class FinalFrameStrikeState: FinalFrameState {}
12+
public final class FinalFrameSpareState: FinalFrameState {}
13+
14+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
//
2+
// MissedState.swift
3+
// Bowlingure
4+
//
5+
// Created by 孙翔宇 on 4/24/19.
6+
// Copyright © 2019 孙翔宇. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
public final class MissedState: CompleteFrameState {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//
2+
// SpareState.swift
3+
// Bowlingure
4+
//
5+
// Created by 孙翔宇 on 4/24/19.
6+
// Copyright © 2019 孙翔宇. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
public final class SpareState: CompleteFrameState {
12+
public func ballsForScoring(_ frame: Frame) -> [UInt]? {
13+
var frames = frame.ballKnockedDownRecord
14+
let firstBallOfNextFrame = frame.getNextBallKnockedDownRecord(count: 1)
15+
if !firstBallOfNextFrame.isEmpty {
16+
frames.append(contentsOf: firstBallOfNextFrame)
17+
}
18+
return frames
19+
}
20+
21+
public var ballsRequiredForScoring: UInt {
22+
return 3
23+
}
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//
2+
// StrikeState.swift
3+
// Bowlingure
4+
//
5+
// Created by 孙翔宇 on 4/24/19.
6+
// Copyright © 2019 孙翔宇. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
public final class StrikeState: CompleteFrameState {
12+
13+
public func ballsForScoring(_ frame: Frame) -> [UInt]? {
14+
15+
var frames = frame.ballKnockedDownRecord
16+
17+
let ballsMissing = ballsRequiredForScoring - frames.count
18+
if ballsMissing > 0 {
19+
frames.append(contentsOf: frame.getNextBallKnockedDownRecord(count: ballsMissing))
20+
}
21+
22+
return frames
23+
}
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+
}
34+
}

0 commit comments

Comments
 (0)