diff --git a/.gitignore b/.gitignore index d1a1edf06f..7b604c0351 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ /**/.DS_Store /coverage +Gemfile.lock # Local cache of Rubocop remote config .rubocop-* + diff --git a/Gemfile b/Gemfile index 63415039a7..da8df08dbd 100644 --- a/Gemfile +++ b/Gemfile @@ -3,12 +3,15 @@ source 'https://rubygems.org' ruby '3.0.2' gem 'sinatra' +gem 'sinatra-reloader' +gem 'puma' group :test do gem 'capybara' gem 'rspec' gem 'simplecov', require: false gem 'simplecov-console', require: false + gem 'capybara-screenshot' end group :development, :test do diff --git a/Gemfile.lock b/Gemfile.lock index 5afab6d697..962d8656ee 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -13,12 +13,19 @@ GEM rack-test (>= 0.6.3) regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) + capybara-screenshot (1.0.26) + capybara (>= 1.0, < 4) + launchy diff-lcs (1.4.4) docile (1.4.0) + launchy (2.5.0) + addressable (~> 2.7) mini_mime (1.1.1) mini_portile2 (2.6.1) + multi_json (1.15.0) mustermann (1.1.1) ruby2_keywords (~> 0.0.1) + nio4r (2.5.8) nokogiri (1.12.3) mini_portile2 (~> 2.6.1) racc (~> 1.4) @@ -26,6 +33,8 @@ GEM parser (3.0.2.0) ast (~> 2.4.1) public_suffix (4.0.6) + puma (5.6.4) + nio4r (~> 2.0) racc (1.5.2) rack (2.2.3) rack-protection (2.1.0) @@ -76,6 +85,14 @@ GEM rack (~> 2.2) rack-protection (= 2.1.0) tilt (~> 2.0) + sinatra-contrib (2.1.0) + multi_json + mustermann (~> 1.0) + rack-protection (= 2.1.0) + sinatra (= 2.1.0) + tilt (~> 2.0) + sinatra-reloader (1.0) + sinatra-contrib terminal-table (3.0.1) unicode-display_width (>= 1.1.1, < 3) tilt (2.0.10) @@ -88,11 +105,14 @@ PLATFORMS DEPENDENCIES capybara + capybara-screenshot + puma rspec rubocop (= 1.20) simplecov simplecov-console sinatra + sinatra-reloader RUBY VERSION ruby 3.0.2p107 diff --git a/README.md b/README.md index 3bd26e2d7d..1ba2c01901 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,19 @@ -# RPS Challenge +# RPS Challenge - Tom Solution + +## Instructions to user + +This is a simple web app that allows the user to input a name and play rock paper scissors against the computer. The player chooses a move, the computer's move is randomly selected and the winner is calculated and the result displayed to the screen. + +The player is then given the option to play again. + +I have not had as much time as I would like to refactor the code and improve the aesthetics of the app but the functionality is there (I think). + +I was unsure how to get buttons with a picture, I was able to follow a student from a previous cohort's code through finding him through a blogpost, his github code is here: + +https://github.com/awye765/rps-challenge + +I did not follow his code anywhere else. + Instructions ------- diff --git a/app.rb b/app.rb new file mode 100644 index 0000000000..27b35b1621 --- /dev/null +++ b/app.rb @@ -0,0 +1,49 @@ +require 'sinatra/base' +require 'sinatra/reloader' +require './lib/player' +require './lib/computer' +require './lib/game' + +class Rps < Sinatra::Base + enable :sessions + + configure :development do + register Sinatra::Reloader + end + + get '/' do + erb :home + end + + post '/begin_single_player' do + p params + $player_1 = Player.new(params[:player_1]) + $computer = Computer.new("Dwayne") + redirect '/single_player_game' + end + + get '/single_player_game' do + @player_1_name = $player_1.name + @computer_name = $computer.name + erb :single_player + end + + post '/calculate_results' do + $game = Game.new + $player_1.choose(params[:choice]) + $computer.choose + $game.calculate_result($player_1.choice,$computer.choice) + redirect '/results' + end + + get '/results' do + @result = $game.result + @player_1_name = $player_1.name + @computer_name = $computer.name + @player_1_choice = $player_1.choice + @computer_choice = $computer.choice + erb :results + end + + run! if app_file == $0 +end diff --git a/config.ru b/config.ru new file mode 100644 index 0000000000..776003b246 --- /dev/null +++ b/config.ru @@ -0,0 +1,2 @@ +require './app' +run Rps diff --git a/lib/computer.rb b/lib/computer.rb new file mode 100644 index 0000000000..a28c9abcdc --- /dev/null +++ b/lib/computer.rb @@ -0,0 +1,16 @@ +class Computer + + attr_reader :name, :choice + + ALLOWED_MOVES = ["rock", "paper", "scissors"] + + def initialize(name) + @name = name + @choice = nil + end + + def choose + @choice = ALLOWED_MOVES.sample + end + +end diff --git a/lib/game.rb b/lib/game.rb new file mode 100644 index 0000000000..d4924889c5 --- /dev/null +++ b/lib/game.rb @@ -0,0 +1,38 @@ +class Game + + attr_reader :result + + ALLOWED_MOVES = ["rock", "paper", "scissors"] + + def initialize + @result = nil + @outcomes = { + "rock rock" => nil, + "rock scissors" => true, + "rock paper" => false, + "scissors rock" => false, + "scissors scissors" => nil, + "scissors paper" => true, + "paper rock" => true, + "paper scissors" => false, + "paper paper" => nil, + } + end + + def calculate_result(move1,move2) + check_move_allowed(move1) + check_move_allowed(move2) + @result = output_winner(move1,move2) + end + + private + + def check_move_allowed(move) + fail 'Incorrect move' unless ALLOWED_MOVES.include?(move) + end + + def output_winner(move1,move2) + @outcomes["#{move1} #{move2}"] + end + +end diff --git a/lib/player.rb b/lib/player.rb new file mode 100644 index 0000000000..f61e275048 --- /dev/null +++ b/lib/player.rb @@ -0,0 +1,14 @@ +class Player + + attr_reader :name, :choice + + def initialize(name) + @name = name + @choice = nil + end + + def choose(choice) + @choice = choice + end + +end diff --git a/public/Rock.png b/public/Rock.png new file mode 100644 index 0000000000..574ccfb136 Binary files /dev/null and b/public/Rock.png differ diff --git a/public/paper.png b/public/paper.png new file mode 100644 index 0000000000..2e913960c3 Binary files /dev/null and b/public/paper.png differ diff --git a/public/rock.jpeg b/public/rock.jpeg new file mode 100644 index 0000000000..0caf3dfc33 Binary files /dev/null and b/public/rock.jpeg differ diff --git a/public/scissors.png b/public/scissors.png new file mode 100644 index 0000000000..b763893e70 Binary files /dev/null and b/public/scissors.png differ diff --git a/spec/Feature_tests/home_feature_spec.rb b/spec/Feature_tests/home_feature_spec.rb new file mode 100644 index 0000000000..4eeaaf3d60 --- /dev/null +++ b/spec/Feature_tests/home_feature_spec.rb @@ -0,0 +1,18 @@ +feature "Going to the homepage, entering a player name" do + + scenario "the home page returns a successful status code" do + visit("/") + expect(page.status_code).to eq(200) + end + + scenario "page contains 'Please enter your name(s)'" do + visit("/") + expect(page).to have_content('Please enter your name(s)') + end + + scenario "Page contains an option for single player mode" do + visit("/") + expect(page).to have_selector(:link_or_button, 'Single Player Mode') + end + +end diff --git a/spec/Feature_tests/result_feature_spec.rb b/spec/Feature_tests/result_feature_spec.rb new file mode 100644 index 0000000000..5786af6a37 --- /dev/null +++ b/spec/Feature_tests/result_feature_spec.rb @@ -0,0 +1,36 @@ +feature "Game outcome screen" do + + scenario "When the player picks rock, they are taken to a results page" do + sign_in_and_play_1p + click_on 'Rock' + expect(page).to have_content('The results are in') + end + + scenario "The player picks rock, the computer picks scissors, the player wins" do + srand(3) + sign_in_and_play_1p + click_on 'Rock' + expect(page).to have_content('Mario wins') + end + + scenario "The player picks scissors, the computer picks scissors, it's a draw" do + srand(3) + sign_in_and_play_1p + click_on 'Scissors' + expect(page).to have_content('draw') + end + + scenario "The player picks paper, the computer picks scissors, the player loses" do + srand(3) + sign_in_and_play_1p + click_on 'Paper' + expect(page).to have_content('Mario loses') + end + + scenario "There is an option to play again at the end of the game" do + sign_in_and_play_1p + click_on 'Rock' + expect(page).to have_selector(:link_or_button,'Play again?') + end + +end diff --git a/spec/Feature_tests/single_player_feature_spec.rb b/spec/Feature_tests/single_player_feature_spec.rb new file mode 100644 index 0000000000..717a639fb1 --- /dev/null +++ b/spec/Feature_tests/single_player_feature_spec.rb @@ -0,0 +1,13 @@ +feature "Playing the game" do + + scenario "When player names have been entered, they appear in the game" do + sign_in_and_play_1p + expect(page).to have_content("Mario vs. Dwayne") + end + + scenario "Player can choose rock" do + sign_in_and_play_1p + expect(page).to have_selector(:link_or_button, 'Rock') + end + +end diff --git a/spec/computer_spec.rb b/spec/computer_spec.rb new file mode 100644 index 0000000000..2eaa5683d0 --- /dev/null +++ b/spec/computer_spec.rb @@ -0,0 +1,11 @@ +require 'computer' + +describe Computer do + subject(:ai) { Player.new('HAL 9000') } + + describe '#name' do + it 'returns the name' do + expect(ai.name).to eq 'HAL 9000' + end + end +end diff --git a/spec/game_spec.rb b/spec/game_spec.rb new file mode 100644 index 0000000000..83ee57cd92 --- /dev/null +++ b/spec/game_spec.rb @@ -0,0 +1,37 @@ +require 'game' + +describe Game do + subject(:game) { Game.new } + + describe '#calculate_result' do + it 'responds to two arguments' do + expect(game).to respond_to(:calculate_result).with(2) + end + + it 'fails if the first move is not rock, paper or scissors' do + expect { game.calculate_result('elephant','rock') }.to raise_error 'Incorrect move' + end + + it 'fails if the first move is not rock, paper or scissors' do + expect { game.calculate_result('rock','octopus') }.to raise_error 'Incorrect move' + end + + it 'outputs true for a winning game' do + expect(game.calculate_result('rock','scissors')).to eq true + expect(game.calculate_result('paper','rock')).to eq true + expect(game.calculate_result('scissors','paper')).to eq true + end + + it 'outputs nil for a drawn game' do + expect(game.calculate_result('rock','rock')).to eq nil + expect(game.calculate_result('scissors','scissors')).to eq nil + expect(game.calculate_result('paper','paper')).to eq nil + end + + it 'outputs false for a losing game' do + expect(game.calculate_result('rock','paper')).to eq false + expect(game.calculate_result('scissors','rock')).to eq false + expect(game.calculate_result('paper','scissors')).to eq false + end + end +end diff --git a/spec/player_spec.rb b/spec/player_spec.rb new file mode 100644 index 0000000000..a1d4d51be7 --- /dev/null +++ b/spec/player_spec.rb @@ -0,0 +1,11 @@ +require 'player' + +describe Player do + subject(:mario) { Player.new('Mario') } + + describe '#name' do + it 'returns the name' do + expect(mario.name).to eq 'Mario' + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 11a50a94e5..2e2af80951 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,6 +1,13 @@ require 'capybara/rspec' +require 'rspec' +require 'rack/test' +require 'capybara' +require 'sinatra' require 'simplecov' require 'simplecov-console' +# require 'capybara-screenshot/rspec' + +ENV['RACK_ENV'] = 'test' SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([ SimpleCov::Formatter::Console, @@ -9,6 +16,11 @@ ]) SimpleCov.start +require File.join(File.dirname(__FILE__), '..', 'app.rb') +require File.join(File.dirname(__FILE__), 'web_helpers.rb') + +Capybara.app = Rps + # For accurate test coverage measurements, require your code AFTER 'SimpleCov.start' RSpec.configure do |config| diff --git a/spec/web_helpers.rb b/spec/web_helpers.rb new file mode 100644 index 0000000000..8180afb643 --- /dev/null +++ b/spec/web_helpers.rb @@ -0,0 +1,5 @@ +def sign_in_and_play_1p + visit("/") + fill_in 'player_1', with: 'Mario' + click_on 'Single Player Mode' +end diff --git a/views/home.erb b/views/home.erb new file mode 100644 index 0000000000..aa2ad33f99 --- /dev/null +++ b/views/home.erb @@ -0,0 +1,9 @@ +
Please choose an option
+ + \ No newline at end of file