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
Binary file added .DS_Store
Binary file not shown.
7 changes: 7 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

source "https://rubygems.org"

# gem "rails"

gem "rspec", "~> 3.12"
26 changes: 26 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
GEM
remote: https://rubygems.org/
specs:
diff-lcs (1.5.0)
rspec (3.12.0)
rspec-core (~> 3.12.0)
rspec-expectations (~> 3.12.0)
rspec-mocks (~> 3.12.0)
rspec-core (3.12.1)
rspec-support (~> 3.12.0)
rspec-expectations (3.12.2)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.12.0)
rspec-mocks (3.12.4)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.12.0)
rspec-support (3.12.0)

PLATFORMS
x86_64-darwin-21

DEPENDENCIES
rspec (~> 3.12)

BUNDLED WITH
2.4.8
11 changes: 11 additions & 0 deletions app.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class Application
def ask_for_input
puts "Please enter a score: "
input = gets.chomp
if input =~ /^([1-9]|10)/
return input.to_i
else
raise ArgumentError, "Invalid input: #{input}"
end
end
end
111 changes: 111 additions & 0 deletions design_app_rb.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Bowling Multi-Class Planned Design Recipe

## 1. Describe the Problem

As a user, I want to be able to keep track of my score while playing a game of bowling.

When I start the program, I want to be prompted to enter the number of pins I knocked down on each throw of each frame, and I want the program to calculate my score and display it on a scorecard.

If I knock down all ten pins on my first throw of a frame, I want the program to automatically record a strike and move on to the next frame. If I knock down all ten pins on the second throw of a frame, I want the program to record a spare and move on to the next frame.

If I do not knock down all ten pins on a frame, I want the program to record an open frame and move on to the next frame.

When I reach the 10th frame, I want the program to allow me to make up to three throws if necessary, and to correctly calculate my score based on the number of pins knocked down in each throw.

At the end of the game, I want the program to display my total score and ask me if I want to start a new game or exit the program.

## 2. Design the Class System

_Consider diagramming out the classes and their relationships. Take care to
focus on the details you see as important, not everything. The diagram below
uses asciiflow.com but you could also use excalidraw.com, draw.io, or miro.com_

(diagram for classes in a separate file)

_Also design the interface of each class in more detail._

```ruby
class Application
def initialize
# new instance of Game class
# new instance of Scorecard class
end

def start
# puts "Start game"
# loop
end

# def all
# Returns a list of track objects
# end

# def search_by_title(keyword) # keyword is a string
# Returns a list of tracks with titles that include the keyword
# end
end

class Frame
def initialize(title, artist) # title and artist are both strings
end

# def format
# Returns a string of the form "TITLE by ARTIST"
# end
end

class Game
def initialize(title, artist) # title and artist are both strings
end

# def format
# Returns a string of the form "TITLE by ARTIST"
# end
end

class Scorecard
def initialize(title, artist) # title and artist are both strings
end

# def format
# Returns a string of the form "TITLE by ARTIST"
# end
end
```

## 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
# EXAMPLE

# 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
# EXAMPLE

# Constructs a track
track = Track.new("Carte Blanche", "Veracocha")
track.title # => "Carte Blanche"
```

_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._
68 changes: 68 additions & 0 deletions design_frame.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Frame Class Design Recipe

## 1. Describe the Problem

First throw of a frame:
As a user, if I knock down all ten pins on my first throw of a frame, I want the program to automatically record a strike and move on to the next frame.

Second throw of a frame:
As a user, if I knock down all ten pins on the first and the second throw of a frame, I want the program to record a spare and move on to the next frame.

Open frame:
As a user, if I do not knock down all ten pins on a frame, I want the program to record an open frame and move on to the next frame.

10th frame:
As a user, when I reach the 10th frame, I want the program to allow me to make up to three throws if necessary, and to correctly calculate my score based on the number of pins knocked down in each throw.

## 2. Design the Class Interface

_Include the initializer and public methods with all parameters and return values._

```ruby
# EXAMPLE

class Reminder
def initialize(name) # name is a string
# ...
end

def remind_me_to(task) # task is a string
# No return value
end

