4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

とあるサイトでオセロのプログラムの問題があったんだけど,全然できないからQiita始める

Last updated at Posted at 2016-03-30

1.途中まで書いたやつ

othelo.rb
class Field < Array
	def initialize
		@size = 8
		super(@size) do |y|
			Array.new(@size) do |x|
				if y == 3 and x == 3
					Stone.new(:W)
				elsif y == 3 and x == 4
					Stone.new(:B)
				elsif y == 4 and x == 3
					Stone.new(:B)
				elsif y == 4 and x == 4
					Stone.new(:W)
				end
			end
		end
	end
end

class Stone
	attr_accessor :color
	def initialize( sym, y, x )
		@color = sym
	end
end

class Othelo
	attr_reader :field
	def initialize
		@field = Field.new
	end
	def put_stone(sym, y, x)
		x -= 1
		y -= 1
		@field[y][x] = Stone.new(sym)
                #ここに石をひっくり返すメソッドを書きたい
	end
	def format
		puts "*12345678"
		@field.each_with_index do |row,i|
			print i+1
			row.each do |stone|
				if stone and stone.color == :B
					print "B"
				else 
					print "W"
				end
				print "_"
			end
			print "\n"
		end
	end
end

 という感じで途中まで書いてはみたのだけれど,結局オセロの肝となる石をひっくり返すアルゴリズムをコードに表現することができずに断念。
 いろいろ書いては見たが,全く石がひっくり返らなかったりとか,全然関係ないところの石がひっくり返ったりとか,もうやんなっちゃう。楽勝だろうと思って取り組んだのに全然できないから,凄い凹んだ。

2.1.分からなかったところ

 ひっくり返すべき石をどのように判定するべきかをコードにすることができなかったと分かっているので,そこをどうしようか考えている。
 タテ・ヨコ・ナナメと探索しなければならないのだが,どうもコードがごちゃごちゃとしてしまい,結果どうして動作しないのかがはっきりと分からない。

 このコードは一体何をしようとしているんだと言われてももう説明できないくらい,しっちゃかめっちゃかになっているコードだけど一応晒しておく。多分まともに動作しない。

reverse.rb
        #タテ・ヨコ・ナナメに位置する石の座標を簡単に取得しようと思って作ったと思う
	def generator(y, x, fy, fx)
		ret_y = y
		ret_x = x
		Enumerator.new do |yeel|
			@field.height.times do
				ret_y = fy.call(ret_y) % @field.height		
				ret_x = fx.call(ret_x) % @field.width
				yeel << [ret_y,ret_x]
			end
		end
	end
        #挟んでひっくり返す石の配列を返して欲しい,返してくれない
	def stones_in_line(sym, y, x, fy:->i{i}, fx:->i{i})
		max_y, max_x = 0, 0
		min_y, min_x = 7, 7
		generator(y,x,fy,fx).each do |_y,_x|
			if @field[_y][_x] and @field[_y][_x].color == sym
				max_y = _y if max_y < _y
				max_x = _x if max_x < _x
				min_y = _y if min_y > _y
				min_x = _x if min_x > _x
			end
		end
		arr = []
		generator(y,x,fy,fx).each do |_y,_x|
			if _y.between?(min_y,max_y) and _x.between?(min_x,max_x)
				if @field[_y][_x] and @field[_y][_x].color != sym
					arr.push(@field[_y][_x])
				else
					return
				end
			end
		end
		return arr
	end
        # 石をひっくり返すはず,そうして欲しい
	def reverse(sym, y, x)
		reverse_stones = []
		inc = ->i{i+=1}
		dec = ->i{i-=1}
		reverse_stones.concat(
			stones_in_line(sym, y, x, fy:inc)
		)
		reverse_stones.concat(
			stones_in_line(sym, y, x, fx:inc)
		)
		reverse_stones.concat(
			stones_in_line(sym, y, x, fx:inc, fy:inc)
		)
		reverse_stones.concat(
			stones_in_line(sym, y, x, fx:inc, fy:dec)
		)
		reverse_stones.uniq.each do |stone|
			stone.color = sym
		end	
	end

