Merge branch 'play-hand-validations_wip_stash'
This commit is contained in:
commit
5a1f19eaa0
1
.gitignore
vendored
1
.gitignore
vendored
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
# ctags
|
# ctags
|
||||||
tags
|
tags
|
||||||
|
gems.tags
|
||||||
|
|
||||||
# bundler state
|
# bundler state
|
||||||
/.bundle
|
/.bundle
|
||||||
|
|||||||
1
Gemfile
1
Gemfile
@ -40,6 +40,7 @@ group :development do
|
|||||||
gem 'rb-fsevent', require: false
|
gem 'rb-fsevent', require: false
|
||||||
gem 'rb-inotify', require: false
|
gem 'rb-inotify', require: false
|
||||||
gem 'spring-commands-rspec'
|
gem 'spring-commands-rspec'
|
||||||
|
gem 'guard-ctags-bundler'
|
||||||
end
|
end
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
gem 'factory_girl_rails'
|
gem 'factory_girl_rails'
|
||||||
|
|||||||
@ -114,6 +114,9 @@ GEM
|
|||||||
guard (~> 2.2)
|
guard (~> 2.2)
|
||||||
guard-compat (~> 1.1)
|
guard-compat (~> 1.1)
|
||||||
guard-compat (1.2.0)
|
guard-compat (1.2.0)
|
||||||
|
guard-ctags-bundler (1.4.0)
|
||||||
|
guard (>= 2.0)
|
||||||
|
guard-compat (>= 0.1.0)
|
||||||
guard-rails (0.7.0)
|
guard-rails (0.7.0)
|
||||||
guard (~> 2.0)
|
guard (~> 2.0)
|
||||||
guard-rspec (4.5.0)
|
guard-rspec (4.5.0)
|
||||||
@ -344,6 +347,7 @@ DEPENDENCIES
|
|||||||
faker
|
faker
|
||||||
foreman
|
foreman
|
||||||
guard-bundler
|
guard-bundler
|
||||||
|
guard-ctags-bundler
|
||||||
guard-rails
|
guard-rails
|
||||||
guard-rspec
|
guard-rspec
|
||||||
haml-lint
|
haml-lint
|
||||||
@ -376,3 +380,6 @@ DEPENDENCIES
|
|||||||
turbolinks
|
turbolinks
|
||||||
uglifier (>= 1.3.0)
|
uglifier (>= 1.3.0)
|
||||||
upmin-admin
|
upmin-admin
|
||||||
|
|
||||||
|
BUNDLED WITH
|
||||||
|
1.10.6
|
||||||
|
|||||||
@ -50,7 +50,7 @@ end
|
|||||||
# * zeus: 'zeus rspec' (requires the server to be started separately)
|
# * zeus: 'zeus rspec' (requires the server to be started separately)
|
||||||
# * 'just' rspec: 'rspec'
|
# * 'just' rspec: 'rspec'
|
||||||
|
|
||||||
guard :rspec, cmd: "bundle exec spring rspec" do
|
guard :rspec, cmd: "bundle exec spring rspec", all_after_pass: true, failed_mode: :focus do
|
||||||
require "guard/rspec/dsl"
|
require "guard/rspec/dsl"
|
||||||
dsl = Guard::RSpec::Dsl.new(self)
|
dsl = Guard::RSpec::Dsl.new(self)
|
||||||
|
|
||||||
@ -93,3 +93,8 @@ guard :rspec, cmd: "bundle exec spring rspec" do
|
|||||||
Dir[File.join("**/#{m[1]}.feature")][0] || "spec/acceptance"
|
Dir[File.join("**/#{m[1]}.feature")][0] || "spec/acceptance"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
guard 'ctags-bundler', src_path: ["app", "lib", "spec/support"], project_file: "tags" do
|
||||||
|
watch(/^(app|lib|spec\/support)\/.*\.rb$/)
|
||||||
|
watch('Gemfile.lock')
|
||||||
|
end
|
||||||
|
|||||||
@ -23,25 +23,45 @@ class GamesController < ApplicationController
|
|||||||
def play_hand
|
def play_hand
|
||||||
# TODO: validate player's turn, current_user == @game.current_player.user
|
# TODO: validate player's turn, current_user == @game.current_player.user
|
||||||
# TODO: move this logic out of controller maybe to model(s)
|
# TODO: move this logic out of controller maybe to model(s)
|
||||||
@cards_to_play = @game.current_player.player_cards.find(params[:player_card_ids])
|
|
||||||
|
# If cards are played
|
||||||
|
if params[:player_card_ids]
|
||||||
|
@cards_to_play = @game.current_player.player_cards.order(:value).find(params[:player_card_ids])
|
||||||
|
else
|
||||||
|
# No cards are played
|
||||||
|
@cards_to_play = []
|
||||||
|
end
|
||||||
|
|
||||||
# Instantiate the play
|
# Instantiate the play
|
||||||
@play = @game.current_player.plays.new
|
@play = @game.current_player.plays.new
|
||||||
@play.game_id = @game.id
|
@play.game_id = @game.id
|
||||||
|
@play.player_cards << @cards_to_play
|
||||||
@play.save
|
@play.save
|
||||||
|
|
||||||
@cards_to_play.each do |card|
|
#@cards_to_play.each do |card|
|
||||||
card.play_id = @play.id
|
#@play.player_cards << card
|
||||||
card.save
|
#end
|
||||||
|
|
||||||
|
if @play.save
|
||||||
|
# TODO: Ensure active player only changes when valid hand is played
|
||||||
|
@game.set_active_player 'next'
|
||||||
|
@game.save
|
||||||
|
else
|
||||||
|
# If play is invalid, remove the cards from the play
|
||||||
|
#@play.player_cards.map {|card| card.play_id = nil; card.save}
|
||||||
|
# Delete play
|
||||||
|
#@play.destroy
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO: Ensure active player only changes when valid hand is played
|
#@play.save
|
||||||
@game.set_active_player 'next'
|
@play.errors.messages.each do |key, msg|
|
||||||
@game.save
|
flash[key] = msg.join
|
||||||
|
end
|
||||||
|
redirect_to(@game)
|
||||||
|
|
||||||
# TODO: add current_hand logic
|
# TODO: add current_hand logic
|
||||||
# @hand > @game.hand_to_beat
|
# @hand > @game.hand_to_beat
|
||||||
redirect_to @game
|
#redirect_to @game
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
|
|||||||
@ -4,6 +4,7 @@ class Game < ActiveRecord::Base
|
|||||||
has_many :users, through: :players
|
has_many :users, through: :players
|
||||||
has_many :player_cards, through: :players
|
has_many :player_cards, through: :players
|
||||||
has_many :plays, through: :players
|
has_many :plays, through: :players
|
||||||
|
belongs_to :play_to_beat
|
||||||
accepts_nested_attributes_for :players
|
accepts_nested_attributes_for :players
|
||||||
validates :title, presence: true
|
validates :title, presence: true
|
||||||
|
|
||||||
@ -106,4 +107,12 @@ class Game < ActiveRecord::Base
|
|||||||
def control_user
|
def control_user
|
||||||
self.players.find(self.control_player_id).user
|
self.players.find(self.control_player_id).user
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def hand_to_beat
|
||||||
|
self.play_to_beat.play.hand
|
||||||
|
end
|
||||||
|
|
||||||
|
def player_to_beat
|
||||||
|
self.play_to_beat.play.player unless self.play_to_beat.play.nil?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -2,4 +2,183 @@ class Play < ActiveRecord::Base
|
|||||||
belongs_to :game, foreign_key: 'game_id'
|
belongs_to :game, foreign_key: 'game_id'
|
||||||
belongs_to :player, foreign_key: 'player_id'
|
belongs_to :player, foreign_key: 'player_id'
|
||||||
has_many :player_cards, foreign_key: 'play_id'
|
has_many :player_cards, foreign_key: 'play_id'
|
||||||
|
alias_attribute :hand, :player_cards
|
||||||
|
|
||||||
|
validates :game, presence: true
|
||||||
|
validates :player, presence: true
|
||||||
|
validate :hand_valid?
|
||||||
|
|
||||||
|
after_save :update_play_to_beat
|
||||||
|
|
||||||
|
def update_play_to_beat
|
||||||
|
if hand_valid?
|
||||||
|
@pb = PlayToBeat.new
|
||||||
|
@pb.game = self.game
|
||||||
|
@pb.play = self
|
||||||
|
@pb.save
|
||||||
|
self.game.play_to_beat = @pb
|
||||||
|
self.game.save!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Validation methods
|
||||||
|
|
||||||
|
def hand_valid?
|
||||||
|
@truths = []
|
||||||
|
|
||||||
|
# Is there a hand_to_beat ?
|
||||||
|
if self.game.play_to_beat
|
||||||
|
# current player is not the player to beat
|
||||||
|
if (self.game.player_to_beat != self.player)
|
||||||
|
@truths << (self.hand_type == self.game.play_to_beat.play.hand_type)
|
||||||
|
# Compare this hand to the hand_to_beat,
|
||||||
|
@truths << (self.beats? self.game.play_to_beat.play)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if has_player_cards
|
||||||
|
|
||||||
|
@truths << !hand_type.empty?
|
||||||
|
|
||||||
|
if first_play?
|
||||||
|
@truths << contains_lowest_card?
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
if @truths.include? false
|
||||||
|
errors.add(:player_cards, "invalid hand")
|
||||||
|
end
|
||||||
|
return (!@truths.include? false)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Check if the value of this hand is higher than the hand_to_beat
|
||||||
|
def beats?(play_to_beat)
|
||||||
|
@truth = self.high_card_value > play_to_beat.high_card_value
|
||||||
|
return @truth
|
||||||
|
end
|
||||||
|
|
||||||
|
def high_card
|
||||||
|
self.hand.last unless self.hand.empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
def high_card_value
|
||||||
|
return self.hand.last.value unless self.hand.empty?
|
||||||
|
0
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO: Recognize face cards
|
||||||
|
def hand_type
|
||||||
|
case self.player_cards.length
|
||||||
|
when 1
|
||||||
|
@hand_type = 'single'
|
||||||
|
when 2
|
||||||
|
@hand_type = 'double'
|
||||||
|
if (self.player_cards[0].rank != self.player_cards[1].rank)
|
||||||
|
errors.add(:player_cards, "Invalid pair. Rank must match.")
|
||||||
|
end
|
||||||
|
when 3
|
||||||
|
@hand_type = 'triple'
|
||||||
|
@ranks = Array.new << self.player_cards.map {|pc| pc.rank}
|
||||||
|
@ranks.flatten!
|
||||||
|
@suits = Array.new << self.player_cards.map {|pc| pc.suit}
|
||||||
|
@suits.flatten!
|
||||||
|
# TODO: Does not work for face cards :(
|
||||||
|
if (@ranks[0].to_i + 1 == @ranks[1].to_i) && (@ranks[1].to_i + 1 == @ranks[2].to_i)
|
||||||
|
@hand_type += ' run'
|
||||||
|
# Check if suited
|
||||||
|
if (@suits.uniq.length == 1)
|
||||||
|
@hand_type += ' suited'
|
||||||
|
end
|
||||||
|
elsif @ranks.uniq.length != 1
|
||||||
|
errors.add(:player_cards, "Invalid triple. Rank must match.")
|
||||||
|
end
|
||||||
|
when 4
|
||||||
|
@ranks = Array.new << self.player_cards.map {|pc| pc.rank}
|
||||||
|
@ranks.flatten!
|
||||||
|
@suits = Array.new << self.player_cards.map {|pc| pc.suit}
|
||||||
|
@suits.flatten!
|
||||||
|
|
||||||
|
if (@ranks.uniq.length == 1)
|
||||||
|
@hand_type = 'bomb'
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO: Does not work for face cards :(
|
||||||
|
if (@ranks[0].to_i + 1 == @ranks[1].to_i) && (@ranks[1].to_i + 1 == @ranks[2].to_i) & (@ranks[2].to_i + 1 == @ranks[3].to_i)
|
||||||
|
@hand_type = 'run of 4'
|
||||||
|
end
|
||||||
|
|
||||||
|
if (@suits.uniq.length == 1)
|
||||||
|
@hand_type += ' suited'
|
||||||
|
end
|
||||||
|
when 5..13
|
||||||
|
@hand_type = "run of #{self.player_cards.length}"
|
||||||
|
|
||||||
|
@ranks = Array.new << self.player_cards.map {|pc| pc.rank}
|
||||||
|
@ranks.flatten!
|
||||||
|
@suits = Array.new << self.player_cards.map {|pc| pc.suit}
|
||||||
|
@suits.flatten!
|
||||||
|
|
||||||
|
# double? && length?
|
||||||
|
if (@ranks.uniq.length == (@ranks.length/2)) && ([6,8,10].include? @ranks.length)
|
||||||
|
@hand_type = "double " + @hand_type
|
||||||
|
# run?
|
||||||
|
# iterate over each unique rank; except for the last rank
|
||||||
|
@ranks.uniq.sort[0...-1].each.with_index do |rank, i|
|
||||||
|
# each rank one away from each other?
|
||||||
|
if (rank.to_i + 1) == @ranks.uniq.sort[i+1].to_i
|
||||||
|
else
|
||||||
|
@hand_type = "Invalid " + @hand_type
|
||||||
|
errors.add(:player_cards, "Invalid double run of #{@ranks.length}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
# normal run?
|
||||||
|
# For each card except for the last card
|
||||||
|
@ranks.sort[0...-1].each_with_index do |r, i|
|
||||||
|
# If cards are not a single run
|
||||||
|
if (r.to_i + 1) != @ranks[i+1].to_i
|
||||||
|
# And if cards are not a double run
|
||||||
|
errors.add(:player_cards, "Invalid run of #{@ranks.length}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Check if suited
|
||||||
|
if (@suits.uniq.length == 1)
|
||||||
|
@hand_type += ' suited'
|
||||||
|
end
|
||||||
|
|
||||||
|
else
|
||||||
|
errors.add(:player_cards, "Unrecognized hand type.")
|
||||||
|
end
|
||||||
|
return @hand_type
|
||||||
|
end
|
||||||
|
|
||||||
|
def first_play?
|
||||||
|
# First play of the game
|
||||||
|
if self.game.plays.empty? || ( self.game.plays.length == 1 && self.game.plays.first == self )
|
||||||
|
true
|
||||||
|
else
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def contains_lowest_card?
|
||||||
|
# Cards being played must include the lowest card
|
||||||
|
if (self.player_cards.include? self.game.lowest_card) == false
|
||||||
|
errors.add(:player_cards, "First hand must contain the lowest card: #{game.lowest_card.to_s}")
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def has_player_cards
|
||||||
|
true unless self.player_cards.empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
player_cards.join ' '
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
4
app/models/play_to_beat.rb
Normal file
4
app/models/play_to_beat.rb
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
class PlayToBeat < ActiveRecord::Base
|
||||||
|
belongs_to :game
|
||||||
|
belongs_to :play
|
||||||
|
end
|
||||||
@ -29,7 +29,8 @@
|
|||||||
= "Me" if player.user == current_user
|
= "Me" if player.user == current_user
|
||||||
|
|
||||||
// Show whose turn it is
|
// Show whose turn it is
|
||||||
%strong= "My Turn " if @game.control_player == player
|
- if @game.active_player_id
|
||||||
|
%strong= "My Turn " if @game.active_player == player
|
||||||
|
|
||||||
// Show the players' cards
|
// Show the players' cards
|
||||||
- unless player.inventory.empty?
|
- unless player.inventory.empty?
|
||||||
@ -45,15 +46,22 @@
|
|||||||
-# TODO better logic, not in view
|
-# TODO better logic, not in view
|
||||||
- unless @game.status == nil
|
- unless @game.status == nil
|
||||||
.table
|
.table
|
||||||
- if @game.current_hand
|
- if @game.play_to_beat
|
||||||
%h1 Hand to beat
|
- if @game.play_to_beat.play
|
||||||
|
%h1
|
||||||
|
Hand to beat
|
||||||
|
- @game.play_to_beat.play.player_cards.each do |card|
|
||||||
|
= card.to_s
|
||||||
|
= @game.play_to_beat.play.player.user.email
|
||||||
- else
|
- else
|
||||||
%h1 Open table
|
%h1 Open table
|
||||||
|
|
||||||
%h2
|
%h2
|
||||||
Played
|
Last Played
|
||||||
- if @game.plays.count > 0
|
- if @game.plays.count > 0
|
||||||
- @game.plays.last.player_cards.each do |card|
|
- @game.plays.last.player_cards.each do |card|
|
||||||
= card.to_s
|
= card.to_s
|
||||||
|
= @game.plays.last.player.user.email
|
||||||
|
|
||||||
.controls
|
.controls
|
||||||
- if @game.control_user == current_user
|
- if @game.control_user == current_user
|
||||||
|
|||||||
16
bin/guard
Executable file
16
bin/guard
Executable file
@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/env ruby
|
||||||
|
#
|
||||||
|
# This file was generated by Bundler.
|
||||||
|
#
|
||||||
|
# The application 'guard' is installed as part of a gem, and
|
||||||
|
# this file is here to facilitate running it.
|
||||||
|
#
|
||||||
|
|
||||||
|
require 'pathname'
|
||||||
|
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
||||||
|
Pathname.new(__FILE__).realpath)
|
||||||
|
|
||||||
|
require 'rubygems'
|
||||||
|
require 'bundler/setup'
|
||||||
|
|
||||||
|
load Gem.bin_path('guard', 'guard')
|
||||||
5
bin/rake
5
bin/rake
@ -3,6 +3,5 @@ begin
|
|||||||
load File.expand_path("../spring", __FILE__)
|
load File.expand_path("../spring", __FILE__)
|
||||||
rescue LoadError
|
rescue LoadError
|
||||||
end
|
end
|
||||||
require_relative '../config/boot'
|
require 'bundler/setup'
|
||||||
require 'rake'
|
load Gem.bin_path('rake', 'rake')
|
||||||
Rake.application.run
|
|
||||||
|
|||||||
5
db/migrate/20150810114449_add_play_to_beat_to_game.rb
Normal file
5
db/migrate/20150810114449_add_play_to_beat_to_game.rb
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
class AddPlayToBeatToGame < ActiveRecord::Migration
|
||||||
|
def change
|
||||||
|
add_reference :games, :play_to_beat, index: true
|
||||||
|
end
|
||||||
|
end
|
||||||
10
db/migrate/20150810115450_play_to_beat.rb
Normal file
10
db/migrate/20150810115450_play_to_beat.rb
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
class PlayToBeat < ActiveRecord::Migration
|
||||||
|
def change
|
||||||
|
create_table :play_to_beats do |t|
|
||||||
|
t.integer :game_id
|
||||||
|
t.integer :play_id
|
||||||
|
|
||||||
|
t.timestamps null: false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
12
db/schema.rb
12
db/schema.rb
@ -11,7 +11,7 @@
|
|||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 20150503133556) do
|
ActiveRecord::Schema.define(version: 20150810115450) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
@ -22,6 +22,16 @@ ActiveRecord::Schema.define(version: 20150503133556) do
|
|||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
t.string "status"
|
t.string "status"
|
||||||
t.integer "control_player_id"
|
t.integer "control_player_id"
|
||||||
|
t.integer "play_to_beat_id"
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index "games", ["play_to_beat_id"], name: "index_games_on_play_to_beat_id", using: :btree
|
||||||
|
|
||||||
|
create_table "play_to_beats", force: true do |t|
|
||||||
|
t.integer "game_id"
|
||||||
|
t.integer "play_id"
|
||||||
|
t.datetime "created_at", null: false
|
||||||
|
t.datetime "updated_at", null: false
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "player_cards", force: true do |t|
|
create_table "player_cards", force: true do |t|
|
||||||
|
|||||||
6
spec/factories/play_to_beats.rb
Normal file
6
spec/factories/play_to_beats.rb
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
FactoryGirl.define do
|
||||||
|
factory :play_to_beat do
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@ -37,8 +37,8 @@ feature 'Play a hand', type: :feature do
|
|||||||
@card_to_play = game.current_player.player_cards.order(:value).last
|
@card_to_play = game.current_player.player_cards.order(:value).last
|
||||||
check @card_to_play.to_s
|
check @card_to_play.to_s
|
||||||
click_button 'Play Hand'
|
click_button 'Play Hand'
|
||||||
expect(page).to have_content
|
expect(page).to have_content "First hand must contain the lowest card: #{game.lowest_card.to_s}"
|
||||||
"First hand must contain the lowest card: #{game.lowest_card.to_s}"
|
expect(game.play_to_beat).to eq(nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Scenario: Played cards leave the players inventory
|
# Scenario: Played cards leave the players inventory
|
||||||
@ -64,6 +64,31 @@ feature 'Play a hand', type: :feature do
|
|||||||
expect(@player.inventory.order(:value).join(' ')).to_not have_content(@card_to_play.to_s)
|
expect(@player.inventory.order(:value).join(' ')).to_not have_content(@card_to_play.to_s)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Scenario: Invalid cards do not leave the players inventory
|
||||||
|
# Given I am the current player
|
||||||
|
# When I play an invalid hand
|
||||||
|
# Then the cards played should not leave my inventory
|
||||||
|
scenario 'invalid hand played cards are not removed from the players inventory' do
|
||||||
|
game = setup_game
|
||||||
|
# Store initial state of players inventory
|
||||||
|
@player = game.current_player
|
||||||
|
@player_inventory = @player.player_cards.order(:value).join(' ')
|
||||||
|
@player_inventory = @player.inventory.order(:value).join(' ')
|
||||||
|
login_as(game.current_player.user, scope: :user)
|
||||||
|
visit game_path game
|
||||||
|
# Play invalid hand
|
||||||
|
@card_to_play = game.current_player.player_cards.order(:value).last
|
||||||
|
check @card_to_play.to_s
|
||||||
|
click_button 'Play Hand'
|
||||||
|
# Played hand is rendered
|
||||||
|
expect(page).to have_content("First hand must contain the lowest card: #{game.lowest_card.to_s}")
|
||||||
|
# Player inventory is rendered with played cards
|
||||||
|
expect(page).to have_content(@player_inventory)
|
||||||
|
# Player inventory method does return played cards
|
||||||
|
expect(@player.inventory.order(:value).join(' ')).to have_content(@card_to_play.to_s)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
# Scenario: Current player status updates after successful play
|
# Scenario: Current player status updates after successful play
|
||||||
# Given I am the current player
|
# Given I am the current player
|
||||||
# When I play a hand
|
# When I play a hand
|
||||||
|
|||||||
@ -10,6 +10,7 @@ RSpec.describe Game, type: :model do
|
|||||||
subject { @game }
|
subject { @game }
|
||||||
|
|
||||||
it { should respond_to(:title) }
|
it { should respond_to(:title) }
|
||||||
|
it { should respond_to(:play_to_beat) }
|
||||||
|
|
||||||
it '#title returns a string' do
|
it '#title returns a string' do
|
||||||
expect(@game.title).to match 'Test Game'
|
expect(@game.title).to match 'Test Game'
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
RSpec.describe Play, type: :model do
|
RSpec.describe Play, type: :model do
|
||||||
before(:each) do
|
before(:each) do
|
||||||
@game = FactoryGirl.create(:game)
|
@game = FactoryGirl.create(:game)
|
||||||
@ -17,23 +15,315 @@ RSpec.describe Play, type: :model do
|
|||||||
subject { @play }
|
subject { @play }
|
||||||
|
|
||||||
it { should respond_to(:game_id) }
|
it { should respond_to(:game_id) }
|
||||||
|
it { should respond_to(:game) }
|
||||||
|
it { should respond_to(:player_id) }
|
||||||
|
it { should respond_to(:player) }
|
||||||
|
it { should respond_to(:player_cards) }
|
||||||
|
it { should respond_to(:hand) }
|
||||||
|
it { should respond_to(:hand_type) }
|
||||||
|
it { should respond_to(:to_s) }
|
||||||
|
|
||||||
it '#game_id returns an integer' do
|
it '#game_id returns a game id' do
|
||||||
expect(@play.game_id).to match @game.id
|
expect(@play.game).to eq Game.find(@play.game_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
it '#player_id return a player id' do
|
||||||
|
expect(@play.player).to eq Player.find(@play.player_id)
|
||||||
|
|
||||||
|
end
|
||||||
|
it '#player_cards returns a PlayerCard collection' do
|
||||||
|
expect(@play.player_cards.class).to eq(PlayerCard::ActiveRecord_Associations_CollectionProxy)
|
||||||
|
end
|
||||||
|
|
||||||
|
it '#hand returns a PlayerCard collection' do
|
||||||
|
expect(@play.hand.class).to eq(PlayerCard::ActiveRecord_Associations_CollectionProxy)
|
||||||
|
end
|
||||||
|
|
||||||
|
it '#hand_type returns a string' do
|
||||||
|
@play.player_cards << create_hand(1, 'single')
|
||||||
|
expect(@play.hand_type).to eq 'single'
|
||||||
|
end
|
||||||
|
|
||||||
|
it '#to_s returns a string of the hand' do
|
||||||
|
@play.player_cards << create_hand(1, 'single')
|
||||||
|
expect(@play.to_s).to eq '3 Spade'
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'validations' do
|
describe 'validations' do
|
||||||
describe 'first_play' do
|
describe 'first_play' do
|
||||||
context 'when there have been no plays yet' do
|
it 'valid if it contains the lowest card' do
|
||||||
it 'is valid if it contains the lowest card' do
|
# expect(@game.plays.count).to eq(1)
|
||||||
expect(@game.plays.count).to eq(1)
|
# Active player is the lowest card holder
|
||||||
expect(@game.current_player).to eq(@game.lowest_card.player)
|
expect(@game.current_player).to eq(@game.lowest_card.player)
|
||||||
@card_to_play = @game.current_player.player_cards.order(:value).first
|
# Play the lowest card
|
||||||
@card_to_play.play_id = @play.id
|
@cards_to_play = @game.current_player.player_cards.order(:value).first
|
||||||
@card_to_play.save
|
@play.player_cards << @cards_to_play
|
||||||
expect(@play.player_cards.include? @game.lowest_card).to eq true
|
@play.save
|
||||||
|
expect(@play.player_cards.include? @game.lowest_card).to eq true
|
||||||
|
expect(subject.valid?).to eq(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
describe 'wins?' do
|
||||||
|
context 'it does not beat the hand_to_beat' do
|
||||||
|
it 'single' do
|
||||||
|
@play_to_beat = Play.new
|
||||||
|
@play_to_beat.player = @game.players.where.not(id: @game.active_player.id).last
|
||||||
|
@play_to_beat.game = @game
|
||||||
|
@play_to_beat.player_cards << @play_to_beat.player.player_cards.order(:value).last
|
||||||
|
@play_to_beat.save
|
||||||
|
@play.game.reload
|
||||||
|
|
||||||
|
expect(@play.game.play_to_beat.play).to eq(@play_to_beat)
|
||||||
|
# Losing card
|
||||||
|
@cards_to_play = @play.player.player_cards.order(:value).first
|
||||||
|
@play.player_cards << @cards_to_play
|
||||||
|
@play.save
|
||||||
|
expect(@game.player_to_beat).to_not eq(@play.player)
|
||||||
|
expect(@play.hand_valid?).to eq(false)
|
||||||
|
expect(@play.errors[:player_cards].include?('invalid hand')).to eq(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'double' do
|
||||||
|
@deck = Deck.new
|
||||||
|
@cards_to_beat = [
|
||||||
|
@spade5 = PlayerCard.new(@deck.cards[8].instance_values),
|
||||||
|
@club5 = PlayerCard.new(@deck.cards[9].instance_values)
|
||||||
|
]
|
||||||
|
|
||||||
|
@play_to_beat = Play.new
|
||||||
|
@play_to_beat.player = @game.players.first
|
||||||
|
@play_to_beat.player_cards << @cards_to_beat
|
||||||
|
@play_to_beat.game = @game
|
||||||
|
@play_to_beat.save
|
||||||
|
|
||||||
|
@play.player_cards = [
|
||||||
|
@spade4 = PlayerCard.new(@deck.cards[4].instance_values),
|
||||||
|
@club4 = PlayerCard.new(@deck.cards[5].instance_values)
|
||||||
|
]
|
||||||
|
|
||||||
|
# Shared tests
|
||||||
|
[@play_to_beat,@play].each do |play|
|
||||||
|
expect(play.errors[:player_cards]).to eq([])
|
||||||
|
expect(play.valid?).to eq(true)
|
||||||
|
expect(play.player_cards.count).to eq(2)
|
||||||
|
expect(play.hand_type).to eq('double')
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(@play.beats? @play_to_beat).to eq(false)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
context 'it beats the hand_to_beat' do
|
||||||
|
it 'single' do
|
||||||
|
@play_to_beat = Play.new
|
||||||
|
@play_to_beat.player = @game.players.last
|
||||||
|
@play_to_beat.game = @game
|
||||||
|
@play_to_beat.player_cards << @play_to_beat.player.player_cards.order(:value).first
|
||||||
|
@play_to_beat.save
|
||||||
|
|
||||||
|
@cards_to_play = @play.player.player_cards.order(:value).last
|
||||||
|
@play.player_cards << @cards_to_play
|
||||||
|
@play.save
|
||||||
|
expect(@play.valid?).to eq(true)
|
||||||
|
expect(@play.errors[:player_cards]).to eq([])
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'double' do
|
||||||
|
@deck = Deck.new
|
||||||
|
@cards_to_beat = [
|
||||||
|
@spade3 = PlayerCard.new(@deck.cards[0].instance_values),
|
||||||
|
@club3 = PlayerCard.new(@deck.cards[1].instance_values)
|
||||||
|
]
|
||||||
|
|
||||||
|
@play_to_beat = Play.new
|
||||||
|
@play_to_beat.player = @game.players.first
|
||||||
|
@play_to_beat.player_cards << @cards_to_beat
|
||||||
|
@play_to_beat.game = @game
|
||||||
|
@play_to_beat.save
|
||||||
|
|
||||||
|
@play.player_cards = [
|
||||||
|
@spade4 = PlayerCard.new(@deck.cards[4].instance_values),
|
||||||
|
@club4 = PlayerCard.new(@deck.cards[5].instance_values)
|
||||||
|
]
|
||||||
|
|
||||||
|
# Shared tests
|
||||||
|
[@play_to_beat,@play].each do |play|
|
||||||
|
expect(play.errors[:player_cards]).to eq([])
|
||||||
|
expect(play.valid?).to eq(true)
|
||||||
|
expect(play.player_cards.count).to eq(2)
|
||||||
|
expect(play.hand_type).to eq('double')
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(@play.beats? @play_to_beat).to eq(true)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'becomes the new play_to_beat' do
|
||||||
|
expect(@game.play_to_beat).to eq(nil)
|
||||||
|
# Play the hand
|
||||||
|
@cards_to_play = @game.current_player.inventory[0]
|
||||||
|
@play.hand << @cards_to_play
|
||||||
|
@play.save
|
||||||
|
expect(@play.hand_valid?).to eq(true)
|
||||||
|
@game.reload
|
||||||
|
expect(@game.play_to_beat.play).to eq(@play)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'hand_type' do
|
||||||
|
context 'when a single card is played' do
|
||||||
|
it 'single' do
|
||||||
|
@cards_to_play = @game.current_player.player_cards.order(:value).first
|
||||||
|
@play.player_cards << @cards_to_play
|
||||||
|
expect(@play.hand_type).to eq('single')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when two cards are played' do
|
||||||
|
it 'valid pair' do
|
||||||
|
@spade3 = PlayerCard.new(rank: "3", suit: "Spade")
|
||||||
|
@club3 = PlayerCard.new(rank: "3", suit: "Club")
|
||||||
|
@cards_to_play = [@spade3, @club3]
|
||||||
|
@play.player_cards << @cards_to_play
|
||||||
|
expect(@play.hand_type).to eq('double')
|
||||||
|
expect(@play.errors[:player_cards]).to eq([])
|
||||||
|
end
|
||||||
|
it 'invalid pair' do
|
||||||
|
@spade3 = PlayerCard.new(rank: "3", suit: "Spade")
|
||||||
|
@club4 = PlayerCard.new(rank: "4", suit: "Club")
|
||||||
|
@cards_to_play = [@spade3, @club4]
|
||||||
|
@play.player_cards << @cards_to_play
|
||||||
|
expect(@play.hand_type).to eq('double')
|
||||||
|
expect(@play.errors[:player_cards]).to eq(['Invalid pair. Rank must match.'])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when three cards are played' do
|
||||||
|
it 'valid triple' do
|
||||||
|
@spade3 = PlayerCard.new(rank: "3", suit: "Spade")
|
||||||
|
@club3 = PlayerCard.new(rank: "3", suit: "Club")
|
||||||
|
@diamond3 = PlayerCard.new(rank: "3", suit: "Diamond")
|
||||||
|
@cards_to_play = [@spade3, @club3, @diamond3]
|
||||||
|
@play.player_cards << @cards_to_play
|
||||||
|
expect(@play.hand_type).to eq('triple')
|
||||||
|
end
|
||||||
|
it 'invalid triple' do
|
||||||
|
@spade3 = PlayerCard.new(rank: "3", suit: "Spade")
|
||||||
|
@club3 = PlayerCard.new(rank: "3", suit: "Club")
|
||||||
|
@diamond4 = PlayerCard.new(rank: "4", suit: "Diamond")
|
||||||
|
@cards_to_play = [@spade3, @club3, @diamond4]
|
||||||
|
@play.player_cards << @cards_to_play
|
||||||
|
expect(@play.hand_type).to eq('triple')
|
||||||
|
expect(@play.errors[:player_cards]).to eq(['Invalid triple. Rank must match.'])
|
||||||
|
end
|
||||||
|
it 'valid run of 3' do
|
||||||
|
@spade3 = PlayerCard.new(rank: "3", suit: "Spade")
|
||||||
|
@club4 = PlayerCard.new(rank: "4", suit: "Club")
|
||||||
|
@diamond5 = PlayerCard.new(rank: "5", suit: "Diamond")
|
||||||
|
@cards_to_play = [@spade3, @club4, @diamond5]
|
||||||
|
@play.player_cards << @cards_to_play
|
||||||
|
expect(@play.hand_type).to eq('triple run')
|
||||||
|
end
|
||||||
|
it 'valid run of 3 suited' do
|
||||||
|
@spade3 = PlayerCard.new(rank: "3", suit: "Spade")
|
||||||
|
@club4 = PlayerCard.new(rank: "4", suit: "Spade")
|
||||||
|
@diamond5 = PlayerCard.new(rank: "5", suit: "Spade")
|
||||||
|
@cards_to_play = [@spade3, @club4, @diamond5]
|
||||||
|
@play.player_cards << @cards_to_play
|
||||||
|
expect(@play.hand_type).to eq('triple run suited')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when four cards are played' do
|
||||||
|
it 'valid bomb' do
|
||||||
|
@spade3 = PlayerCard.new(rank: "3", suit: "Spade")
|
||||||
|
@club3 = PlayerCard.new(rank: "3", suit: "Club")
|
||||||
|
@diamond3 = PlayerCard.new(rank: "3", suit: "Diamond")
|
||||||
|
@heart3 = PlayerCard.new(rank: "3", suit: "Heart")
|
||||||
|
@cards_to_play = [@spade3, @club3, @diamond3, @heart3]
|
||||||
|
@play.player_cards << @cards_to_play
|
||||||
|
expect(@play.hand_type).to eq('bomb')
|
||||||
|
end
|
||||||
|
it 'valid run of 4' do
|
||||||
|
@spade3 = PlayerCard.new(rank: "3", suit: "Spade")
|
||||||
|
@club4 = PlayerCard.new(rank: "4", suit: "Club")
|
||||||
|
@diamond5 = PlayerCard.new(rank: "5", suit: "Diamond")
|
||||||
|
@diamond6 = PlayerCard.new(rank: "6", suit: "Diamond")
|
||||||
|
@cards_to_play = [@spade3, @club4, @diamond5, @diamond6]
|
||||||
|
@play.player_cards << @cards_to_play
|
||||||
|
expect(@play.hand_type).to eq('run of 4')
|
||||||
|
end
|
||||||
|
it 'valid run of 4 suited' do
|
||||||
|
@spade3 = PlayerCard.new(rank: "3", suit: "Spade")
|
||||||
|
@club4 = PlayerCard.new(rank: "4", suit: "Spade")
|
||||||
|
@diamond5 = PlayerCard.new(rank: "5", suit: "Spade")
|
||||||
|
@diamond6 = PlayerCard.new(rank: "6", suit: "Spade")
|
||||||
|
@cards_to_play = [@spade3, @club4, @diamond5, @diamond6]
|
||||||
|
@play.player_cards << @cards_to_play
|
||||||
|
expect(@play.hand_type).to eq('run of 4 suited')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when five cards are played' do
|
||||||
|
it 'valid run of 5' do
|
||||||
|
@spade3 = PlayerCard.new(rank: "3", suit: "Spade")
|
||||||
|
@club4 = PlayerCard.new(rank: "4", suit: "Club")
|
||||||
|
@diamond5 = PlayerCard.new(rank: "5", suit: "Diamond")
|
||||||
|
@diamond6 = PlayerCard.new(rank: "6", suit: "Diamond")
|
||||||
|
@diamond7 = PlayerCard.new(rank: "7", suit: "Diamond")
|
||||||
|
@cards_to_play = [@spade3, @club4, @diamond5, @diamond6, @diamond7]
|
||||||
|
@play.player_cards << @cards_to_play
|
||||||
|
expect(@play.hand_type).to eq('run of 5')
|
||||||
|
end
|
||||||
|
it 'invalid run of 5' do
|
||||||
|
@spade3 = PlayerCard.new(rank: "3", suit: "Spade")
|
||||||
|
@club4 = PlayerCard.new(rank: "4", suit: "Club")
|
||||||
|
@diamond5 = PlayerCard.new(rank: "5", suit: "Diamond")
|
||||||
|
@diamond6 = PlayerCard.new(rank: "6", suit: "Diamond")
|
||||||
|
@diamond8 = PlayerCard.new(rank: "8", suit: "Diamond")
|
||||||
|
@cards_to_play = [@spade3, @club4, @diamond5, @diamond6, @diamond8]
|
||||||
|
@play.player_cards << @cards_to_play
|
||||||
|
expect(@play.hand_type).to eq('run of 5')
|
||||||
|
expect(@play.errors[:player_cards]).to eq(['Invalid run of 5'])
|
||||||
|
end
|
||||||
|
it 'valid run of 5 suited' do
|
||||||
|
@spade3 = PlayerCard.new(rank: "3", suit: "Spade")
|
||||||
|
@club4 = PlayerCard.new(rank: "4", suit: "Spade")
|
||||||
|
@diamond5 = PlayerCard.new(rank: "5", suit: "Spade")
|
||||||
|
@diamond6 = PlayerCard.new(rank: "6", suit: "Spade")
|
||||||
|
@diamond7 = PlayerCard.new(rank: "7", suit: "Spade")
|
||||||
|
@cards_to_play = [@spade3, @club4, @diamond5, @diamond6, @diamond7]
|
||||||
|
@play.player_cards << @cards_to_play
|
||||||
|
expect(@play.hand_type).to eq('run of 5 suited')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
(5..13).each do |i|
|
||||||
|
context "when #{i} cards are played" do
|
||||||
|
it "run of #{i}" do
|
||||||
|
@cards_to_play = create_hand(i, 'run')
|
||||||
|
@play.player_cards << @cards_to_play
|
||||||
|
expect(@play.hand_type).to eq("run of #{i}")
|
||||||
|
end
|
||||||
|
it "run of #{i} suited" do
|
||||||
|
@cards_to_play = create_hand(i, 'run suited')
|
||||||
|
@play.player_cards << @cards_to_play
|
||||||
|
expect(@play.hand_type).to eq("run of #{i} suited")
|
||||||
|
end
|
||||||
|
if [6, 8, 10].include? i
|
||||||
|
it "double run of #{i}" do
|
||||||
|
@cards_to_play = create_hand(i, 'double run')
|
||||||
|
@play.player_cards << @cards_to_play
|
||||||
|
expect(@play.hand_type).to eq("double run of #{i}")
|
||||||
|
expect(@play.errors[:player_cards]).to eq([])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
5
spec/models/play_to_beat_spec.rb
Normal file
5
spec/models/play_to_beat_spec.rb
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe PlayToBeat, :type => :model do
|
||||||
|
pending "add some examples to (or delete) #{__FILE__}"
|
||||||
|
end
|
||||||
@ -2,4 +2,5 @@ require 'support/helpers/session_helpers'
|
|||||||
RSpec.configure do |config|
|
RSpec.configure do |config|
|
||||||
config.include Features::SessionHelpers, type: :feature
|
config.include Features::SessionHelpers, type: :feature
|
||||||
config.include Features::GameHelpers, type: :feature
|
config.include Features::GameHelpers, type: :feature
|
||||||
|
config.include Models::PlayHelpers, type: :model
|
||||||
end
|
end
|
||||||
|
|||||||
35
spec/support/helpers/play_helpers.rb
Normal file
35
spec/support/helpers/play_helpers.rb
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
module Models
|
||||||
|
module PlayHelpers
|
||||||
|
def create_hand(count, type)
|
||||||
|
cards = []
|
||||||
|
count.times do
|
||||||
|
cards << PlayerCard.new
|
||||||
|
end
|
||||||
|
|
||||||
|
case type
|
||||||
|
when /single/
|
||||||
|
cards[0].rank = Card::RANKS[0]
|
||||||
|
cards[0].suit = Card::SUITS[0]
|
||||||
|
when /run/
|
||||||
|
cards.each.with_index(3) do |c, i|
|
||||||
|
|
||||||
|
# double
|
||||||
|
if type.include? 'double'
|
||||||
|
c.rank = Card::RANKS[i % (cards.count/2)]
|
||||||
|
else
|
||||||
|
c.rank = Card::RANKS[i % 13]
|
||||||
|
end
|
||||||
|
|
||||||
|
# suited
|
||||||
|
if type.include? 'suited'
|
||||||
|
c.suit = Card::SUITS[0]
|
||||||
|
else
|
||||||
|
c.suit = Card::SUITS[i % 4]
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return cards
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Loading…
x
Reference in New Issue
Block a user