Files
kondiplo_front/app/models/game.rb
kontei f25fd6f802
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
フロントエンドプレイアブル
2026-02-15 14:57:17 +09:00

166 lines
5.1 KiB
Ruby
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
class Game < ApplicationRecord
has_many :turns, dependent: :destroy
has_many :participants, dependent: :destroy
has_many :users, through: :participants
# パスワード保護
has_secure_password :password, validations: false
# バリデーション
validates :status, inclusion: {
in: %w[recruiting power_selection in_progress finished cancelled]
}
validates :auto_order_mode, inclusion: { in: %w[hold random] }
validates :participants_count,
numericality: { greater_than_or_equal_to: 2, less_than_or_equal_to: 7 },
unless: :is_solo_mode?
# ハウスルールバリデーション
validates :year_limit,
numericality: { only_integer: true, greater_than_or_equal_to: 1901, less_than_or_equal_to: 1999 },
allow_nil: true
validates :victory_sc_count,
numericality: { only_integer: true, greater_than_or_equal_to: 1, less_than_or_equal_to: 34 }
validates :scoring_system, inclusion: { in: %w[none sc_count sc_ratio dss sos] }
# ターンスケジュールバリデーション
validate :validate_turn_schedule
# ヘルパーメソッド
def password_protected?
password_digest.present?
end
def solo_mode?
is_solo_mode
end
def administrator
participants.find_by(is_administrator: true)&.user
end
def available_powers
assigned_powers = participants.where.not(power: nil).pluck(:power)
%w[AUSTRIA ENGLAND FRANCE GERMANY ITALY RUSSIA TURKEY] - assigned_powers
end
def all_powers_assigned?
participants.where(power: nil).empty? &&
participants.count == participants_count
end
def all_orders_submitted?
participants.where(power: nil).empty? &&
participants.all?(&:orders_submitted)
end
def unassigned_powers
all_powers = %w[AUSTRIA ENGLAND FRANCE GERMANY ITALY RUSSIA TURKEY]
assigned_powers = participants.where.not(power: nil).pluck(:power)
all_powers - assigned_powers
end
# ターンスケジュール関連メソッド
def auto_turn?
turn_schedule.present?
end
# 次のデッドラインを計算(日本時間基準)
def calculate_next_deadline
return nil unless turn_schedule.present?
hours = turn_schedule.split(",").map(&:strip).map(&:to_i).sort
now = Time.current.in_time_zone("Asia/Tokyo")
# 今日の残りの時間枠を探すJST基準
next_time = hours.map { |h| now.beginning_of_day + h.hours }
.find { |t| t > now }
# 今日の枠がなければ翌日の最初の枠
next_time || (now.beginning_of_day + 1.day + hours.first.hours)
end
# スケジュール表示用
def schedule_display
return "手動" unless turn_schedule.present?
hours = turn_schedule.split(",").map(&:strip)
"毎日 " + hours.map { |h| "#{h}" }.join("")
end
# ハウスルール関連メソッド
# 年数制限チェック: フェーズ名から年を抽出し、year_limitを超えているか判定
def year_limit_reached?(phase_name)
return false unless year_limit.present? && phase_name.present?
# フェーズ名の例: "S1901M", "F1910R", "W1901A"
year_match = phase_name.match(/[SFW](\d{4})[MRA]/)
return false unless year_match
year_match[1].to_i > year_limit
end
# ソロ勝利判定: いずれかの国のSC数がvictory_sc_count以上か
def solo_victory?(game_state)
centers = game_state&.dig("centers") || {}
centers.any? { |_power, scs| scs.size >= victory_sc_count }
end
# ソロ勝利した国を返す
def solo_victory_power(game_state)
centers = game_state&.dig("centers") || {}
centers.find { |_power, scs| scs.size >= victory_sc_count }&.first
end
# スコア計算
def calculate_scores(game_state)
return {} if scoring_system == "none"
centers = game_state&.dig("centers") || {}
total_scs = centers.values.flatten.size
alive_count = centers.count { |_power, scs| scs.any? }
case scoring_system
when "sc_count"
# 単純SC数
centers.transform_values { |scs| scs.size }
when "sc_ratio"
# SC比率%
centers.transform_values { |scs| total_scs > 0 ? (scs.size.to_f / total_scs * 100).round(1) : 0 }
when "dss"
# Draw Size Scoring: 生存国で均等分割
centers.transform_values { |scs| scs.any? ? (100.0 / alive_count).round(1) : 0 }
when "sos"
# Sum of Squares
sum_of_squares = centers.values.sum { |scs| scs.size ** 2 }.to_f
centers.transform_values { |scs| sum_of_squares > 0 ? ((scs.size ** 2) / sum_of_squares * 100).round(1) : 0 }
else
{}
end
end
# スコアリング方式の日本語名
def scoring_system_name
case scoring_system
when "none" then "なし"
when "sc_count" then "SC数"
when "sc_ratio" then "SC比率"
when "dss" then "DSS均等分割"
when "sos" then "SoS二乗和"
else scoring_system
end
end
private
def validate_turn_schedule
return if turn_schedule.blank?
hours = turn_schedule.split(",").map(&:strip)
unless hours.all? { |h| h.match?(/\A\d{1,2}\z/) && h.to_i.between?(0, 23) }
errors.add(:turn_schedule, "は0〜23の数値をカンマ区切りで入力してください例: 0,18")
end
end
end