【Ruby】レッスン5-☆3:マルバツゲームで学ぶクラスとメソッドの使い方

一つ前のページではモンスターとの戦闘ゲームを作成しました。
今回は マルバツゲーム を作成しましょう。
Lesson1:基礎文法編
Lesson2:制御構造編
Lesson3:メソッド編
Lesson4:コレクション編
Lesson5:オブジェクト指向編
・Lesson5-1:クラスの基本を理解しよう
・Lesson5-2:イニシャライザを理解しよう
・Lesson5-3:アクセスメソッドを理解しよう
・Lesson5-4:クラス変数とクラスメソッドを理解しよう
・Lesson5-5:privateメソッドを理解しよう
・Lesson5-6:正規表現を理解しよう
・Lesson5-7:クラスの継承を理解しよう
・Lesson5-8:ファイル操作を理解しよう
・Lesson5-9:オーバーライドを理解しよう
・Lesson5-10:モジュールを使ってみよう
・Lesson5-11:ミックスインを使ってみよう
・確認問題5-☆1:モンスター捕獲ゲームを作ろう
・確認問題5-☆2:モンスターとの戦闘ゲームを作ろう
・確認問題5-☆3:マルバツゲームを作ろう ◁今回はココ
Rubyのゲームコード一覧はこちら
確認問題|Rubyでマルバツゲームを作りながらクラス設計を練習しよう

