最新 追記

ema log


2015年11月02日 [長年日記] この日を編集

_ [game] THE GAME on fire のヴァリアント部分の和訳

Google 翻訳にかけた内容から校正しました。

ヴァリアント

山札から値が「22、33、44、55、66、77」の6枚のカードを除外し、火災カードの6枚のカードと交換して下さい(元のカードは箱に戻して下さい!)。ゲームの準備とプロセスは通常の「THE GAME」と同様です。

ただし、火災カードは遅くとも、次のプレーヤのアクションで覆い隠す必要があります。もし覆い隠すことが出来なければ、直ちに敗北となります

注:1人プレイでも、火災カードは、遅くとも次のアクションで覆い隠す必要があります

原文

 Entfernen Sie die 6 Spielkarten mit den Werten 22, 33, 44, 55, 66 und 77 aus dem Spiel (zurück in die Schachtel legen!) und ersetzen Sie diese durch die 6 Feuer-Karten.
 Die Spielvorbereitung und der Ablauf bleiben exakt so, wie beim normalen "The Game"-Spiel, mit folgendem Zusatz: Spielt jemand eine Feuer-Karte aus, muss diese spätestens in der Aktion des folgenden Spielers abgedeckt werden, sonst ist das Spiel sofort verloren.
 Beachte: Auch in der Solovariante muss die Feuer-Karte spätestens in der folgenden Aktion abgedeckt werden.

お名前 : コメント :


2015年11月07日 [長年日記] この日を編集

_ [game]ごいたの初期手牌シミュレーション

1000万回シミュレーションさせてみました。し、王に関する数値はゲームで登場する確率ですが、香に関する数値は自分がそれを積もる確率です。(18:45、追記:香を二重にカウントしてしまっていたので修正しました)

(11/16追記)自分が積もる確率に統一した、シミュと理論値を出しました

(11/13追記)自分が積もる確率に統一した、5し5し以外のシミュと理論値を出しました

(11/12追記)5し5しが高すぎるとの指摘が入って、感覚的にも、理論的にもバグってそうなのですがバグが見つけられません。

役名出現回数パーセンテージ (有効数字三桁)
五し147620314.8 %
六し1844351.84 %
七し102550.103 %
八し1440.00144 %
五し五し9900.0099 %
三香3745413.75 %
四香194280.194 %
王だまだま225865122.6 %

ソースは以下の通りです

class SHI  ; def to_s; "し"; end; def to_i; 0x11; end; end
class GON  ; def to_s; "香"; end; def to_i; 0x21; end; end
class GIN  ; def to_s; "金"; end; def to_i; 0x31; end; end
class KIN  ; def to_s; "銀"; end; def to_i; 0x32; end; end
class BAKKO; def to_s; "馬"; end; def to_i; 0x33; end; end
class KAKU ; def to_s; "角"; end; def to_i; 0x41; end; end
class HI   ; def to_s; "飛"; end; def to_i; 0x42; end; end
class OU   ; def to_s; "王"; end; def to_i; 0x51; end; end

TIMES = 10000000

def main
	yama = Array.new(10, SHI.new)
	yama.concat Array.new( 4, GON.new)
	yama.concat Array.new( 4, GIN.new)
	yama.concat Array.new( 4, KIN.new)
	yama.concat Array.new( 4, BAKKO.new)
	yama.concat Array.new( 2, KAKU.new)
	yama.concat Array.new( 2, HI.new)
	yama.concat Array.new( 2, OU.new)

	goshi = rokushi = nanashi = hasshi = goshigoshi = sanngon = yonngon = 0

	damadama = 0

	TIMES.times do
#	100.times do
		yama.shuffle!
		hand1 = yama[ 0,8]#.sort_by{|o|o.to_i}
		hand2 = yama[ 8,8]#.sort_by{|o|o.to_i}
		hand3 = yama[16,8]#.sort_by{|o|o.to_i}
		hand4 = yama[24,8]#.sort_by{|o|o.to_i}

=begin
		puts hand1.map{|o|o.to_s}.join
		puts hand2.map{|o|o.to_s}.join
		puts hand3.map{|o|o.to_s}.join
		puts hand4.map{|o|o.to_s}.join
