Instantly share code, notes, and snippets.

# Peranikov/facility.rb Last active Apr 9, 2017

 class Facility attr_reader :name, :cost, :income, :range, :color def initialize(name:, cost:, income:, color:, range:) @name = name @cost = cost @income = income @color = color @range = Array(range) end def self.initial_data [ Facility.new(name: "麦畑", cost: 1, income: 1, color: :blue, range: 1), Facility.new(name: "牧場", cost: 1, income: 1, color: :blue, range: 2), Facility.new(name: "パン屋", cost: 1, income: 1, color: :green, range: [2, 3]), Facility.new(name: "コンビニ", cost: 2, income: 4, color: :green, range: 4), Facility.new(name: "カフェ", cost: 2, income: 3, color: :red, range: 3), Facility.new(name: "森林", cost: 3, income: 1, color: :blue, range: 5), Facility.new(name: "鉱山", cost: 6, income: 5, color: :blue, range: 9), Facility.new(name: "ファミレス", cost: 3, income: 2, color: :red, range: [9, 10]), Facility.new(name: "リンゴ園", cost: 3, income: 2, color: :blue, range: 10), ] end end
 require("./facility") # ダイスを1つ振った時の各目の確率 def solo_dice_ptobabilities(range) range.reduce(0) do |sum, r| sum + if r >= 1 && r <= 6 Rational(1, 6) else 0 end end end # ダイスを2つ振った時の各目の確率 def twice_dice_ptobabilities(range) range.reduce(0) do |sum, r| sum + ( { 2 => Rational(1, 36), 3 => Rational(2, 36), 4 => Rational(3, 36), 5 => Rational(4, 36), 6 => Rational(5, 36), 7 => Rational(6, 36), 8 => Rational(5, 36), 9 => Rational(4, 36), 10 => Rational(3, 36), 11 => Rational(2, 36), 12 => Rational(1, 36), }[r] || 0 ) end end class Machikoro def initialize(player_number: 4) @player_number = player_number end # 階乗 def factorial(n) return 1 if n == 0 (1..n).inject(:*) end alias :f :factorial # 組み合わせ def combination(n, r) f(n) / (f(n - r) * f(r)) end alias :c :combination # 期待値 # income 報酬 # probability 報酬の出る確率 # tries 試行回数 def mean(income, probability, tries) t = probability # 目が出る確率 f = 1 - probability # 目以外が出る確率 (1..tries).map { |k| (k * income) * (c(tries, k)) * (t ** k) * (f ** (tries - k)) }.reduce(:+) end # 試行回数 def tries(color) case color when :blue @player_number # 全てのプレイヤーのターン when :green 1 # 自分のターン when :red @player_number - 1 # 相手のターン end end # 元が取れるまで def break_even(cost, mean) return Float::INFINITY if mean == 0 (cost / mean).round end def run puts "プレイヤー数(試行回数): #{@player_number}" puts "" Facility.initial_data.each do |f| t = tries(f.color) m1 = mean(f.income, solo_dice_ptobabilities(f.range), t) m2 = mean(f.income, twice_dice_ptobabilities(f.range), t) b1 = break_even(f.cost, m1) b2 = break_even(f.cost, m2) puts "#{f.name}(#{f.color}) 目:#{f.range} コスト:#{f.cost} 収入:#{f.income}" puts "サイコロが1つ 期待値:#{m1} 元が取れるターン数:#{b1}" puts "サイコロが2つ 期待値:#{m2} 元が取れるターン数:#{b2}" puts "" end end end player_number = (ARGV[0] || 4).to_i Machikoro.new(player_number: player_number).run
 プレイヤー数(試行回数): 4 麦畑(blue) 目:[1] コスト:1 収入:1 サイコロが1つ 期待値:2/3 元が取れるターン数:2 サイコロが2つ 期待値:0 元が取れるターン数:Infinity 牧場(blue) 目:[2] コスト:1 収入:1 サイコロが1つ 期待値:2/3 元が取れるターン数:2 サイコロが2つ 期待値:1/9 元が取れるターン数:9 パン屋(green) 目:[2, 3] コスト:1 収入:1 サイコロが1つ 期待値:1/3 元が取れるターン数:3 サイコロが2つ 期待値:1/12 元が取れるターン数:12 コンビニ(green) 目:[4] コスト:2 収入:4 サイコロが1つ 期待値:2/3 元が取れるターン数:3 サイコロが2つ 期待値:1/3 元が取れるターン数:6 カフェ(red) 目:[3] コスト:2 収入:3 サイコロが1つ 期待値:3/2 元が取れるターン数:1 サイコロが2つ 期待値:1/2 元が取れるターン数:4 森林(blue) 目:[5] コスト:3 収入:1 サイコロが1つ 期待値:2/3 元が取れるターン数:5 サイコロが2つ 期待値:4/9 元が取れるターン数:7 鉱山(blue) 目:[9] コスト:6 収入:5 サイコロが1つ 期待値:0 元が取れるターン数:Infinity サイコロが2つ 期待値:20/9 元が取れるターン数:3 ファミレス(red) 目:[9, 10] コスト:3 収入:2 サイコロが1つ 期待値:0 元が取れるターン数:Infinity サイコロが2つ 期待値:7/6 元が取れるターン数:3 リンゴ園(blue) 目:[10] コスト:3 収入:2 サイコロが1つ 期待値:0 元が取れるターン数:Infinity サイコロが2つ 期待値:2/3 元が取れるターン数:5