Rubyの初心者向け問題4-☆1:マルバツゲーム(Tic-Tac-Toe)を作ろう
この問題を解くために必要な知識:
【レベル1~3の知識】
コメントの書き方、変数と定数、基本データ型、四則演算と演算子、入力と出力、文字列操作、分岐処理(if、if~else、elsif、case)、繰り返し処理(for、while、until、each)、範囲(Range)、例外処理(begin-rescue-end)、配列(Array)、ハッシュ(Hash)、コレクション操作(配列、ハッシュの操作)、Enumerableモジュール、メソッドの定義と呼び出し、メソッドの戻り値、クラスの定義と使用、コンストラクタ、クラスの継承、モジュールの定義と使用、ミックスイン
【レベル4の知識】
アクセスメソッド(ゲッター、セッター)、カプセル化、クラスメソッドとインスタンスメソッド、共通メソッド、ブロックとイテレータ、プロック(Proc)、ラムダ(Lambda)、ファイル操作(読み書き)、正規表現、メタプログラミングの基礎
<<前の問題 | 問題集Top |
次の問題>> |
Ruby練習問題4-☆1:マルバツゲーム(Tic-Tac-Toe)を作ろう
マルバツゲーム(〇×ゲーム、Tic-Tac-Toe)を作成しましょう。
このゲームは2人で交互に「〇」か「×」を3×3のグリッドに置き、先に一列(横、縦、斜め)を揃えた方が勝ちとなります。
ゲームは次の要件に従って実装してください。
この問題の要件
以下の要件に従ったプログラムを作成してください。
- 3×3のグリッドを表す配列を使用すること。
- プレイヤーは「〇」と「×」を交互に置くこと。
- 勝利条件を判定するメソッドを作成すること。
- 勝者が決まるか、全てのマスが埋まったらゲームを終了すること。
- ゲームの進行を表示するためのメソッドを作成すること
ただし、以下のような実行結果となること。
----- ↓出力される結果の例↓ -----
| | ---+---+--- | | ---+---+--- | | 〇のターンです。行(0, 1, 2)と列(0, 1, 2)を入力してください。 0 0 〇 | | ---+---+--- | | ---+---+--- | | ×のターンです。行(0, 1, 2)と列(0, 1, 2)を入力してください。 1 1 〇 | | ---+---+--- | × | ---+---+--- | | 〇のターンです。行(0, 1, 2)と列(0, 1, 2)を入力してください。 0 1 〇 | 〇 | ---+---+--- | × | ---+---+--- | | ×のターンです。行(0, 1, 2)と列(0, 1, 2)を入力してください。 1 0 〇 | 〇 | ---+---+--- × | × | ---+---+--- | | 〇のターンです。行(0, 1, 2)と列(0, 1, 2)を入力してください。 0 2 〇 | 〇 | 〇 ---+---+--- × | × | ---+---+--- | | 〇の勝ちです!
----- ↑出力される結果の例↑ -----
この問題を解くヒント
1からコードを組み立てることが難しい場合は、以下のヒントを開いて参考にしましょう。
正解のコードは上から順に以下のような構成となっています。
1.クラス定義
1-1. TicTacToeクラスの定義
2.コンストラクタ
2-1. 初期状態のボードを作成
2-2. 初期プレイヤーを設定
3.メソッド定義
3-1. display_boardメソッド
3-1-1. 現在のボードの状態を表示
3-2. switch_playerメソッド
3-2-1. 現在のプレイヤーを交互に切り替え
3-3. player_moveメソッド
3-3-1. 現在のプレイヤーのターンを表示
3-3-2. 行と列を入力として受け取る
3-3-3. 入力位置が空であるかを確認
3-3-4. ボードに現在のプレイヤーのマークを配置
3-3-5. 入力位置が無効な場合、再度入力を求める
3-4. winner?メソッド
3-4-1. 行、列、斜めの勝利条件をチェック
3-5. draw?メソッド
3-5-1. 全てのマスが埋まっているかを確認
3-6. playメソッド
3-6-1. メインループを実行
3-6-2. ボードを表示
3-6-3. プレイヤーの入力を受け取る
3-6-4. 勝利条件を判定
3-6-5. 引き分け条件を判定
3-6-6. プレイヤーを切り替え
4.ゲームの実行
4-1. TicTacToeクラスのインスタンスを作成
4-2. ゲームを開始
以下のコードをコピーし、コメントに従ってコードを完成させて下さい。
# マルバツゲームクラス class TicTacToe # コンストラクタ:初期状態のボードとプレイヤーを設定 def initialize # ***ここでボードを初期化し、初期プレイヤーを設定するコードを書いてください*** end # ボードを表示するメソッド def display_board puts " #{@board[0][0]} | #{@board[0][ 1 ]} | #{@board[0][ 2 ]} " puts "---+---+---" puts " #{@board[ 1 ][0]} | #{@board[ 1 ][ 1 ]} | #{@board[ 1 ][ 2 ]} " puts "---+---+---" puts " #{@board[ 2 ][0]} | #{@board[ 2 ][ 1 ]} | #{@board[ 2 ][ 2 ]} " end # プレイヤーのターンを切り替えるメソッド def switch_player # ***ここで現在のプレイヤーを交互に切り替えるコードを書いてください*** end # プレイヤーの入力を受け取るメソッド def player_move puts "#{@current_player}のターンです。行(0, 1, 2)と列(0, 1, 2)を入力してください。" # ***ここで入力を受け取り、その位置にマークを配置するコードを書いてください*** end # 勝利条件を判定するメソッド def winner? # ***ここで行、列、斜めの勝利条件をチェックするコードを書いてください*** end # 全てのマスが埋まったかどうかを判定するメソッド def draw? # ***ここでボードの全てのマスが埋まっているかチェックするコードを書いてください*** end # ゲームを実行するメソッド def play loop do display_board # ボードを表示 # ***ここでプレイヤーの入力を受け取るコードを書いてください*** if winner? display_board puts "#{@current_player}の勝ちです!" break elsif draw? display_board puts "引き分けです!" break else # ***ここでプレイヤーを切り替えるコードを書いてください*** end end end end # ゲームの実行 # ***ここでTicTacToeクラスのインスタンスを作成し、ゲームを開始するコードを書いてください***
このヒントを見てもまだ回答を導き出すのが難しいと感じる場合は、先に正解のコードと解説を見て内容を理解するようにしましょう。
この問題の解答例
例えば以下のようなプログラムが考えられます。
********************
# マルバツゲームクラス class TicTacToe # コンストラクタ:初期状態のボードとプレイヤーを設定 def initialize @board = Array.new(3) { Array.new(3, " ") } # 3x3の空のボードを作成 @current_player = "〇" # 初期プレイヤーは「〇」 end # ボードを表示するメソッド def display_board puts " #{@board[0][0]} | #{@board[0][ 1 ]} | #{@board[0][ 2 ]} " puts "---+---+---" puts " #{@board[ 1 ][0]} | #{@board[ 1 ][ 1 ]} | #{@board[ 1 ][ 2 ]} " puts "---+---+---" puts " #{@board[ 2 ][0]} | #{@board[ 2 ][ 1 ]} | #{@board[ 2 ][ 2 ]} " end # プレイヤーのターンを切り替えるメソッド def switch_player @current_player = @current_player == "〇" ? "×" : "〇" # 「〇」と「×」を交互に切り替え end # プレイヤーの入力を受け取るメソッド def player_move puts "#{@current_player}のターンです。行(0, 1, 2)と列(0, 1, 2)を入力してください。" row, col = gets.split.map(&:to_i) # 入力を行と列に分割し整数に変換 if @board[row][col] == " " @board[row][col] = @current_player # ボードに現在のプレイヤーのマークを配置 else puts "その位置には置けません。別の場所を選んでください。" player_move # 再度入力を求める end end # 勝利条件を判定するメソッド def winner? # 行、列、斜めの勝利条件をチェック (0..2).any? { |i| @board[i].all? { |cell| cell == @current_player } } || # 行のチェック (0..2).any? { |i| @board.all? { |row| row[i] == @current_player } } || # 列のチェック [@board[0][0], @board[ 1 ][ 1 ], @board[ 2 ][ 2 ]].all? { |cell| cell == @current_player } || # 左斜めのチェック [@board[0][ 2 ], @board[ 1 ][ 1 ], @board[ 2 ][0]].all? { |cell| cell == @current_player } # 右斜めのチェック end # 全てのマスが埋まったかどうかを判定するメソッド def draw? @board.flatten.none? { |cell| cell == " " } # ボードの全てのマスが埋まっているかチェック end # ゲームを実行するメソッド def play loop do display_board # ボードを表示 player_move # プレイヤーの入力を受け取る if winner? display_board puts "#{@current_player}の勝ちです!" break elsif draw? display_board puts "引き分けです!" break else switch_player # プレイヤーを切り替え end end end end # ゲームの実行 game = TicTacToe.new # ゲームオブジェクトを作成 game.play # ゲームを開始
********************
正解のコードの解説
initialize
メソッド:ゲームの初期設定
def initialize @board = Array.new(3) { Array.new(3, " ") } # 3x3の空のボードを作成 @current_player = "〇" # 初期プレイヤーは「〇」 end
このメソッドは、ゲーム開始時に呼び出され、ボードと現在のプレイヤーを初期化します。
@board
: 3×3の二次元配列を作成し、全てのセルを空白(” “)で埋めています。これがゲームボードになります。@current_player
: ゲームを開始する最初のプレイヤーは「〇」です。
display_board
メソッド:ボードの表示
def display_board puts " #{@board[ 0][ 0]} | #{@board[ 0][ 1]} | #{@board[ 0][ 2]} " puts "---+---+---" puts " #{@board[ 1][ 0]} | #{@board[ 1][ 1]} | #{@board[ 1][ 2]} " puts "---+---+---" puts " #{@board[ 2][ 0]} | #{@board[ 2][ 1]} | #{@board[ 2][ 2]} " end
このメソッドは、現在のゲームボードを表示します。各行の間には区切り線が表示され、ボードの状態をわかりやすく視覚化しています。
switch_player
メソッド:プレイヤーの切り替え
def switch_player @current_player = @current_player == "〇" ? "×" : "〇" end
このメソッドでは、現在のプレイヤーを切り替えます。@current_player
が「〇」なら「×」に変更し、逆も同様です。
player_move
メソッド:プレイヤーの入力
def player_move puts "#{@current_player}のターンです。行(0, 1, 2)と列(0, 1, 2)を入力してください。" row, col = gets.split.map(&:to_i) if @board[row][col] == " " @board[row][col] = @current_player else puts "その位置には置けません。別の場所を選んでください。" player_move end end
プレイヤーに行(row)と列(col)を入力してもらい、ボード上にその位置が空いているか確認します。
空いていればプレイヤーの記号(〇または×)を配置し、埋まっている場合は再度入力を求めます。
winner?
メソッド:勝利条件のチェック
def winner? (0..2).any? { |i| @board[i].all? { |cell| cell == @current_player } } || (0..2).any? { |i| @board.all? { |row| row[i] == @current_player } } || [@board[ 0][ 0], @board[ 1][ 1], @board[ 2][ 2]].all? { |cell| cell == @current_player } || [@board[ 0][ 2], @board[ 1][ 1], @board[ 2][ 0]].all? { |cell| cell == @current_player } end
このメソッドでは、プレイヤーが勝利条件を満たしたかを判定します。
- 各行が同じプレイヤーの記号で埋まっているか
- 各列が同じプレイヤーの記号で埋まっているか
- 左斜めと右斜めのラインが同じ記号で埋まっているか
いずれかが満たされていれば勝利です。
draw?
メソッド:引き分けの判定
def draw? @board.flatten.none? { |cell| cell == " " } end
ボード上に空いているマスがなく、勝者がいない場合、ゲームは引き分けになります。このメソッドでは、全てのマスが埋まっているかをチェックします。
play
メソッド:ゲームの実行
def play loop do display_board player_move if winner? display_board puts "#{@current_player}の勝ちです!" break elsif draw? display_board puts "引き分けです!" break else switch_player end end end
このメソッドはゲームのメインループです。ボードを表示し、プレイヤーの入力を受け取り、勝者や引き分けのチェックを行います。
勝敗が決まるまでプレイヤーを交互に切り替えて続行します。
<<前の問題 |
問題集Top |
次の問題>> |
この問題への質問・コメント
この問題を作成するにあたりAIを活用しています。
問題ないことは確認していますが、もし間違いや表現の違和感などありましたら、ご指摘頂けると大変助かります。