=end

		# ダマダマは発生する確率が欲しいので全部で調べる
		ou_counts = [
			hand1.count{|o|o.class == OU},
			hand2.count{|o|o.class == OU}, 
			hand3.count{|o|o.class == OU},
			hand4.count{|o|o.class == OU}
		]
		ou_counts.each do |ou_count|
			damadama += 1 if ou_count == 2
		end

		# しは5し5しが有るのでペアで調べないといけない 
		shi_counts = [
			[
				hand1.count{|o|o.class == SHI},
				hand2.count{|o|o.class == SHI}
			],
			[
				hand3.count{|o|o.class == SHI},
				hand4.count{|o|o.class == SHI}
			]
		]

		shi_counts.each do |shi_count1, shi_count2|
			case shi_count1
			when 5
				if shi_count2 == 5
					goshigoshi += 1
				else
					goshi += 1
				end
			when 6 then rokushi += 1
			when 7 then nanashi += 1
			when 8 then hasshi  += 1
			end
	
			case shi_count2
			when 5
				goshi += 1 if shi_count1 != 5 # 5し5しのときはカウントしない
			when 6 then rokushi += 1
			when 7 then nanashi += 1
			when 8 then hasshi  += 1
			end
		end

		# 香は自分の手札に来る確率を求めたいので、自分の手札だけみる
		gon_count  = hand1.count{|o|o.class == GON}
		case gon_count
		when 3 then sanngon += 1
		when 4 then yonngon += 1
		end
	end

	puts "だまだま:#{damadama}…#{damadama*100.0/TIMES} %"
	puts "五し:#{goshi  }…#{goshi  *100.0/TIMES} %"
	puts "六し:#{rokushi}…#{rokushi*100.0/TIMES} %"
	puts "七し:#{nanashi}…#{nanashi*100.0/TIMES} %"
	puts "八し:#{hasshi }…#{hasshi *100.0/TIMES} %"
	puts "五し五し:#{goshigoshi}…#{goshigoshi*100.0/TIMES} %"
	puts "三香:#{sanngon}…#{sanngon*100.0/TIMES} %"
	puts "四香:#{yonngon}…#{yonngon*100.0/TIMES} %"
end

main

お名前 : コメント :


2015年11月10日 [長年日記] この日を編集

_ [Programming]ごいたシミュレーションのソースコードの解説

簡単に先日のシミュレーションのソースコード解説のメモを。デバッグ用コードのコメントアウトは消しています。

定数宣言

まずは、定数代わりにクラスを宣言しています。デバッグ時に文字列表示したくなったからです。遅くなりましたが、まぁコーヒー飲んでれば終わる次元なので問題なし。

class SHI  ; def to_s; "し"; end; def to_i; 0x11; end; end
class GON  ; def to_s; "香"; end; def to_i; 0x21; end; end
class GIN  ; def to_s; "金"; end; def to_i; 0x31; end; end
class KIN  ; def to_s; "銀"; end; def to_i; 0x32; end; end
class BAKKO; def to_s; "馬"; end; def to_i; 0x33; end; end
class KAKU ; def to_s; "角"; end; def to_i; 0x41; end; end
class HI   ; def to_s; "飛"; end; def to_i; 0x42; end; end
class OU   ; def to_s; "王"; end; def to_i; 0x51; end; end

定数でシミュレーション回数を宣言

TIMES = 10000000

main関数

一応、main 関数を作って、プログラム末尾から呼ぶ形にするのが好きです。

def main
山を作る

山を作ります。牌種に応じた枚数の配列を作り、連結していきます。ごいた牌をケースから取り出してるイメージですね。

	yama = Array.new(10, SHI.new)
	yama.concat Array.new( 4, GON.new)
	yama.concat Array.new( 4, GIN.new)
	yama.concat Array.new( 4, KIN.new)
	yama.concat Array.new( 4, BAKKO.new)
	yama.concat Array.new( 2, KAKU.new)
	yama.concat Array.new( 2, HI.new)
	yama.concat Array.new( 2, OU.new)
カウンタの初期化

各種発生回数のカウンタを0に初期化します。

	goshi = rokushi = nanashi = hasshi = goshigoshi = sanngon = yonngon = 0
	damadama = 0