def remind()
# Throws an exception if no task is set
# Otherwise, returns a string reminding the user to do the task
end
end
```

## 3. Create Examples as Tests

_Make a list of examples of how the class will behave in different situations._

```ruby
# EXAMPLE

# 1
reminder = Reminder("Kay")
reminder.remind_me_to("Walk the dog")
reminder.remind() # => "Walk the dog, Kay!"

# 2
reminder = Reminder("Kay")
reminder.remind() # fails with "No task set."

# 3
reminder = Reminder("Kay")
reminder.remind_me_to("")
reminder.remind() # => ", Kay!"
```

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

## 4. Implement the Behaviour

_After each test you write, follow the test-driving process of red, green, refactor to implement the behaviour._


Empty file added design_game.md
Empty file.
Empty file added design_scorecard.md
Empty file.
Empty file added lib/frame.rb
Empty file.
Empty file added lib/game.rb
Empty file.
49 changes: 49 additions & 0 deletions lib/scorecard.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
class Scorecard
attr_reader :frame_number, :score_so_far
ROLLING_FIRST_BALL = 1
ROLLING_SECOND_BALL = 2

def initialize
@frame_number = 1
@score_so_far = 0
@frame_scores = []
@state = ROLLING_FIRST_BALL
@game_over = false

end

def game_over?
@game_over
end

def roll(ball)

if @state == @rolling_first_ball
@first_ball_in_frame = ball
@state = @rolling_second_ball
elsif @state == :rolling_second_ball
@score_so_far += @first_ball_in_frame + ball
@frame_number += 1
@frame_scores << @score_so_far
@state = :rolling_first_ball
else
puts "Invalid state: #{@state}"
exit(1)
end
[@score_so_far]
end




# My old code
# attr_reader :total_score

# def initialize
# @total_score = 0
# end
# def add_score(score)
# @total_score += score
# end

end
20 changes: 20 additions & 0 deletions spec/app_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
require_relative "../app"

RSpec.describe Application do
let(:app) { Application.new }
context "when asking for a user input" do
xit "should accept a valid input" do
allow(STDIN).to receive(:gets).and_return("10\n")
expect { app.ask_for_input }.not_to raise_error
end
xit "should reject invalid input" do
allow(STDIN).to receive(:gets).and_return("invalid\n")
expect { app.ask_for_input }.to raise_error(ArgumentError)
end
end
context "when given an input" do
xit "updates the scorecard" do

end
end
end
Empty file added spec/frame_spec.rb
Empty file.
Empty file added spec/game_spec.rb
Empty file.
55 changes: 55 additions & 0 deletions spec/scorecard_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
require_relative "../lib/scorecard"

RSpec.describe do
let(:scorecard) { Scorecard.new }

describe "#initialize" do
it "creates a new Scorecard object with correct initial values" do
scorecard = Scorecard.new

expect(scorecard.frame_number).to eq(1)
expect(scorecard.score_so_far).to eq(0)
expect(scorecard.game_over?).to be_falsey
expect(scorecard.instance_variable_get(:@state)).to eq(1)
# expect(scorecard.instance_variable_get(:@state)).to eq(Scorecard::ROLLING_FIRST_BALL) - equivalent
end
end

describe "#roll" do #unsure about this one
it "updates the scorecard after 1st roll" do
# in the description it says test a single roll that's not a strike'
result = scorecard.roll(1)

expect(result.length).to eq(1)
expect(scorecard.frame_number).to eq(1)
expect(scorecard.score_so_far).to eq(0)
expect(scorecard.game_over?).to be_falsey
# expect(scorecard.state).to eq(:rolling_second_ball)
expect(scorecard.instance_variable_get(:@state)).to eq(2)

end
it "updates the scorecard after frame 1 miss" do
result = scorecard.roll(1)

expect(result.length).to eq(1)
expect(result[0]).to eq(3)
expect(scorer.frame_number).to eq(2)
expect(scorer.score_so_far).to eq(3)
expect(scorer.game_is_over).to eq(false)
end
end



# my old code below
context "when given an input" do
xit "updates the scorecard" do
scorecard.add_score(2)
expect(scorecard.total_score).to eq(2)
scorecard.add_score(5)
expect(scorecard.total_score).to eq(7)
scorecard.add_score(8)
expect(scorecard.total_score).to eq(15)
end
end
end
Loading