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 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? # Gather the truths about the hand @truths = [] # Validations # Is there a hand_to_beat ? if game.try(:hand_to_beat) # current player is not the player to beat if (game.player_to_beat != player) # hand_type match? @truths << (self.hand_type == game.play_to_beat.try("play").try("hand_type")) if game.play_to_beat.try("play").try(:hand_type) # beats? @truths << (self.beats? self.game.play_to_beat.try(:play)) end end if has_player_cards @truths << !hand_type.nil? if first_play? @truths << contains_lowest_card? end end if @truths.include? false errors.add(:player_cards, "invalid hand") end # Did any validations return false? return (!@truths.include? false) end # Check if the value of this hand is higher than the hand_to_beat def beats?(play_to_beat) if play_to_beat self.high_card_value > play_to_beat.high_card_value else false end 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.try(:plays).try(:empty?) || ( self.game.try(:plays).try(:length) == 1 && self.game.try(:plays).try(: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