シミュレーション

シミュレーション回数分のループを回します。

	TIMES.times do
配牌

配牌を配ります。山をシャッフルして、8枚ずつ取り出します。1枚ずつ配らなくて良いの?と思われるかもしれませんが、シャッフルが公正なら連続して8枚とっても確率での差は生まれないので8枚連続して配っています。

		yama.shuffle!
		hand1 = yama[ 0,8]
		hand2 = yama[ 8,8]
		hand3 = yama[16,8]
		hand4 = yama[24,8]
「香」周りのカウント

まずは、「香」についてが分かりやすいので順番を元のソースと入れ替えて先に説明します。

「香」だけは自分の積もる確率が知りたかったので、一人分の手牌だけを調べています。Array#count はブロックを付けて渡すと、その内容が true になる個数を返してくれます。ここでは GON クラスの個数、すなわち、手牌に「香」のある個数を調べているわけです。

	# 香は自分の手札に来る確率を求めたいので、自分の手札だけみる
		gon_count  = hand1.count{|o|o.class == GON}

後は、「香」の枚数毎にカウンタを加算するのみです。

		case gon_count
		when 3 then sanngon += 1
		when 4 then yonngon += 1
		end
「王」のダマダマのカウント

次に、「王」のダマダマを調べるコードです。四人の手牌に含まれる王の枚数をカウントし、それぞれについて「王」が2枚あれば、ダマダマカウンタを1増やしています。枚数の数え方は「香」と同一。山に「王」は2枚しか無いことが保証されているので重複の確認などは省いています。

		# ダマダマは発生する確率が欲しいので全部で調べる
		ou_counts = [
			hand1.count{|o|o.class == OU},
			hand2.count{|o|o.class == OU},
			hand3.count{|o|o.class == OU},
			hand4.count{|o|o.class == OU}
		]
		ou_counts.each do |ou_count|
			damadama += 1 if ou_count == 2
		end
「し」周りのカウント

つぎに「し」についてカウントしています。「5し5し」を調べるためにペアを組ませて、そのそれぞれに対してカウントする必要があります。そのために配列の配列を作っています。本来のルールではペアは対角で組みますが、ペアの組み方さえ変わらなければシミュレーション上の差は生まれないため、ここでは可読性を優先。

		# しは5し5しが有るのでペアで調べないといけない
		shi_counts = [
			[
				hand1.count{|o|o.class == SHI},
				hand2.count{|o|o.class == SHI}
			],
			[
				hand3.count{|o|o.class == SHI},
				hand4.count{|o|o.class == SHI}
			]
		]

Ruby では配列の配列のそれぞれをループして調べる際に、次のような書き方で配列の展開が出来ます。shi_count1 に配列の1番目が、shi_count2 に配列の2番目が代入されます。楽ちんですね。

		shi_counts.each do |shi_count1, shi_count2|

後は、ペアのプレイヤー毎に「し」の枚数を調べるだけです。「5し5し」のチェックだけ例外処理が入っています。shi_count1==5 かつ shi_count2==5 のときのみ、「5し5し」のカウンタを、そうでない単なる「5し」の場合には「5し」のカウンタを加算しています。「6し」「7し」「8し」は重複することがあり得ないため、こちらの例外処理は省いています。

			case shi_count1
			when 5
				if shi_count2 == 5
					goshigoshi += 1
				else
					goshi += 1
				end
			when 6 then rokushi += 1
			when 7 then nanashi += 1
			when 8 then hasshi  += 1
			end

			case shi_count2
			when 5
				goshi += 1 if shi_count1 != 5 # 5し5しのときはカウントしない
			when 6 then rokushi += 1
			when 7 then nanashi += 1
			when 8 then hasshi  += 1
			end
		end
	end
結果出力

さて、以上でシミュレーションが終わったので、後は結果を出力するだけです。それぞれの発生回数と、発生確率を計算しているだけですね。

	puts "だまだま:#{damadama}…#{damadama*100.0/TIMES} %"
	puts "五し:#{goshi  }…#{goshi  *100.0/TIMES} %"
	puts "六し:#{rokushi}…#{rokushi*100.0/TIMES} %"
	puts "七し:#{nanashi}…#{nanashi*100.0/TIMES} %"
	puts "八し:#{hasshi }…#{hasshi *100.0/TIMES} %"
	puts "五し五し:#{goshigoshi}…#{goshigoshi*100.0/TIMES} %"
	puts "三香:#{sanngon}…#{sanngon*100.0/TIMES} %"
	puts "四香:#{yonngon}…#{yonngon*100.0/TIMES} %"
