Skip to content

Commit

Permalink
1. code style improvements and remove duplicated code
Browse files Browse the repository at this point in the history
2. added a new feature that allows the game generate the record of a full game
  • Loading branch information
Xiangyu Sun committed Jun 21, 2019
1 parent 5de4052 commit 7ab07bc
Show file tree
Hide file tree
Showing 17 changed files with 140 additions and 164 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,18 @@
<integer>0</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>BowlingKit</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>BowlingKitTests</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>
55 changes: 19 additions & 36 deletions Sources/BowlingKit/Frame.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,43 +11,30 @@ import Foundation
public final class Frame {

public static let maxiumPinsCount: UInt = 10
public var state: FrameState

private(set) var ballKnockedDownRecord = [UInt]()
public var calcualtedScore: UInt { state.calcualtedScore(self) }

public var state: FrameState
public var isCompleted: Bool { state.isFrameCompleted(self) }

weak var scoringFrame: Frame?
public var isCompletelyScored: Bool { state.canBeScored(self) }

let lastFrame: Bool

public var calcualtedScore: UInt {
return state.calcualtedScore(self)
}
weak var scoringFrame: Frame?

public var isCompleted: Bool {
return state.isFrameCompleted(self)
}
let isLastFrame: Bool

public var isCompletelyScored: Bool {
return state.canBeScored(self)
}
var pinsLeft: UInt { Frame.maxiumPinsCount - (ballKnockedDownRecord.first ?? 0) }

var pinsLeft: UInt {
return Frame.maxiumPinsCount - (ballKnockedDownRecord.first ?? 0)
}
private(set) var ballKnockedDownRecord = [UInt]()

public init(lastFrame: Bool = false) {
self.lastFrame = lastFrame
self.isLastFrame = lastFrame
self.state = EmptyState()
}

public func addPinsKnockedDown(_ count: UInt) {
state.addPinsKnockedDown(count, frame: self)
}
public func addPinsKnockedDown(_ count: UInt) { state.addPinsKnockedDown(count, frame: self) }

func addBallKnockedDownRecord(count: UInt) {
ballKnockedDownRecord.append(count)
}
func addBallKnockedDownRecord(count: UInt) { ballKnockedDownRecord.append(count) }

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

func getFirstBallRolledState() -> FrameState {
return FirstBallRolledState()
}
func getFirstBallRolledState() -> FrameState { FirstBallRolledState() }

func getStrikeState() -> FrameState {
return lastFrame ? FinalFrameStrikeState() : StrikeState()
}
func getStrikeState() -> FrameState { isLastFrame ? FinalFrameStrikeState() : StrikeState() }

func getSpareState() -> FrameState {
return lastFrame ? FinalFrameSpareState() : SpareState()
}
func getSpareState() -> FrameState { isLastFrame ? FinalFrameSpareState() : SpareState() }

func getMissedState() -> FrameState {
return MissedState()
}
func getMissedState() -> FrameState { MissedState() }
}

extension Frame : CustomDebugStringConvertible {
public var debugDescription: String { ballKnockedDownRecord.debugDescription }
}
2 changes: 1 addition & 1 deletion Sources/BowlingKit/FrameState/EmptyState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import Foundation