2.Qiitaを始める

 自分のプログラミングの実力があるのか無いのか,試そうとしたら,オセロすらプログラミングできず,結果が白黒はっきりついてしまったわけでがっかり。意識高そうなブログで気分を更に沈めようとして記事を読んでいると,プログラミングの学習にはアウトプットが欠かせませんとかあったので,始めてみることにした。

 未熟なコードを晒すことを以って戒めとし,技術の向上に努めるために利用する予定。

 基本3日坊主なので,続かないかもしれない。

3.完成した(はず)

 @sclvola さんありがとうございます。完成しました!(多分)

othelo.rb
class OthelloBoard
	BOARD_SIZE = 8
	STONE_CHARS = {none: ".", white: "o", black: "x"}

	def initialize
		@grid = Array.new(BOARD_SIZE) {Array.new(BOARD_SIZE){:none}}
		put_initial_stones
	end

	def put_initial_stones
		i0 = BOARD_SIZE.div(2) - 1
		i1 = i0 + 1
		@grid[i0][i0] = :white
		@grid[i0][i1] = :black
		@grid[i1][i0] = :black
		@grid[i1][i1] = :white
	end

	def to_s
		result = "  "
		result << (1..BOARD_SIZE).to_a.join(" ")
		result << "\n"
		BOARD_SIZE.times do |i|
			result << "#{i+1} "
			result << BOARD_SIZE.times.map { |j|
			   	STONE_CHARS[@grid[i][j]]
			}.join(" ")
			result << "\n"
		end
		result
	end

	def put_stone(players_color, x, y)
		u = x - 1
		v = y - 1
		@grid[u][v] = players_color
		reverse(players_color, u, v)
	end

	def reverse(color, u, v)
		directions = [:nw, :n_, :ne, :e_, :se, :s_, :sw, :w_]	
		directions.each do |dir|
			reversible_positions(color, u, v, dir).each do |_u,_v|
				@grid[_u][_v] = color
			end
		end
	end

	def reversible_positions(players_color, u, v, direction)
		temp = []
		[].tap do |positions|
			generator(u, v, direction).each do |_u,_v|
				if @grid[_u][_v] == :none
					break
				elsif @grid[_u][_v] != players_color
					temp.push([_u, _v])
				elsif @grid[_u][_v] == players_color
					positions.concat(temp)
				end
			end
			positions.compact!
		end
	end

	def generator(u, v, direction)
		fu = ->i{i}
		fv = ->i{i}
		inc = ->i{i+1}
		dec = ->i{i-1}

		case direction
		when :nw; fu, fv = dec, dec
		when :n_; fv = dec
		when :ne; fu, fv = inc, dec
		when :e_; fu = inc
		when :se; fu, fv = inc, inc
		when :s_; fv = inc
		when :sw; fu, fv = dec, inc
		when :w_; fu = dec
		end

		Enumerator.new do |yeel|
			while u.between?(0, BOARD_SIZE-1) and 
				  v.between?(0, BOARD_SIZE-1)

				u = fu.call(u)
				v = fv.call(v)
				yeel << [u,v]
			end
		end
	end
end

 石を並べてみる。

play.rb
board = OthelloBoard.new
puts board
board.put_stone(:black, 6, 5)
puts board
board.put_stone(:white, 4, 6)
puts board
board.put_stone(:black, 3, 7)
puts board
board.put_stone(:white, 4, 7)
puts board
board.put_stone(:black, 3, 3)
puts board
board.put_stone(:white, 6, 4)
puts board
board.put_stone(:black, 5, 7)
puts board
board.put_stone(:white, 2, 2)
puts board
board.put_stone(:black, 5, 6)
puts board
board.put_stone(:white, 6, 8)
puts board
board.put_stone(:black, 1, 1)
puts board