end
main 関数呼び出し

最後に、main 関数を呼ぶコードを書いておいて、main 関数内に処理を書くスタイルが好きです。ただそれだけ。

main

以上がソースコードの概略です。プログラミング分かる人向けの説明しか書けません。

お名前 : コメント :


2015年11月13日 [長年日記] この日を編集

_ [game]ごいたのシミュレーション修正版

全ての数値を自分が積もる確率に変更しました。シミュレーション回数は1000万回です。(11/16追記)ごいたのシミュレーション決定版に更新しました。5し5しについては、ゆっくり考えて理論値を算出してみます。

シミュレーション結果

名称シミュ理論値
五し3.70%3.69%≒1/27.1
六し0.46%0.46%≒1/217
七し0.0255%0.0251%≒1/3,984
八し0.000430%0.000428%≒1/233,740
だまだま5.65%5.65%≒1/17.7
三香3.73%3.74%≒1/26.8
四香0.191%0.195%≒1/514

理論値の算出方法

基本的にこの考え方で合っているはずです。例えば8しを算出すると

10C8 * (32-10)C(8-8) / 32C8 = 10C8 * 22C0 / 32C8 = 0.000428%

また、5しを算出すると

10C5 * (32-10)C(8-5) / 32C8 = 10C5 * 22C3 / 32C8 = 3.6896%

ソースコード

class SHI  ; def to_s; "し"; end; def to_i; 0x11; end; end
class GON  ; def to_s; "香"; end; def to_i; 0x21; end; end
class GIN  ; def to_s; "金"; end; def to_i; 0x31; end; end
class KIN  ; def to_s; "銀"; end; def to_i; 0x32; end; end
class BAKKO; def to_s; "馬"; end; def to_i; 0x33; end; end
class KAKU ; def to_s; "角"; end; def to_i; 0x41; end; end
class HI   ; def to_s; "飛"; end; def to_i; 0x42; end; end
class OU   ; def to_s; "王"; end; def to_i; 0x51; end; end

TIMES = 10000000

def main
	yama = Array.new(10, SHI.new)
	yama.concat Array.new( 4, GON.new)
	yama.concat Array.new( 4, GIN.new)
	yama.concat Array.new( 4, KIN.new)
	yama.concat Array.new( 4, BAKKO.new)
	yama.concat Array.new( 2, KAKU.new)
	yama.concat Array.new( 2, HI.new)
	yama.concat Array.new( 2, OU.new)

	goshi = rokushi = nanashi = hasshi = sanngon = yonngon = 0

	damadama = 0

	TIMES.times do
		yama.shuffle!
		hand1 = yama[ 0,8]
		hand2 = yama[ 8,8]
		hand3 = yama[16,8]
		hand4 = yama[24,8]

		 ou_count = hand1.count{|o|o.class == OU}
		shi_count = hand1.count{|o|o.class == SHI}
		gon_count = hand1.count{|o|o.class == GON}

		damadama += 1 if ou_count == 2

		case shi_count
		when 5 then goshi   += 1
		when 6 then rokushi += 1
		when 7 then nanashi += 1
		when 8 then hasshi  += 1
		end

		case gon_count
		when 3 then sanngon += 1
		when 4 then yonngon += 1
		end
	end

	puts "だまだま:#{damadama}…#{damadama*100.0/TIMES} %"
	puts "五し:#{goshi  }…#{goshi  *100.0/TIMES} %"
	puts "六し:#{rokushi}…#{rokushi*100.0/TIMES} %"
	puts "七し:#{nanashi}…#{nanashi*100.0/TIMES} %"
	puts "八し:#{hasshi }…#{hasshi *100.0/TIMES} %"
 	puts "三香:#{sanngon}…#{sanngon*100.0/TIMES} %"
	puts "四香:#{yonngon}…#{yonngon*100.0/TIMES} %"
