RSpec.describe Play, type: :model do before(:each) do @game = setup_game @play = @game.controlling_player.plays.new(game_id: @game.id) end subject { @play } 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 a game id' do 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_s).to eq("PlayerCard::ActiveRecord_Associations_CollectionProxy") end it '#hand returns a PlayerCard collection' do expect(@play.hand.class.to_s).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 describe 'validations' do describe 'first_play' do it 'valid if it contains the lowest card' do # expect(@game.plays.count).to eq(1) # Active player is the lowest card holder expect(@game.controlling_player).to eq(@game.lowest_card.player) # Play the lowest card @cards_to_play = @game.controlling_player.player_cards.order(:value).first @play.player_cards << @cards_to_play #@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 # TODO: Refactor "lowest card first" to FactoryGirl.create :game, hand_to_beat: @cards # First hand must contain lowest card @cards_to_play = @play.player.player_cards.order(:value).first @play.player_cards = [@cards_to_play] @play.save @game.reload # Play a high single card @play = @game.controlling_player.plays.new(game_id: @game.id) @cards_to_play = @play.player.player_cards.order(:value).last @play.player_cards = [@cards_to_play] @play.save @game.reload # Try to play a losing card @losing_play = @game.controlling_player.plays.new(game_id: @game.id) @losing_play.player_cards << @losing_play.player.player_cards.order(:value).first expect(@losing_play.beats? @play).to eq(false) expect(@losing_play.hand_valid?).to eq(false) expect(@losing_play.save).to eq(false) expect(@losing_play.errors[:player_cards].include?('invalid hand')).to eq(true) expect(@game.play_to_beat.play).to eq(@play) expect(@game.player_to_beat).to eq(@play.player) end it 'double' do # First hand must contain low card @cards_to_play = @play.player.player_cards.order(:value).first @play.player_cards = [@cards_to_play] @play.save @game.reload @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.where.not(id: @game.controlling_player_id).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.hand_type).to eq('double') expect(play.player_cards.length).to eq(2) end @game.reload expect(@play.valid?).to eq(false) expect(@play.beats? @play_to_beat).to eq(false) end it 'mismatch type' do @deck = Deck.new # Hand to beat is a double @cards_to_beat = [ @spade4 = PlayerCard.new(@deck.cards[4].instance_values), @club4 = PlayerCard.new(@deck.cards[5].instance_values) ] @play_to_beat = Play.new @play_to_beat.player = @game.players.where.not(id: @game.controlling_player_id).first @play_to_beat.player_cards << @cards_to_beat @play_to_beat.game = @game #@play_to_beat.save # Attempt to play a single @play.player_cards = [ @spade5 = PlayerCard.new(@deck.cards[8].instance_values) ] @game.reload expect(@play.valid?).to eq(false) end end context 'it beats the hand_to_beat' do it 'single' do # current player plays their lowest card @cards_to_play = @play.player.player_cards.order(:value).first @play.player_cards << @cards_to_play @play.save @game.reload @play = @game.controlling_player.plays.new(game_id: @game.id) # next player plays their lowest card and beats the previous play @cards_to_play = @play.player.player_cards.order(:value).first @play.player_cards << @cards_to_play expect(@play.valid?).to eq(true) @play.save @game.reload expect(@game.play_to_beat.play).to eq(@play) expect(@play.errors[:player_cards]).to eq([]) end it 'double' do @deck = Deck.new # Give 3 of clubs to current player @club3 = @game.player_cards.find_by(value: 2) @club3.player_id = @game.controlling_player.id @club3.save @cards_to_beat = [ @spade3 = @game.controlling_player.player_cards.order(:value).first, @club3 ] @play = @game.controlling_player.plays.new(game_id: @game.id) @play.player_cards = @cards_to_beat expect(@play.save).to be true @game.reload @game.controlling_player_id = @game.next_player_id @game.save @game.reload @winning_play = Play.new @winning_play.player = @game.controlling_player @winning_play.game = @game # Give needed cards to current player @spade4 = @game.player_cards.find_by(value: 5) @spade4.player_id = @game.controlling_player.id @spade4.save @club4 = @game.player_cards.find_by(value: 6) @club4.player_id = @game.controlling_player.id @club4.save @winning_play.player_cards = [ @spade4, @club4 ] expect(@winning_play.beats? @play).to eq(true) expect(@winning_play.save).to be true end it 'becomes the new play_to_beat' do expect(@play.game.play_to_beat).to eq(nil) # Play the hand @cards_to_play = @play.player.inventory.first @play.hand << @cards_to_play expect(@play.hand_valid?).to eq(true) expect(@play.save) expect(@play.game.play_to_beat.play).to eq(@play) end end end end describe 'hand_type' do context 'when a single card is played' do it 'single' do @cards_to_play = @game.controlling_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 against a 2' do # TODO: Refactor "lowest card first" to FactoryGirl.create :game, hand_to_beat: @cards # First hand must contain lowest card @spade3 = @game.lowest_card first_play = Play.new(game_id: @game.id, player_id: @game.controlling_player_id, player_cards: [@spade3]) first_play.save @game.reload # Setup play to beat @heart2 = PlayerCard.new(rank: '2', suit: 'Heart', value: 52, player_id: @game.next_player_id) @play_to_beat = Play.new(game_id: @game.id, player_id: @game.controlling_player_id, player_cards: [@heart2]) @play_to_beat.save @game.reload @spade3 = PlayerCard.new(value: 1, rank: "3", suit: "Spade") @club3 = PlayerCard.new(value: 2, rank: "3", suit: "Club") @diamond3 = PlayerCard.new(value: 3, rank: "3", suit: "Diamond") @heart3 = PlayerCard.new(value: 4, rank: "3", suit: "Heart") @cards_to_play = [@spade3, @club3, @diamond3, @heart3] @play = @game.controlling_player.plays.new(game_id: @game.id) @play.player_cards = @cards_to_play expect(@play.hand_type).to eq('bomb') expect(@play.valid?).to eq(true) end it 'valid bomb against a lesser bomb' do # Setup the play to beat @spade3 = PlayerCard.new(rank: "3", suit: "Spade", value: 1) @club3 = PlayerCard.new(rank: "3", suit: "Club", value: 2) @diamond3 = PlayerCard.new(rank: "3", suit: "Diamond", value: 3) @heart3 = PlayerCard.new(rank: "3", suit: "Heart", value: 4) @cards_to_beat = [@spade3, @club3, @diamond3, @heart3] @play_to_beat = Play.new(game_id: @game.id, player_id: @game.controlling_player_id, player_cards: @cards_to_beat) @play_to_beat.save @game.play_to_beat_id = @play_to_beat.id @game.reload # Setup the play @spade4 = PlayerCard.new(rank: "4", suit: "Spade", value: 5) @club4 = PlayerCard.new(rank: "4", suit: "Club", value: 6) @diamond4 = PlayerCard.new(rank: "4", suit: "Diamond", value: 7) @heart4 = PlayerCard.new(rank: "4", suit: "Heart", value: 8) @cards_to_play = [@spade4, @club4, @diamond4, @heart4] @play = @game.controlling_player.plays.new(game_id: @game.id) @play.player_cards = @cards_to_play expect(@play.hand_type).to eq('bomb') expect(@play.beats? @play_to_beat).to eq(true) expect(@play.valid?).to eq(true) end it 'invalid bomb against a greater bomb' do # Play lowest card first @spade3 = @game.lowest_card first_play = Play.new(game_id: @game.id, player_id: @game.controlling_player_id, player_cards: [@spade3]) first_play.save @game.reload # Create an Open Table, so every hand is valid. @game.controlling_player_id = first_play.player_id @game.save #@game.reload # # Setup the play to beat @spade4 = PlayerCard.new(rank: "4", suit: "Spade", value: 5) @club4 = PlayerCard.new(rank: "4", suit: "Club", value: 6) @diamond4 = PlayerCard.new(rank: "4", suit: "Diamond", value: 7) @heart4 = PlayerCard.new(rank: "4", suit: "Heart", value: 8) @cards_to_beat = [@spade4, @club4, @diamond4, @heart4] @play_to_beat = Play.new(game_id: @game.id, player_id: @game.controlling_player_id, player_cards: @cards_to_beat) expect(@play_to_beat.save).to eq true @game.reload # Setup the play @spade3 = PlayerCard.new(rank: "3", suit: "Spade", value: 1) @club3 = PlayerCard.new(rank: "3", suit: "Club", value: 2) @diamond3 = PlayerCard.new(rank: "3", suit: "Diamond", value: 3) @heart3 = PlayerCard.new(rank: "3", suit: "Heart", value: 4) @cards_to_play = [@spade3, @club3, @diamond3, @heart3] @play = Play.new(game_id: @game.id, player_id: @game.controlling_player_id, player_cards: @cards_to_play) expect(@play.hand_type).to eq('bomb') #TODO: Refactor this test to only test the @play.beats? method #TODO: Move other expectations to another test #TODO: Test every validation of a play expect(@play.beats? @play_to_beat).to eq(false) #TODO: Refactor @play.valid? method to account for beats? pending('#EXPECT invalid play Refactor @play.valid? method to account for beats?') expect(@play.valid?).to eq(false) 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 be_empty end end end end end end