こんにちは!じゃいごテックのあつしです。
今回はpaiza Bランクレベルアップメニュー から五目並べという問題を解説します。
この問題集は二次元配列に関する5個のSTEP問題(D~C級)とFINAL問題(B級)で構成されていて、STEP問題を解いて行けばFINAL問題も解けるはず!となっています。
STEP問題を解いてみる
STEP問題は基本的なメソッドについての問題です。
簡単な解説は付けていますが、難しいと感じたら下記の記事も参考にしてみて下さい。
※過去に出題された小問題が出てくることがありますが、チケットに余裕があれば復習のつもりで取り組んでみましょう。
STEP1: 文字列の出力 (paizaランク D 相当)
STEP1 は5行の文字列をそのまま出力する問題です。
解答例
<<"EOS" 入力例1 XXOXO OXOXX OOOOO OXOX. XOXXO 出力例1 XXOXO OXOXX OOOOO OXOX. XOXXO 入力例2 XXOXO OXOXX .OXXO OXOO. XXXXX 出力例2 XXOXO OXOXX .OXXO OXOO. XXXXX 入力例3 ...X. ...X. ...X. ...X. OOOO. 出力例3 ...X. ...X. ...X. ...X. OOOO. EOS # [解答例1] N = 5 N.times { puts gets } # [解答例2] N = 5 ary = N.times.map { gets.chomp } puts ary
解答例1 : 5行分の入力をそのまま出力しています。
解答例2 : 5行分の入力を配列ary
に格納してから、先頭から順に出力しています。
STEP2: 五目並べ(1行) (paizaランク D 相当)
STEP2 は5文字で3種類の文字(”O” か " X" か "." の3種類)の文字が与えられ、"O"か"X"が5個ならその文字、それ以外なら"D"を出力する問題です。
解答例
<<"EOS" 入力例1 OOOOO 出力例1 O 入力例2 OXOOO 出力例2 D 入力例3 ...O. 出力例3 D EOS # [解答例1] s = gets.chomp o, x = 0, 0 (0..s.length - 1).each do |idx| if s[idx] == "O" o += 1 elsif s[idx] == "X" x += 1 end end if o == 5 puts "O" elsif x == 5 puts "X" else puts "D" end # [解答例2] s = gets.chomp if s.count("O") == 5 puts "O" elsif s.count("X") == 5 puts "X" else puts "D" end
解答例1 : 文字列を先頭から参照して、"O"と"X"の集計を行い、どちらかが5個ならその文字を、それ以外なら"D"を出力しています。
解答例2 : countメソッド
で"O"が5個か、"X"が5個かを判定して、どちらかが5個ならその文字を、それ以外なら"D"を出力しています。
STEP3: 五目並べ(横) (paizaランク C 相当)
STEP3 は5行5文字3種類("O"か"X"か".")で構成された文字列が与えられ、横の並び(各行)について調べて、"O"か"X"が5個あればその文字を、それ以外なら"D"を出力する問題です。
解答例
INPUT1 = <<~"EOS" XXOXO OXOXX OOOOO OXOX. XOXXO EOS OUTPUT1 = <<~"EOS" O EOS INPUT2 = <<~"EOS" XXOXO OXOXX .OXXO OXOO. XXXXX EOS OUTPUT2 = <<~"EOS" X EOS INPUT3 = <<~"EOS" O.O.X OXX.X O.X.X OO..X X.XOX EOS OUTPUT3 = <<~"EOS" D EOS # [解答例1] N = 5 h_lines = N.times.map { gets.chomp } result = "D" h_lines.each do |line| if line.count("O") == 5 result = "O" break elsif line.count("X") == 5 result = "X" break end end puts result # [解答例2] N = 5 h_lines = N.times.map { gets.chomp.chars } result = "D" h_lines.each do |line| if line.count("O") == 5 result = "O" break elsif line.count("X") == 5 result = "X" break end end puts result # [解答例3] def solve(input_lines) h_lines = input_lines.split("\n").map(&:chars) n = h_lines.length result = "D" h_lines.each do |line| if line.count("O") == n result = "O" break elsif line.count("X") == n result = "X" break end end result end puts solve(STDIN.read) # [参考 解答例3の動作確認用コード] # puts solve(INPUT1) # puts solve(INPUT2) # puts solve(INPUT3)
解答例1: 各行を文字列のまま、countメソッド
で"O"か"X"が5個揃っているかを判定しています。
解答例2: 各行をcharsメソッドで文字の配列にして、二次元配列にしてからcountメソッド
で"O"か"X"が5個揃っているかを判定しています。
※ 後の問題の、縦や斜めのラインを確認するのに都合が良いためです。
解答例3: 解答例2と同じ方法で解いていますが、メソッドを定義しています。
※ 入力データが多くなってくると、打ち込みが大変なのでこの方法が便利です。
STEP4: 五目並べ(縦) (paizaランク C 相当)
STEP4 は5行5文字3種類("O"か"X"か".")で構成された文字列が与えられ、縦の並びについて調べて、"O"か"X"が5個あればその文字を、それ以外なら"D"を出力する問題です。
解答例
INPUT1 = <<~"EOS" XXOXO OXOXX OOOOO OXOX. XOXXO EOS OUTPUT1 = <<~"EOS" D EOS INPUT2 = <<~"EOS" XXOXO OXOXX .OXXO OXOO. XXXXX EOS OUTPUT2 = <<~"EOS" D EOS INPUT3 = <<~"EOS" ...X. ...X. ...X. ...X. OOOO. EOS OUTPUT3 = <<~"EOS" D EOS # [解答例1] N = 5 h_lines = N.times.map { gets.chomp.chars } v_lines = [] (0..N - 1).each do |h| line = [] (0..N - 1).each do |w| line.push(h_lines[w][h]) end v_lines.push(line) end result = "D" v_lines.each do |line| if line.count("O") == 5 result = "O" break elsif line.count("X") == 5 result = "X" break end end puts result # [解答例2] def solve(input_lines) h_lines = input_lines.split("\n").map(&:chars) n = h_lines.length v_lines = [] (0..n - 1).each do |h| line = [] (0..n - 1).each do |w| line.push(h_lines[w][h]) end v_lines.push(line) end result = "D" v_lines.each do |line| if line.count("O") == n result = "O" break elsif line.count("X") == n result = "X" break end end result end puts solve(STDIN.read) # [参考 確認用コード] # puts solve(INPUT1) # puts solve(INPUT2) # puts solve(INPUT3) # [解答例3] def solve(input_lines) h_lines = input_lines.split("\n").map(&:chars) n = h_lines.length v_lines = h_lines.transpose result = "D" v_lines.each do |line| if line.count("O") == n result = "O" break elsif line.count("X") == n result = "X" break end end result end puts solve(STDIN.read) # [参考 確認用コード] # puts solve(INPUT1) # puts solve(INPUT2) # puts solve(INPUT3)
解答例1: eachメソッド
で0から4のインデックスで2重ループを設定し、縦と横の並びを入れ替えた配列を作り、各ラインの文字について"O"か"X"が5個揃っているかを判定しています。
解答例2: 解答例1と同じ方法で解いていますが、メソッドを定義しています。
※ 入力データが多くなってくると、打ち込みが大変なのでこの方法が便利です。
解答例3: transposeメソッド
を使って、縦と横の並びを入れ替えた配列を生成して、各ラインの文字について"O"か"X"が5個揃っているかを判定しています。
STEP5: 五目並べ(斜め) (paizaランク C 相当)
STEP5 は5行5文字3種類("O"か"X"か".")で構成された文字列が与えられ、斜めの並びについて調べて、"O"か"X"が5個あればその文字を、それ以外なら"D"を出力する問題です。
解答例
INPUT1 = <<~"EOS" XXOXO OXOXX OOOOO OXOX. XOXXO EOS OUTPUT1 = <<~"EOS" D EOS INPUT2 = <<~"EOS" XXOXO OXOXX .OXXO OXOO. XXXXX EOS OUTPUT2 = <<~"EOS" D EOS INPUT3 = <<~"EOS" ...X. ...X. ...X. ...X. OOOO. EOS OUTPUT3 = <<~"EOS" D EOS # [解答例] def solve(input_lines) h_lines = input_lines.split("\n").map(&:chars) n = h_lines.length d_lines = [[], []] (0..n - 1).each do |i| d_lines[0].push(h_lines[i][i]) d_lines[1].push(h_lines[n - 1 - i][i]) end result = "D" d_lines.each do |line| if line.count("O") == n result = "O" break elsif line.count("X") == n result = "X" break end end result end puts solve(STDIN.read) # [参考 確認用コード] # puts solve(INPUT1) # puts solve(INPUT2) # puts solve(INPUT3)
解答例: eachメソッド
で0から4のインデックスでループを設定して斜めの配列を二つ作り、各ラインの文字について"O"か"X"が5個揃っているかを判定しています。
- 斜めライン1: ary[0][0], ary[1][1], ary[2][2], ary[3][3], ary[4][4]
- 斜めライン2: ary[4][0], ary[3][1], ary[2][2], ary[1][3], ary[0][4]
五目並べ (paizaランク B 相当)を解いてみる
※ paiza Bランクレベルアップメニューより
問題
5行5列の五目並べの盤面が与えられます。
盤面の各マスには、"O"か"X"か"."が書かれています。
"O"と"X"は、それぞれプレイヤーの記号を表します。
同じ記号が縦か横か斜めに連続で5つ並んでいれば、その記号のプレイヤーが勝者となります。
勝者の記号を1行で表示してください。
勝者がいない場合は、引き分けとして、"D"を表示してください。
入力される値
入力は以下のフォーマットで与えられます。
s_1
s_2
s_3
s_4
s_5
入力値最終行の末尾に改行が1つ入ります。
期待する出力
勝者の記号を1行で表示してください。
勝者がいない場合は、引き分けとして、"D"を表示してください。
条件
すべてのテストケースにおいて、以下の条件をみたします。
- s_iの文字数は5文字
- s_iに含まれる文字は"O"か"X"か"."のいずれか
- 勝者が2人になる盤面が与えられることはありません
- 入力例1
- XXOXO
OXOXX
OOOOO
OXOX.
XOXXO
- 出力例1
- O
- 入力例2
- XXOXO
OXOXX
.OXXO
OXOO.
XXXXX
- 出力例2
- X
- 入力例3
- ...X.
...X.
...X.
...X.
OOOO.
- 出力例3
- D
攻略ポイント
ポイント
- 二次元配列を扱う(縦・斜めにアクセスする)
- 配列の要素を集計する
問題を解く流れ
入出力例をコピペしてヒアドキュメントで変数に代入し、データを確認します。正しく受け取れていれば確認用コードは削除します。
INPUT1 = <<~"EOS" XXOXO OXOXX OOOOO OXOX. XOXXO EOS OUTPUT1 = <<~"EOS" O EOS INPUT2 = <<~"EOS" XXOXO OXOXX .OXXO OXOO. XXXXX EOS OUTPUT2 = <<~"EOS" X EOS INPUT3 = <<~"EOS" ...X. ...X. ...X. ...X. OOOO. EOS OUTPUT3 = <<~"EOS" D EOS p INPUT1 # > "XXOXO\nOXOXX\nOOOOO\nOXOX.\nXOXXO\n" p OUTPUT1 # > "O\n" p INPUT2 # > "XXOXO\nOXOXX\n.OXXO\nOXOO.\nXXXXX\n" p OUTPUT2 # > "X\n" p INPUT3 # > "...X.\n...X.\n...X.\n...X.\nOOOO.\n" p OUTPUT3 # > "D\n"
続いて問題を解くメソッド( solve
メソッドとします)を変数の下に定義します。
まず、入力データを受け取る処理を書きます。
※ データ量が多い場合はppメソッドを使うといい感じに改行を入れて表示してくれます。
def solve(input_lines) # 入力データ受け取り # 横5ライン h_lines = input_lines.split("\n").map(&:chars) # 確認用コード h_lines end # 確認用コード pp solve(INPUT1) # > [["X", "X", "O", "X", "O"], # > ["O", "X", "O", "X", "X"], # > ["O", "O", "O", "O", "O"], # > ["O", "X", "O", "X", "."], # > ["X", "O", "X", "X", "O"]] pp solve(INPUT2) # > [["X", "X", "O", "X", "O"], # > ["O", "X", "O", "X", "X"], # > [".", "O", "X", "X", "O"], # > ["O", "X", "O", "O", "."], # > ["X", "X", "X", "X", "X"]] pp solve(INPUT3) # > [[".", ".", ".", "X", "."], # > [".", ".", ".", "X", "."], # > [".", ".", ".", "X", "."], # > [".", ".", ".", "X", "."], # > ["O", "O", "O", "O", "."]]
データが正しく受け取れていれば、solve
メソッドに横5ライン、縦5ライン、斜め2ラインについて"O"と"X"の数を調べる処理を追加していきます。(STEP3~5をひとつにまとめる)
def solve(input_lines) # 入力データ受け取り # 横5ライン h_lines = input_lines.split("\n").map(&:chars) # 縦5ライン v_lines = h_lines.transpose # 斜め2ライン n = h_lines.length d_lines = [[], []] (0..n - 1).each do |i| d_lines[0].push(h_lines[i][i]) d_lines[1].push(h_lines[n - 1 - i][i]) end # 全ラインをまとめる all_lines = h_lines + v_lines + d_lines # 全ラインで勝者の判定を行う result = "D" all_lines.each do |line| if line.count("O") == n result = "O" elsif line.count("X") == n result = "X" end break if result != "D" end # 確認用コード result end # 確認用コード p solve(INPUT1) # > "O" p solve(INPUT1) == OUTPUT1 # > false p solve(INPUT2) # > "X" p solve(INPUT2) == OUTPUT2 # false p solve(INPUT3) # > "D" p solve(INPUT3) == OUTPUT3 # > false
あとは出力形式を整えれば完成です。
p solve(INPUT1) == OUTPUT1 -> true
を確認するために、判定結果の末尾に改行を加えます。
def solve(input_lines) # 入力データ受け取り # 横5ライン h_lines = input_lines.split("\n").map(&:chars) # 縦5ライン v_lines = h_lines.transpose # 斜め2ライン n = h_lines.length d_lines = [[], []] (0..n - 1).each do |i| d_lines[0].push(h_lines[i][i]) d_lines[1].push(h_lines[n - 1 - i][i]) end # 全ラインをまとめる all_lines = h_lines + v_lines + d_lines # 全ラインで勝者の判定を行う result = "D" all_lines.each do |line| if line.count("O") == n result = "O" elsif line.count("X") == n result = "X" end break if result != "D" end # 判定結果の末尾に改行を追加 result << "\n" end # 確認用コード p solve(INPUT1) # > "O\n" p solve(INPUT1) == OUTPUT1 # > true p solve(INPUT2) # > "X\n" p solve(INPUT2) == OUTPUT2 # > true p solve(INPUT3) # > "D\n" p solve(INPUT3) == OUTPUT3 # > true
入出力例での動作確認が出来ましたので、標準入力からのデータ受け取りに変更して、手動で動作確認をしたら提出します。
複数行のデータ受け取りなので STDIN.read
を使います。(入力終了は Ctrl+D
又は Ctrl+Z
)
解答コード
def solve(input_lines) # 入力データ受け取り # 横5ライン h_lines = input_lines.split("\n").map(&:chars) # 縦5ライン v_lines = h_lines.transpose # 斜め2ライン n = h_lines.length d_lines = [[], []] (0..n - 1).each do |i| d_lines[0].push(h_lines[i][i]) d_lines[1].push(h_lines[n - 1 - i][i]) end # 全ラインをまとめる all_lines = h_lines + v_lines + d_lines # 全ラインで勝者の判定を行う result = "D" all_lines.each do |line| if line.count("O") == n result = "O" elsif line.count("X") == n result = "X" end break if result != "D" end # 判定結果の末尾に改行を追加 result << "\n" end
他の解答例
- 横・縦・斜めラインの配列を生成する処理をメソッドにまとめる
- 勝者を判定する処理をメソッドにまとめる
# 盤面情報を与えると横・縦・斜めの12ラインを返す def all_lines(board) n = board.length # 縦5ライン v_lines = board.transpose # 斜め2ライン d_lines = [[], []] (0..n - 1).each do |i| d_lines[0].push(board[i][i]) d_lines[1].push(board[n - 1 - i][i]) end # 横5ライン・縦5ライン・斜め2ラインを配列にして返す board + v_lines + d_lines end # ラインの情報を与えると勝者を判定する def winner(lines) lines.each do |line| if line.count("O") == 5 return "O" elsif line.count("X") == 5 return "X" end end return "D" end def solve(input_lines) # 入力データ受け取り board = input_lines.split("\n").map(&:chars) # 横5ライン・縦5ライン・斜め2ラインの配列を取得 all_lines = all_lines(board) # 各ラインを調べて勝者を判定する result = winner(all_lines) # 判定結果の末尾に改行を追加 result << "\n" end puts solve(STDIN.read)
今回のまとめ
charsメソッド
で文字列を文字に分割して配列を作りました- 二次元配列を扱いました
- インデックスを使って縦や斜めにアクセスする
- transposeメソッドで行と列を入れ替える(転置)
countメソッド
で配列(文字列)の指定した要素の数をカウントしました- 処理を分割したメソッドを作ってみました
B級問題では二次元配列を扱う問題が沢山出てきます。transposeメソッドも時々使うので覚えておくと便利だと思います!