ちゃんとひっくり返るのか確かめる。

result.txt
  1 2 3 4 5 6 7 8
1 . . . . . . . .
2 . . . . . . . .
3 . . . . . . . .
4 . . . o x . . .
5 . . . x o . . .
6 . . . . . . . .
7 . . . . . . . .
8 . . . . . . . .
  1 2 3 4 5 6 7 8
1 . . . . . . . .
2 . . . . . . . .
3 . . . . . . . .
4 . . . o x . . .
5 . . . x x . . .
6 . . . . x . . .
7 . . . . . . . .
8 . . . . . . . .
  1 2 3 4 5 6 7 8
1 . . . . . . . .
2 . . . . . . . .
3 . . . . . . . .
4 . . . o o o . .
5 . . . x x . . .
6 . . . . x . . .
7 . . . . . . . .
8 . . . . . . . .
  1 2 3 4 5 6 7 8
1 . . . . . . . .
2 . . . . . . . .
3 . . . . . . x .
4 . . . o o x . .
5 . . . x x . . .
6 . . . . x . . .
7 . . . . . . . .
8 . . . . . . . .
  1 2 3 4 5 6 7 8
1 . . . . . . . .
2 . . . . . . . .
3 . . . . . . x .
4 . . . o o o o .
5 . . . x x . . .
6 . . . . x . . .
7 . . . . . . . .
8 . . . . . . . .
  1 2 3 4 5 6 7 8
1 . . . . . . . .
2 . . . . . . . .
3 . . x . . . x .
4 . . . x o o o .
5 . . . x x . . .
6 . . . . x . . .
7 . . . . . . . .
8 . . . . . . . .
  1 2 3 4 5 6 7 8
1 . . . . . . . .
2 . . . . . . . .
3 . . x . . . x .
4 . . . x o o o .
5 . . . x o . . .
6 . . . o x . . .
7 . . . . . . . .
8 . . . . . . . .
  1 2 3 4 5 6 7 8
1 . . . . . . . .
2 . . . . . . . .
3 . . x . . . x .
4 . . . x o o x .
5 . . . x o . x .
6 . . . o x . . .
7 . . . . . . . .
8 . . . . . . . .
  1 2 3 4 5 6 7 8
1 . . . . . . . .
2 . o . . . . . .
3 . . o . . . x .
4 . . . o o o x .
5 . . . x o . x .
6 . . . o x . . .
7 . . . . . . . .
8 . . . . . . . .
  1 2 3 4 5 6 7 8
1 . . . . . . . .
2 . o . . . . . .
3 . . o . . . x .
4 . . . o o o x .
5 . . . x x x x .
6 . . . o x . . .
7 . . . . . . . .
8 . . . . . . . .
  1 2 3 4 5 6 7 8
1 . . . . . . . .
2 . o . . . . . .
3 . . o . . . x .
4 . . . o o o x .
5 . . . x x x o .
6 . . . o x . . o
7 . . . . . . . .
8 . . . . . . . .
  1 2 3 4 5 6 7 8
1 x . . . . . . .
2 . x . . . . . .
3 . . x . . . x .
4 . . . x o o x .
5 . . . x x x o .
6 . . . o x . . o
7 . . . . . . . .
8 . . . . . . . .

 できた!

盤外ならもうダメ。裏返せないので空配列を返します。
空きセルだったらもうダメ。裏返せないので空配列を返します。
players_color と同じだったらもうダメ。空配列を返します。
反対の色だったら,座標値を候補に入れます。

 という @sclvola さんの簡潔な指摘で,ひっくり返す石の判定をするアルゴリズムを書くことができました。こんなにシンプルなのにどうして思いつかなかったんだろう。

 その他,学ばせていただいたこと

  • Array を継承する意味を考えるとき,オセロ盤は配列ではないので,継承せずインスタンス変数に配列を入れる。
  • to_s って便利。
  • 意味のないクラスは使わない。

 大変参考になりました。ありがとうございます。

4
4
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?