Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .rspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
--require spec_helper
--format documentation
120 changes: 120 additions & 0 deletions design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# Bowling Challenge Multi-Class Planned Design Recipe

## 1. Describe the Problem

Count and sum the scores of a bowling game for one player.

### Bowling scoring

Scoring it bowling is pretty tricky. This is a representation of how the logic works.

```mermaid
flowchart TD
F1[Frame 1] --> B1.1{Ball 1}
B1.1 -->|Score < 10| B1.1S[Record B1 score]
B1.1 -->|Score == 10| NF1.1[<strong>Strike</strong>: FS pending and<br />move to next frame]
NF1.1 -->|Carry strike from F1| F2
B1.2 -->|Frame score == 10| NF1.2[<strong>Spare</strong>: BS2 pending and move<br />to next fame]
NF1.2 -->|Carry spare from F1| F2
B1.1S --> B1.2{Ball 2}
B1.2 -->|Frame score < 10| FS1.2[Record F1 score and<br />move to next frame]
FS1.2 --> F2

F2[Frame 2] --> B2.1{Ball 1}
B2.1 -->|Score < 10| B2.1S{B1 score}
B2.1 -->|Score == 10| NF2.1{<strong>Strike</strong>:<br />FS pending<br />and move to<br />next frame}
NF2.1 -->|If strike from F1,<br />carry strikes from F1 & F2| F3
NF2.1 -->|If spare from F1,<br />record F1 bonus| F3
NF2.1 -->|If no strike from F1,<br />carry strike from F2| F3
B2.2 -->|Frame score == 10| NF2.2[<strong>Spare</strong>: BS2 pending and move<br />to next fame]
NF2.2 -->|Carry spare from F2| F3
B2.1S -->|Record score.<br />Next ball.| B2.2{Ball 2}
B2.1S -->|Spare from F1| B2.1Ss[Add bonus to<br />F1 score]
B2.1Ss --> B2.2
B2.2 -->|Strike from F1| B2.2Ss[Add bonus to<br />F1 score]
B2.2Ss --> F3[Add bonus to<br />F1 score]
B2.2 -->|Frame score < 10| FS2.2[Record frame score and<br />move to next frame]
FS2.2 --> F3[Frame 3]
```

## 2. Design the Class System

```mermaid
classDiagram
Application <|-- Frame
Application <|-- ScoreCard
Application <|-- Gameplay
Application : +array frames
Application : +object gameplay
Application : +run()
Application : +create_frames()
class Frame{
+array ball_scores
+int bonus_score
+bool strike
+bool spare
+add_ball_score(ball, ball_score)
+get_ball_score(ball)
+update_bonus_score(score)
+bonus_score()
+two_balls?()
+strike(ball)
+strike?()
+spare()
+spare?()
+frame_score()
+total_frame_score()
}
class ScoreCard{
+update_pending_bonuses(frames, current_frame)
+show_scorecard(frames)
-update_pending_strikes(frames, current_frame)
-update_pending_spares(frames, current_frame)
-scorecard_segment(frame, frame_number)
-scorecard_segment_frame_10(frame)
}
class Gameplay{
+int current_frame
+int current_ball
+object scorecard
+score_prompt(frames)
+process_current_ball(frame, input)
+next_ball(frames)
+continue?(frames)
+final_score(frames)
-validate_input(input, frames)
}
```

<!-- ## 3. Create Examples as Integration Tests

_Create examples of the classes being used together in different situations and
combinations that reflect the ways in which the system will be used._

```ruby
# # Gets all tracks
# library = MusicLibrary.new
# track_1 = Track.new("Carte Blanche", "Veracocha")
# track_2 = Track.new("Synaesthesia", "The Thrillseekers")
# library.add(track_1)
# library.add(track_2)
# library.all # => [track_1, track_2]
```

## 4. Create Examples as Unit Tests

_Create examples, where appropriate, of the behaviour of each relevant class at
a more granular level of detail._

```ruby
# Creates the `@frames` array of `frame` objects
app = Application.new
app.return_frames.length # => 10
```

_Encode each example as a test. You can add to the above list as you go._

## 5. Implement the Behaviour

_After each test you write, follow the test-driving process of red, green,
refactor to implement the behaviour._ -->
34 changes: 34 additions & 0 deletions lib/app.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
require_relative 'frame'
require_relative 'gameplay'
require_relative 'scorecard'

class Application
def initialize
@frames = {}
@gameplay = Gameplay.new
create_frames
end

def run
loopy = true
while loopy do
success = @gameplay.score_prompt(@frames)
@gameplay.next_ball(@frames) if success == true
loopy = @gameplay.continue?(@frames)
end
@gameplay.final_score(@frames)
return
end

def create_frames
10.times do |i|
frame = Frame.new
@frames[i + 1] = frame
end
end
end

unless ENV['ENV'] == 'test'
app = Application.new
app.run
end
58 changes: 58 additions & 0 deletions lib/frame.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
class Frame
def initialize
@ball_scores = [0, 0, 0]
@bonus_score = 0
@strike = false
@spare = false
end

def add_ball_score(ball, ball_score)
@ball_scores[ball - 1] = ball_score
end

def get_ball_score(ball)
return @ball_scores[ball - 1]
end

def update_bonus_score(score)
@bonus_score = score
end

def bonus_score
return @bonus_score
end

# Returns true if this is a complete frame with no strike
def two_balls?
return true if (get_ball_score(1) != 0 && get_ball_score(2) != 0)
end

def strike(ball)
@strike = true
add_ball_score(ball, 10)
end

def strike?
return true if @strike == true
return false
end

def spare
@spare = true
score = 10 - get_ball_score(1)
add_ball_score(2, score)
end

def spare?
return true if @spare == true
return false
end

def frame_score
return @ball_scores.sum
end

def total_frame_score
return @ball_scores.sum + @bonus_score
end
end
77 changes: 77 additions & 0 deletions lib/gameplay.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
require_relative 'scorecard'

class Gameplay
attr_accessor :current_frame, :current_ball

def initialize
@current_frame = 1
@current_ball = 1
@scorecard = ScoreCard.new
end

def score_prompt(frames)
puts "Enter score for frame #{current_frame}, ball #{current_ball}:"
input = gets.chomp
input = input.upcase if input.is_a? String
return false if validate_input(input, frames) == false
input = input.to_i if input != "X" && input != "/"
frame = frames[@current_frame]
process_current_ball(frame, input)
@scorecard.update_pending_bonuses(frames, @current_frame)
@scorecard.show_scorecard(frames)
return true
end

def process_current_ball(frame, input)
if input == "X"
frame.strike(@current_ball)
elsif input == "/"
frame.spare
else
frame.add_ball_score(@current_ball, input)
# binding.irb
end
end

def next_ball(frames)
if @current_frame < 10
if @current_ball == 1 && frames[@current_frame].strike? != true
@current_ball = 2
else
@current_frame = @current_frame + 1
@current_ball = 1
end
else
@current_ball = @current_ball + 1
end
end

def continue?(frames)
frame = frames[@current_frame]
# binding.irb
if @current_frame == 10 && @current_ball == 4
return false
elsif @current_frame == 10 && frame.two_balls? == true && frame.spare? == false && frame.strike? == false
return false
else
return true
end
end

def final_score(frames)
score = 0
frames.values.each do |frame|
score = score + frame.total_frame_score
end
puts "\nYour final score is: #{score}"
end

private

def validate_input(input, frames)
return false if input == ""
return false if input == "/" && @current_ball == 1
return false if input == "X" && @current_ball == 2 && @current_frame != 10
return false if /[X\/1-9]/.match(input) == nil
end
end
Loading