棒取りゲームを作ってみた
を見て、前に考えた棒取りゲームの必勝法をプログラムにしてみようと思い立った。
以下ソース
#!/usr/bin/ruby class BokeshiField def initialize(arr) @state=[] arr.each{|n| @state=@state+[[true]*n] } end def state @state.map{|row| row.dup } end def print_field puts " 01234567890123456789" @state.each_index{|i| print "#{i} " @state[i].each{|x| print x ? "|" : "+" } print "\n" } end def outoffield(arow,acol) if arow<0 || @state.length<=arow puts "Bad row number" true elsif acol<0 || @state[arow].length<=acol puts "Bad column number" true else false end end def delrange(arow,acol1,acol2) if outoffield(arow,acol1) || outoffield(arow,acol2) return false end if acol1>acol2 tmp=acol1 acol1=acol2 acol2=tmp end if @state[arow][acol1..acol2].inject(true){|x,y|x & y} for i in acol1 .. acol2 @state[arow][i]=false end return true else puts "Bad number" return false end end def endgame b=false @state.each{|row| row.each{|x| b=b|x } } !b end end class BokeshiCPU def hand(field) state=bar_position_length(field.state) case count_gt1(state) when 0 return [state[0][0],state[0][1]] when 1 state.each{|x| if x[2]>1 l=state.length%2>0?x[2]-1:x[2] return [x[0],x[1],x[1]+l-1] end } else r=0 state.each{|x| r=r^x[2] } if r==0 return rand_hand(state) else hb=highest_bit(r) state.each{|x| if x[2]&hb != 0 l=x[2]-(x[2]^r) return [x[0],x[1],x[1]+l-1] end } end end end def bar_position_length(state) arr=[] state.each_index{|i| row=state[i] n=0 while n<row.length b=search_from(row,n,true) n=search_from(row,b,false) if n>b arr=arr+[[i,b,n-b]] end end } arr end def search_from(arr,from,val) n=from while n<arr.length && arr[n]!=val n=n+1 end n end def count_gt1(s) c=0 s.each{|x| if x[2]>1 c=c+1 end } c end def highest_bit(n) mask=-1 while mask&n != 0 mask=mask*2 end mask=mask/2 mask & n end def rand_hand(s) n=rand(s.length) m=rand(s[n][2]) [s[n][0],s[n][1]+m] # 1 本だけ消して相手のミスを待つ #l=rand(s[n][2]) #[s[n][0],s[n][1]+m,s[n][1]+l] # 複数の棒を消させたい場合 end end class BokeshiGame def initialize(players=[:human,:cpu],bars=[3,5,7]) @fld=BokeshiField.new(bars) @cpu=BokeshiCPU.new @players=players @playerid=0 puts "init" @fld.print_field end def game_start while(true) if @players[@playerid]==:human puts "your hand" m=gets.split.map{|s|s.to_i} if m.length<2 puts "Bad numbers" next end else puts "cpu's hand" m=@cpu.hand(@fld) end if m.length==2 suc=@fld.delrange(m[0],m[1],m[1]) else suc=@fld.delrange(m[0],m[1],m[2]) end if suc if @fld.endgame show_end break end @playerid=1-@playerid @fld.print_field end end end def show_end if @players[1-@playerid]==:human puts "you win" else puts "cpu win" end end end game=BokeshiGame.new([:human,:cpu],[1,2,3,4,5]) game.game_start