diff --git a/app/models/game.rb b/app/models/game.rb index e1583fa..ed51e19 100644 --- a/app/models/game.rb +++ b/app/models/game.rb @@ -4,7 +4,7 @@ class Game < ActiveRecord::Base has_many :users, through: :players has_many :player_cards, through: :players has_many :plays, through: :players - has_one :play_to_beat + belongs_to :play_to_beat accepts_nested_attributes_for :players validates :title, presence: true @@ -107,4 +107,8 @@ class Game < ActiveRecord::Base def control_user self.players.find(self.control_player_id).user end + + def hand_to_beat + self.play_to_beat.play.hand + end end diff --git a/app/models/play.rb b/app/models/play.rb index a0cc3d9..99f436f 100644 --- a/app/models/play.rb +++ b/app/models/play.rb @@ -2,6 +2,7 @@ class Play < ActiveRecord::Base belongs_to :game, foreign_key: 'game_id' belongs_to :player, foreign_key: 'player_id' has_many :player_cards, foreign_key: 'play_id' + alias_attribute :hand, :player_cards validates :game, presence: true validates :player, presence: true @@ -15,26 +16,54 @@ class Play < ActiveRecord::Base @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 = [] + if has_player_cards - first_play? - # Open table / first play / no play to beat OR hand type matches OR play is back to the original active player - self.game.play_to_beat == nil || self.hand_type == self.game.play_to_beat.play.hand_type || self.player == self.game.play_to_beat.player + + @truths << !hand_type.empty? + + if first_play? + @truths << contains_lowest_card? + end + + # Is there a hand_to_beat ? + if self.game.play_to_beat + # current player is not the player to beat + if (self.game.play_to_beat.try('play').player != self.player) + @truths << (self.hand_type == self.game.play_to_beat.hand_type) + # Compare this hand to the hand_to_beat, + @truths << (self.beats? self.game.play_to_beat) + end + end end + return (!@truths.include? false) end - # TODO: Recognize all valid hand types + # 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.order(:value).last + end + + # TODO: Recognize face cards def hand_type case self.player_cards.length when 1 @hand_type = 'single' when 2 - @hand_type = 'pair' + @hand_type = 'double' if (self.player_cards[0].rank != self.player_cards[1].rank) errors.add(:player_cards, "Invalid pair. Rank must match.") end @@ -119,10 +148,19 @@ class Play < ActiveRecord::Base def first_play? # First play of the game if self.game.plays.empty? || ( self.game.plays.length == 1 && self.game.plays.first == self ) - # 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}") - end + 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 diff --git a/app/views/games/show.html.haml b/app/views/games/show.html.haml index a0453dc..a7c6295 100644 --- a/app/views/games/show.html.haml +++ b/app/views/games/show.html.haml @@ -29,7 +29,8 @@ = "Me" if player.user == current_user // 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 - unless player.inventory.empty? @@ -46,11 +47,12 @@ - unless @game.status == nil .table - if @game.play_to_beat - %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 + - 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 %h1 Open table diff --git a/spec/models/play_spec.rb b/spec/models/play_spec.rb index 19515cf..b22c750 100644 --- a/spec/models/play_spec.rb +++ b/spec/models/play_spec.rb @@ -28,7 +28,7 @@ RSpec.describe Play, type: :model do describe 'first_play' do context 'when there have been no plays yet' do it '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) # Play the lowest card @@ -36,158 +36,219 @@ RSpec.describe Play, type: :model do @play.player_cards << @cards_to_play @play.save expect(@play.player_cards.include? @game.lowest_card).to eq true - expect(@game.play_to_beat.play).to eq subject + #expect(@game.play_to_beat.play).to eq subject + expect(subject.valid?).to eq(true) + end + end + end + describe 'wins?' do + context 'valid if 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) + puts play + 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 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 + 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 - 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('pair') - 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('pair') - expect(@play.errors[:player_cards]).to eq(['Invalid pair. Rank must match.']) - end - 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 + 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 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 + 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 - - 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 + 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 - (5..13).each do |i| - context "when #{i} cards are played" do - it "run of #{i}" do - @cards_to_play = create_hand(i, 'run') + 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("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 + expect(@play.hand_type).to eq("double run of #{i}") + expect(@play.errors[:player_cards]).to eq([]) end end end