マルバツゲーム
Posted feedbacks - Ruby
Rubyらしくなくビット演算を使ってみました。○,×それぞれ9ビットのビット配列(実際にはただの整数)を用意し、ゲーム盤を表現しています。
0x1c0, 0x124, 0x111, 0x92, 0x54, 0x49, 0x38, 0x7
は3つ並んだかどうか判定するためのマスクです。例えば、0x111は2進数で100010001となりますが、これを3ビットずつ並べると
100
010
001
となり、斜めの判定パターンになってることが分かります。これと○・×のビット配列の論理和を取ることで、並んだかの判定を行うことが出来ます。
実行例です。
ruby marubatu.rb
{"PLAYER 0"=>5870, "PLAYER 1"=>2828, "DRAW"=>1302}
PLAYER 0が先手、 PLAYER 1が後手です。
0x1c0, 0x124, 0x111, 0x92, 0x54, 0x49, 0x38, 0x7
は3つ並んだかどうか判定するためのマスクです。例えば、0x111は2進数で100010001となりますが、これを3ビットずつ並べると
100
010
001
となり、斜めの判定パターンになってることが分かります。これと○・×のビット配列の論理和を取ることで、並んだかの判定を行うことが出来ます。
実行例です。
ruby marubatu.rb
{"PLAYER 0"=>5870, "PLAYER 1"=>2828, "DRAW"=>1302}
PLAYER 0が先手、 PLAYER 1が後手です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | class Judge
def initialize(player1, player2)
@players = []
@players[0] = player1
@players[1] = player2
end
def is_end?(ban)
[0x1c0, 0x124, 0x111, 0x92, 0x54, 0x49, 0x38, 0x7].each do |mask|
if ban & mask == mask then
return true
end
end
return false
end
KOMA = {0 => " ", 1 => "○", 2 => "×"}
def display(play1, play2)
print "---\n"
(0..2).each do |i|
(0..2).each do |j|
print KOMA[(play1 & 1) + ((play2 & 1) << 1)]
play1 >>= 1
play2 >>= 1
end
print "\n"
end
print "---\n"
print "\n"
end
def play(vervose = false)
bans = [0, 0]
curplayer = 0
(1..9).each do |no|
other = 1 - curplayer
bans[curplayer] = @players[curplayer].think(bans[curplayer], bans[other])
if vervose then
display(bans[0], bans[1])
end
if is_end?(bans[curplayer]) then
return "PLAYER #{curplayer}"
end
curplayer = other
end
"DRAW"
end
end
class Player
def think(myban, yourban)
r = 0
begin
r = rand(9)
end while ((myban | yourban) & (1 << r) != 0)
myban | (1 << r)
end
end
result = Hash.new(0)
jd = Judge.new(Player.new, Player.new)
(1..10000).each do |n|
result[jd.play] += 1
end
p result
|
賢いプレーヤのいるマルバツゲームの布石として作ってみました. 空欄の位置を取得して, 適当に選んだ位置に印を付けるようなアルゴリズムになってると思います.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | class Game
def initialize(n=1)
@n, @out = n, Array.new(n)
@p0, @p1 = Player.new, Player.new
@out.map!{MaruBatu.new(@p0, @p1).run} #collect results
# p @p0
end
class MaruBatu
def initialize(*players)
@size = 3
@b = Array.new(@size){Array.new(@size)} #board
@p = [:b, :w].zip(players). #players list
map{|m,p| {:mark => m, :player => p}}
end
def run
move until win? or pos_list.empty?
win? ? cur[:player].win : @p.each{|h| h[:player].even}
self
end
private
def cur() @p.first end #current move player
def pos_list #get positions blanked
scan(@b).reject(&:first).map(&:last) end
def win?
cross = [proc{|x,pos| pos.first == pos.last},
proc{|x,pos| pos.first + pos.last == @size-1}].
map{|p| scan(@b).select(&p).map(&:first)}
(@b + @b.transpose + cross).
any?{|a| a.first and a.all?{|x| a.first == x}}
end
def move
@p.reverse! #change the move of player
i,j = *cur[:player].set(pos_list)
@b[i][j] = cur[:mark] and self
end
def scan(mat,&proc) #iterate a matx with 2-dim index as a 1-dim one
mat.each_with_index.inject([]){|a,rowi|
rowi.first.each_with_index.inject(a){|b,xj|
b << [xj.first, [rowi,xj].map(&:last)]}}.map(&proc)
end
end
class Player
def initialize() @win, @even = 0, 0 end
def set(pos) pos.choice end
def win() @win += 1 end
def even() @even += 1 end
end
end
Game.new(100)
|

syat
#6190()
Rating5/5=1.00
マルバツゲームは3×3の格子に交互に○と×を書き込み、先に縦・横・斜めに記号をそろえたほうが勝ちというおなじみのゲームです。
「毎ターン乱数を使って手を決めるランダムプレイヤー同士を対戦させる」というのが今回のお題です。 1万回対戦させ、勝ち・負け・引き分けの数を表示してください。 そして先手が有利であることを確かめてください。
良い手を思考するプレイヤーについては別のお題にしようと思っています。 プレイヤーを簡単に差し換えることができる設計を目指してください。
[ reply ]