end

main

お名前 : コメント :


2015年11月16日 [長年日記] この日を編集

_ [game]ごいたのシミュレーション決定版

全ての数値を自分が積もる確率に変更しました。シミュレーション回数は1億回です。5し5しだけ保留していましたが納得がいったので数値を公開します。

シミュレーション結果

名称シミュ理論値
五し3.69%3.69%≒1/27.1
六し0.46%0.46%≒1/217
七し0.0255%0.0251%≒1/3,984
八し0.000410%0.000428%≒1/233,740
五し五し0.00489%0.00486%≒1/20,572
だまだま5.64%5.65%≒1/17.7
三香3.74%3.74%≒1/26.8
四香0.195%0.195%≒1/514

シミュレーション回数を一桁増やしたことで、レアな事象以外は概ね有効数値3桁ぐらいで収束してそうです。

理論値の算出方法

ということで、例の如く、ゆらゆらさんに理論値を算出していただきました。

とかやりとりしていたら

既に詳細なシミュレーションがあることをじぜまろさんから教えていただきました。しかも勝敗が配牌で決まる場合分けされている、より良い内容でした(こちらの計算結果はゲームで登場する確率)。

”ごいた”の駒を配った時点で勝敗が確定している確率を計算してみた

ソースコード

クラスをインスタンス化せず、そのままクラス自体を定数代わりにするようにしたのでちょっと速くなった分、シミュレーション回数を1億回に増やしました。

class SHI  ; def self.to_s; "し"; end; def self.to_i; 0x11; end; end
class GON  ; def self.to_s; "香"; end; def self.to_i; 0x21; end; end
class GIN  ; def self.to_s; "金"; end; def self.to_i; 0x31; end; end
class KIN  ; def self.to_s; "銀"; end; def self.to_i; 0x32; end; end
class BAKKO; def self.to_s; "馬"; end; def self.to_i; 0x33; end; end
class KAKU ; def self.to_s; "角"; end; def self.to_i; 0x41; end; end
class HI   ; def self.to_s; "飛"; end; def self.to_i; 0x42; end; end
class OU   ; def self.to_s; "王"; end; def self.to_i; 0x51; end; end

TIMES = 100000000

def main
	yama = Array.new(10, SHI)
	yama.concat Array.new( 4, GON)
	yama.concat Array.new( 4, GIN)
	yama.concat Array.new( 4, KIN)
	yama.concat Array.new( 4, BAKKO)
	yama.concat Array.new( 2, KAKU)
	yama.concat Array.new( 2, HI)
	yama.concat Array.new( 2, OU)

	goshi = rokushi = nanashi = hasshi = goshigoshi = sanngon = yonngon = 0

	damadama = 0

	TIMES.times do
		yama.shuffle!
		hand1 = yama[ 0,8]
		hand2 = yama[ 8,8]
		hand3 = yama[16,8]
		hand4 = yama[24,8]
		 ou_count = hand1.count(OU )
		shi_count = hand1.count(SHI)
		shi_count2= hand2.count(SHI)
		gon_count = hand1.count(GON)

		damadama += 1 if ou_count == 2

		goshigoshi += 1 if shi_count==5 && shi_count2==5

		case shi_count
		when 5 then goshi   += 1
		when 6 then rokushi += 1
		when 7 then nanashi += 1
		when 8 then hasshi  += 1
		end

		case gon_count
		when 3 then sanngon += 1
		when 4 then yonngon += 1
		end
	end

	puts "だまだま:#{damadama}…#{damadama*100.0/TIMES} %"
	puts "五し:#{goshi  }…#{goshi  *100.0/TIMES} %"
	puts "六し:#{rokushi}…#{rokushi*100.0/TIMES} %"
	puts "七し:#{nanashi}…#{nanashi*100.0/TIMES} %"
	puts "八し:#{hasshi }…#{hasshi *100.0/TIMES} %"
	puts "五し五し:#{goshigoshi}…#{goshigoshi*100.0/TIMES} %"
	puts "三香:#{sanngon}…#{sanngon*100.0/TIMES} %"
	puts "四香:#{yonngon}…#{yonngon*100.0/TIMES} %"
end

main

お名前 : コメント :