209 lines
5.8 KiB
Ruby
209 lines
5.8 KiB
Ruby
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
|
|
after_save :check_for_winner
|
|
|
|
def check_for_winner
|
|
if self.game.over?
|
|
self.game.winner_player_id = self.player.id
|
|
self.game.status = "Player #{self.player.id} wins!"
|
|
self.game.save
|
|
end
|
|
end
|
|
|
|
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?
|
|
return false if self.hand_type == nil
|
|
# Gather the truths about the hand
|
|
@truths = []
|
|
|
|
# Validations
|
|
|
|
# Is there a hand_to_beat ?
|
|
if !game.try(:hand_to_beat).blank?
|
|
# current player is not the player to beat
|
|
if (game.player_to_beat != player)
|
|
# valid bomb?
|
|
if (self.hand_type == 'bomb' && game.play_to_beat.try("play").try("hand_type") == 'single' && game.play_to_beat.try("play").try("player_cards")[0].try('rank') == '2')
|
|
@truths << true
|
|
return @truths
|
|
end
|
|
# 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 0
|
|
@hand_type = 'empty'
|
|
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 = [self.player_cards.map {|pc| pc.rank}].flatten!.sort_by(&:to_i)
|
|
@suits = [self.player_cards.map {|pc| pc.suit}].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_by(&:to_i)[0...-1].each.with_index do |rank, i|
|
|
# each rank one away from each other?
|
|
if (rank.to_i + 1) == @ranks.uniq.sort_by(&:to_i)[i+1].to_i
|
|
# valid do nothing
|
|
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_by(&:to_i)[0...-1].each_with_index do |rank, i|
|
|
# If cards are not a single run
|
|
if (rank.to_i + 1) != @ranks[i+1].to_i
|
|
# And if cards are not a 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
|