1.途中まで書いたやつ
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.分からなかったところ
ひっくり返すべき石をどのように判定するべきかをコードにすることができなかったと分かっているので,そこをどうしようか考えている。
タテ・ヨコ・ナナメと探索しなければならないのだが,どうもコードがごちゃごちゃとしてしまい,結果どうして動作しないのかがはっきりと分からない。
このコードは一体何をしようとしているんだと言われてももう説明できないくらい,しっちゃかめっちゃかになっているコードだけど一応晒しておく。多分まともに動作しない。
#タテ・ヨコ・ナナメに位置する石の座標を簡単に取得しようと思って作ったと思う
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 さんありがとうございます。完成しました!(多分)
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
石を並べてみる。
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
ちゃんとひっくり返るのか確かめる。
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 って便利。
- 意味のないクラスは使わない。
大変参考になりました。ありがとうございます。