public final class EmptyState: FrameState {
public struct EmptyState: FrameState {
public func addPinsKnockedDown(_ count: UInt, frame: Frame) {
frame.state = count == Frame.maxiumPinsCount ? frame.getStrikeState() : frame.getFirstBallRolledState()
frame.state.addPinsKnockedDown(count, frame: frame)
Expand Down
2 changes: 1 addition & 1 deletion Sources/BowlingKit/FrameState/FirstBallRolledState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import Foundation

public final class FirstBallRolledState: FrameState {
public struct FirstBallRolledState: FrameState {
public func addPinsKnockedDown(_ count: UInt, frame: Frame) {
if frame.ballKnockedDownRecord.count == 0 {
frame.addBallKnockedDownRecord(count: count)
Expand Down
46 changes: 18 additions & 28 deletions Sources/BowlingKit/FrameState/FrameState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import Foundation
public protocol FrameState {
var maximumBallCount: UInt { get }
var ballsRequiredForScoring: UInt { get }

func isFrameCompleted(_ frame: Frame) -> Bool
func canBeScored(_ frame: Frame) -> Bool
func calcualtedScore(_ frame: Frame) -> UInt
Expand All @@ -21,37 +20,25 @@ public protocol FrameState {

public extension FrameState {

func isFrameCompleted(_ frame: Frame) -> Bool {
return false
}
func isFrameCompleted(_ frame: Frame) -> Bool { false }

var maximumBallCount: UInt {
return 2
}
var maximumBallCount: UInt { 2 }

var ballsRequiredForScoring: UInt {
return maximumBallCount
}
var ballsRequiredForScoring: UInt { maximumBallCount }

func calcualtedScore(_ frame: Frame) -> UInt {
return ballsForScoring(frame)?.sum() ?? 0
}
func calcualtedScore(_ frame: Frame) -> UInt { ballsForScoring(frame)?.sum() ?? 0 }

func canBeScored(_ frame: Frame) -> Bool {
return ballsForScoring(frame)?.count == ballsRequiredForScoring
}
func canBeScored(_ frame: Frame) -> Bool { ballsForScoring(frame)?.count == ballsRequiredForScoring }

func ballsForScoring(_ frame: Frame) -> [UInt]? {
return frame.ballKnockedDownRecord
}
func ballsForScoring(_ frame: Frame) -> [UInt]? { frame.ballKnockedDownRecord }
}

public protocol CompleteFrameState: FrameState {}
public extension CompleteFrameState {
func isFrameCompleted(_ frame: Frame) -> Bool{
return true
}
var ballsRequiredForScoring: UInt { 3 }

func isFrameCompleted(_ frame: Frame) -> Bool { true }

func addPinsKnockedDown(_ count: UInt, frame: Frame) {
guard frame.ballKnockedDownRecord.count < maximumBallCount else { return }
frame.addBallKnockedDownRecord(count: count)
Expand All @@ -60,16 +47,19 @@ public extension CompleteFrameState {

public protocol FinalFrameState: FrameState {}
public extension FinalFrameState {
var maximumBallCount: UInt {
return 3
}
var maximumBallCount: UInt { 3 }

func isFrameCompleted(_ frame: Frame) -> Bool{
return canBeScored(frame)
}
func isFrameCompleted(_ frame: Frame) -> Bool { canBeScored(frame) }

func addPinsKnockedDown(_ count: UInt, frame: Frame) {
guard !canBeScored(frame) else { return }
frame.addBallKnockedDownRecord(count: count)
}
}

public struct MissedState: CompleteFrameState {
public var ballsRequiredForScoring: UInt { maximumBallCount }
}

public struct FinalFrameStrikeState: FinalFrameState {}
public struct FinalFrameSpareState: FinalFrameState {}
14 changes: 0 additions & 14 deletions Sources/BowlingKit/FrameState/LastFrameStrikeState.swift

This file was deleted.

11 changes: 0 additions & 11 deletions Sources/BowlingKit/FrameState/MissedState.swift

This file was deleted.

6 changes: 1 addition & 5 deletions Sources/BowlingKit/FrameState/SpareState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import Foundation

public final class SpareState: CompleteFrameState {
public struct SpareState: CompleteFrameState {
public func ballsForScoring(_ frame: Frame) -> [UInt]? {
var frames = frame.ballKnockedDownRecord
let firstBallOfNextFrame = frame.getNextBallKnockedDownRecord(count: 1)
Expand All @@ -17,8 +17,4 @@ public final class SpareState: CompleteFrameState {
}
return frames
}

public var ballsRequiredForScoring: UInt {
return 3
}
}
15 changes: 3 additions & 12 deletions Sources/BowlingKit/FrameState/StrikeState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@

import Foundation

public final class StrikeState: CompleteFrameState {

public struct StrikeState: CompleteFrameState {
public var maximumBallCount: UInt { 1 }

public func ballsForScoring(_ frame: Frame) -> [UInt]? {

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

return frames
}


public var ballsRequiredForScoring: UInt {
return 3
}

public func addPinsKnockedDown(_ count: UInt, frame: Frame) {
guard frame.ballKnockedDownRecord.count == 0 else { return }
frame.addBallKnockedDownRecord(count: count)
}
}
54 changes: 37 additions & 17 deletions Sources/BowlingKit/Game.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,46 +8,67 @@

import Foundation

enum GameError : Error {
case gameFinished
}

public struct Game {

public static let maximumFrameCount: UInt = 10

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

public var completelyScoredFames: [Frame] {
return frames.filter{ $0.isCompletelyScored }
}
public var completelyScoredFames: [Frame] { frames.filter{ $0.isCompletelyScored } }

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

public var isGameover: Bool {
return completelyScoredFames.count >= Game.maximumFrameCount
}
public var isGameover: Bool { completelyScoredFames.count >= Game.maximumFrameCount }

public init() {
public init() {}

public mutating func rolledWith(pinsKnockedDown: UInt) throws {
guard !isGameover else { throw GameError.gameFinished }

if let newFrame = makeNextFrame() {
frames.append(newFrame)
}

frames.last?.addPinsKnockedDown(pinsKnockedDown)
}

public mutating func rolledWith(pinsKnockedDown: UInt) {
guard !isGameover else { return }

func makeNextFrame() -> Frame? {
if frames.isEmpty || frames.last?.isCompleted == true {
let newFrame = Frame(lastFrame: frames.count == 9)
let newFrame = Frame(lastFrame: frames.count == Game.maximumFrameCount - 1 )

if frames.last?.state is StrikeState || frames.last?.state is SpareState{
if frames.last?.state is StrikeState || frames.last?.state is SpareState {
frames.last?.scoringFrame = newFrame
}

frames.append(newFrame)
return newFrame
}

frames.last?.addPinsKnockedDown(pinsKnockedDown)
return nil
}

public mutating func rolledWith(pinsKnockedDownSequence: [UInt]) throws {
try pinsKnockedDownSequence.forEach{ try self.rolledWith(pinsKnockedDown: $0) }
}

public mutating func rolledWith(pinsKnockedDownSequence: [UInt]) {
pinsKnockedDownSequence.forEach{ self.rolledWith(pinsKnockedDown: $0) }
mutating func rollNextBall() throws {
if let lastFrame = frames.last, !lastFrame.isCompleted {
try rolledWith(pinsKnockedDown: UInt.random(in: 0...lastFrame.pinsLeft))
} else {
try rolledWith(pinsKnockedDown: UInt.random(in: 0...10))
}
}

public mutating func generateFullGame() throws {
while !isGameover {
try rollNextBall()
}
}
}

Expand All @@ -62,4 +83,3 @@ extension ArraySlice where Element == Frame {
return map{ $0.calcualtedScore }
}
}

Loading

0 comments on commit 7ab07bc

Please sign in to comment.