3×3のマルバツゲーム(Tic Tac Toe)を作成してください。
このプログラムではゲームボードを表示し、プレイヤーが交互に〇または×を入力します。
勝者が決定するかすべてのマスが埋まるとゲームが終了します。
以下の要件に従ってコードを完成させてください。
- モジュール
BoardDisplay
を作成し、ゲームボードを表示する機能を定義すること。display_board
メソッドを実装し、ボードを「|」で区切り、行の間に「ー+ー+ー」の線を表示すること。
- クラス
TicTacToe
を作成し、ゲーム全体の管理を行うこと。- 初期化メソッド(
initialize
)で以下を設定する:- ボードを3×3の全角数字(1~9)で初期化すること。
- 現在のプレイヤーを「〇」に設定すること。
switch_player
メソッドを作成し、プレイヤーを交代すること。player_move
メソッドを作成し、プレイヤーが選択した位置に〇または×を記入すること。- 入力は全角数字(1~9)で受け取ること。
- 入力された位置が無効またはすでに使用済みの場合、再入力を促すこと。
find_position
メソッドを作成し、入力された全角数字に対応するボード上の位置(行・列)を特定すること。valid_move?
メソッドを作成し、入力された位置が有効かどうかを判定すること。winner?
メソッドを作成し、勝利条件を判定すること。- 行、列、または斜めがすべて同じ記号で埋まった場合に勝利とする。
draw?
メソッドを作成し、すべてのマスが埋まり、勝者がいない場合に引き分けを判定すること。play
メソッドを作成し、ゲームのメインループを実装すること。- ボードを表示し、プレイヤーの入力を受け付け、勝者や引き分けを判定すること。
- ゲームが終了するまでプレイヤーを交互に切り替えること。
- 初期化メソッド(
ただし、以下のような実行結果となること。
1|2|3
ー+ー+ー
4|5|6
ー+ー+ー
7|8|9
〇のターンです。
選択するマスの番号を入力してください(例: 1 ~ 9):
1
〇|2|3
ー+ー+ー
4|5|6
ー+ー+ー
7|8|9
×のターンです。
選択するマスの番号を入力してください(例: 1 ~ 9):
5
〇|2|3
ー+ー+ー
4|×|6
ー+ー+ー
7|8|9
...
【ヒント】自力で解くのが難しい人へ
1からコードを組み立てることが難しい場合は、以下のヒントを開いて参考にしましょう。
- ヒント1【コードの構成を見る】
-
正解のコードは上から順に以下のような構成となっています。
1:BoardDisplayモジュールの定義
□ display_boardメソッドの定義
□ □ ボードをループで走査し、各行を結合して表示
□ □ 行の区切り線を特定の条件で表示
2:TicTacToeクラスの定義
□ BoardDisplayモジュールをミックスイン
□ initializeメソッドの定義
□ □ 3×3のボードを全角数字で初期化
□ □ 初期プレイヤーを「〇」に設定
□ switch_playerメソッドの定義
□ □ プレイヤーを「〇」または「×」に交代
□ player_moveメソッドの定義
□ □ 入力が有効になるまでループ
□ □ □ 現在のプレイヤーを表示
□ □ □ マス番号の入力を求める
□ □ □ find_positionメソッドを呼び出して位置を特定
□ □ □ 入力が有効であれば、ボードを更新してループを終了
□ □ □ 入力が無効ならエラーメッセージを表示
□ find_positionメソッドの定義
□ □ 指定された番号が存在する行と列を返す
□ □ 該当する位置がなければnilを返す
□ valid_move?メソッドの定義
□ □ ボード上で指定された位置が未使用かを確認
□ winner?メソッドの定義
□ □ 勝利条件を満たすラインが存在するか確認
□ draw?メソッドの定義
□ □ ボードがすべて埋まっているかを確認
□ linesメソッドの定義
□ □ 横・縦・斜めのすべてのラインを取得して返す
□ playメソッドの定義
□ □ ゲームループを開始
□ □ □ ボードを表示
□ □ □ player_moveメソッドを呼び出す
□ □ □ 勝者がいる場合、勝利メッセージを表示して終了
□ □ □ 引き分けの場合、引き分けメッセージを表示して終了
□ □ □ 勝敗が決まらない場合、プレイヤーを交代
3:TicTacToeクラスのインスタンスを生成してplayメソッドを呼び出す
- ヒント2【穴埋め問題にする】
-
以下のコードをコピーし、コメントに従ってコードを完成させて下さい。
Ruby(クリックして開く)162621# ボード表示用のモジュール
2module BoardDisplay
3# ボードを画面に表示するメソッド
4def display_board
5=begin
6【穴埋め問題1】
7ここにボードの各行を「|」で区切り、画面に表示するコードを書いてください。
8また、行の間に区切り線を挿入するコードも含めてください。
9=end
10end
11end
12
13# マルバツゲームのメインクラス
14class TicTacToe
15include BoardDisplay # モジュールを取り込む(ミックスイン)
16
17# 初期化メソッド(ゲームの初期状態を設定)
18def initialize
19=begin
20【穴埋め問題2】
21ここに3x3のボードを全角の数字で初期化し、最初のプレイヤーを設定するコードを書いてください。
22=end
23end
24
25# プレイヤーを交代するメソッド
26def switch_player
27=begin
28【穴埋め問題3】
29ここにプレイヤーを交代するコードを書いてください。
30現在のプレイヤーが「〇」の場合は「×」に、それ以外は「〇」に切り替えるロジックを実装してください。
31=end
32end
33
34# プレイヤーのターンを処理するメソッド
35def player_move
36=begin
37【穴埋め問題4】
38ここに現在のプレイヤーにターンを促し、ボードを更新するコードを書いてください。
39入力が無効な場合には、再入力を求めるロジックを含めてください。
40=end
41end
42
43# 勝利条件をチェックするメソッド
44def winner?
45=begin
46【穴埋め問題5】
47ここに勝利条件を満たすラインがあるかを確認するコードを書いてください。
48=end
49end
50
51# ゲームのメインループ
52def play
53=begin
54【穴埋め問題6】
55ここにゲームを進行するメインループを記述してください。
56勝者が決まるか、引き分けになるまでプレイヤーの操作を繰り返すコードを書いてください。
57=end
58end
59end
60
61# ゲームを開始
62TicTacToe.new.play
このヒントを見てもまだ回答を導き出すのが難しいと感じる場合は、先に正解のコードと解説を見て内容を理解するようにしましょう。
この問題の解答例と解説
この問題の正解コードとその解説は以下の通りです。
クリックして開いて確認してください。
- 正解コード
-
Ruby(クリックして開く)11071071
# ボード表示用のモジュール
2module BoardDisplay
3# ボードを画面に表示するメソッド
4def display_board
5@board.each_with_index do |row, i|
6# ボードを「|」で区切り表示(全角「+」を使用)
7puts row.join('|')
8puts "ー+ー+ー" if i < 2 # 行の間に全角の区切り線を挿入
9end
10end
11end
12
13# マルバツゲームのメインクラス
14class TicTacToe
15include BoardDisplay # モジュールを取り込む(ミックスイン)
16
17# 初期化メソッド(ゲームの初期状態を設定)
18def initialize
19# 3x3のボードを全角の数字で初期化
20@board = [
21["1", "2", "3"],
22["4", "5", "6"],
23["7", "8", "9"]
24]
25# 最初のプレイヤーを「〇」に設定
26@current_player = "〇"
27end
28
29# プレイヤーを交代するメソッド
30def switch_player
31@current_player = @current_player == "〇" ? "×" : "〇"
32end
33
34# プレイヤーのターンを処理するメソッド
35def player_move
36valid_move = false
37while !valid_move
38puts "#{@current_player}のターンです。" # 現在のプレイヤーを表示
39puts "選択するマスの番号を入力してください(例: 1 ~ 9):"
40move = gets.chomp # 入力を取得
41row, col = find_position(move) # 入力に対応する行と列を取得
42
43if row && col && valid_move?(row, col)
44@board[row][col] = @current_player # 選択したマスに記号を記入
45valid_move = true # 有効な入力があればループを終了
46else
47puts "その位置には置けません。別の番号を選んでください。"
48end
49end
50end
51
52# マス番号をボード上の行と列に変換するメソッド
53def find_position(move)
54@board.each_with_index do |row, i|
55col = row.index(move) # 行内で指定された番号を探す
56return [i, col] if col # 見つかった場合はその位置を返す
57end
58nil # 見つからない場合は nil を返す
59end
60
61# 入力が有効かどうかを判定するメソッド
62def valid_move?(row, col)
63# 指定された位置が現在のボードで数字のままかを確認
64@board[row][col] =~ /[1-9]/
65end
66
67# 勝利条件をチェックするメソッド
68def winner?
69lines.any? { |line| line.all? { |cell| cell == @current_player } }
70end
71
72# 引き分け(すべてのマスが埋まった場合)をチェックするメソッド
73def draw?
74@board.flatten.none? { |cell| cell =~ /[1-9]/ } # 数字が残っているか確認
75end
76
77# 勝利条件を構成するラインを取得するメソッド
78def lines
79@board + # 行(横方向)
80@board.transpose + # 列(縦方向)
81[[@board[ 0][ 0], @board[ 1][ 1], @board[ 2][ 2]], # 左斜め
82[@board[ 0][ 2], @board[ 1][ 1], @board[ 2][ 0]]] # 右斜め
83end
84
85# ゲームのメインループ
86def play
87game_over = false
88while !game_over
89display_board
90player_move
91if winner?
92display_board
93puts "#{@current_player}の勝ちです!"
94game_over = true
95elsif draw?
96display_board
97puts "引き分けです!"
98game_over = true
99else
100switch_player
101end
102end
103end
104end
105
106# ゲームを開始
107TicTacToe.new.play
- 正解コードの解説
-
コードをブロックごとに分割して解説します。
モジュールの定義
Ruby(クリックして開く)181module BoardDisplay
2def display_board
3@board.each_with_index do |row, i|
4puts row.join('|')
5puts "ー+ー+ー" if i < 2
6end
7end
8end
module
:
Rubyのモジュールを定義しています。モジュールは、関連するメソッドや定数をまとめて定義する仕組みです。
このモジュールはボードを表示するための機能を提供します。display_board
:
現在のボードの状態を画面に表示します。
各セルの間に「|」を挿入し、行の間に「ー+ー+ー」を入れて見やすくしています。
クラスの定義と初期化
Ruby(クリックして開く)111111class TicTacToe
2include BoardDisplay
3
4def initialize
5@board = [
6["1", "2", "3"],
7["4", "5", "6"],
8["7", "8", "9"]
9]
10@current_player = "〇"
11end
class
:マルバツゲーム全体を管理するクラスを定義しています。include BoardDisplay
:先ほど定義したモジュールをクラスに取り込み、display_board
メソッドを利用可能にしています。initialize
:クラスの初期化メソッドで、3×3のボードを全角数字で初期化します。また最初のプレイヤーを「〇」に設定します。
プレイヤーを交代するメソッド
Ruby(クリックして開く)131def switch_player
2@current_player = @current_player == "〇" ? "×" : "〇"
3end
- 現在のプレイヤーを管理するインスタンス変数。
- このメソッドは現在のプレイヤーが「〇」なら「×」に、それ以外なら「〇」に交代します。
プレイヤーのターンを処理するメソッド
Ruby(クリックして開く)116161def player_move
2valid_move = false
3while !valid_move
4puts "#{@current_player}のターンです。"
5puts "選択するマスの番号を入力してください(例: 1 ~ 9):"
6move = gets.chomp
7row, col = find_position(move)
8
9if row && col && valid_move?(row, col)
10@board[row][col] = @current_player
11valid_move = true
12else
13puts "その位置には置けません。別の番号を選んでください。"
14end
15end
16end
gets.chomp
:プレイヤーが選択した番号を入力として受け取ります。find_position
:入力された番号をボード上の行と列に変換します。valid_move?
:指定されたセルがまだ未使用かどうかをチェックします。
行と列を取得するメソッド
Ruby(クリックして開く)171def find_position(move)
2@board.each_with_index do |row, i|
3col = row.index(move)
4return [i, col] if col
5end
6nil
7end
each_with_index
:ボードを1行ずつ確認し、入力に該当するセルがあるかを探します。index
:行内で該当するセルの位置を探します。
勝利条件や引き分けをチェックするメソッド
Ruby(クリックして開く)171def winner?
2lines.any? { |line| line.all? { |cell| cell == @current_player } }
3end
4
5def draw?
6@board.flatten.none? { |cell| cell =~ /[1-9]/ }
7end
winner?
:勝利条件を満たす行や列、斜めがあるかをチェックします。draw?
:ボード上に数字が残っていない場合、引き分けと判定します。
ゲームのメインループ
Ruby(クリックして開く)118181def play
2game_over = false
3while !game_over
4display_board
5player_move
6if winner?
7display_board
8puts "#{@current_player}の勝ちです!"
9game_over = true
10elsif draw?
11display_board
12puts "引き分けです!"
13game_over = true
14else
15switch_player
16end
17end
18end
while
:ゲームが終了するまでループを続けます。game_over
:勝者が決定するか、引き分けになるとループを終了します。
まとめ
このコードではRubyの基礎的な文法(クラス、メソッド、配列、ループなど)を使って、シンプルなマルバツゲームを実現しています。
特にモジュールを活用してコードを分割する点や、条件分岐を駆使してゲームの進行を管理する点が学びどころです。
この記事を通じてRubyの基本文法やオブジェクト指向の基礎を理解し、さらなるプログラミングスキルの向上を目指してください!
Rubyのゲームコード一覧はこちら