Go練習問題3-☆2:マルバツゲームを作ろう

記事内に商品プロモーションを含む場合があります

Go言語の初心者向け問題3-☆2:マルバツゲームを作ろう

<<前の問題 問題集Top 次の問題>>

Go練習問題3-☆2:マルバツゲームを作ろう




「マルバツゲーム(Tic-Tac-Toe)」を作成しましょう。

プレイヤーが交互に「○」と「×」を3×3のマスに置いていき、縦・横・斜めのいずれか一列に3つ揃ったら勝ちです。

プレイヤーとコンピュータが対戦する形式で、コンピュータの選択はランダムに行います。ゲームの進行に従って、ボードの状態を表示し、プレイヤーの勝敗や引き分けを適切に判定します。

この問題の要件

以下の要件に従ってコードを完成させてください。

  • 3×3のゲームボードを表現するために、9つの要素を持つ配列を使用すること。
  • プレイヤーは「○」、コンピュータは「×」を使うこと。
  • プレイヤーは1〜9の番号を入力して、自分のマークを配置すること。
  • ボードの状態を常に表示し、ゲームの進行状況をわかりやすくすること。
  • コンピュータはランダムに空いているマスに「×」を配置すること。
  • 勝利条件(縦・横・斜めで3つ揃ったら勝ち)を判定すること。
  • 勝利判定後、プレイヤーが勝った場合とコンピュータが勝った場合、引き分けの場合それぞれ適切なメッセージを表示すること。

ただし、以下のような実行結果となるコードを書くこと。

*****↓↓正解コードの実行結果の例↓↓*****

マルバツゲームを開始します!
1 | 2 | 3
--+---+--
4 | 5 | 6
--+---+--
7 | 8 | 9
どのマスに置きますか?(1-9で入力してください)> 5
1 | 2 | 3
--+---+--
4 | ○ | 6
--+---+--
7 | 8 | 9
コンピュータが選んだマス: 1
× | 2 | 3
--+---+--
4 | ○ | 6
--+---+--
7 | 8 | 9

この問題を解くヒント

1からコードを組み立てることが難しい場合は、以下のヒントを開いて参考にしましょう。

ヒント1【コードの構成を見る】

正解のコードは上から順に以下のような構成となっています。

1.import文
 1-1. fmtパッケージのインポート
 1-2. math/randパッケージのインポート
 1-3. timeパッケージのインポート

2.printBoard関数の定義
 2-1. 3×3のゲームボードの状態を表示するための関数
  2-1-1. 横の区切り「–+—+–」を表示
  2-1-2. それぞれのボード要素を表示

3.checkWin関数の定義
 3-1. 勝利条件を判定するための関数
  3-1-1. 横のラインの勝利条件を定義
  3-1-2. 縦のラインの勝利条件を定義
  3-1-3. 斜めのラインの勝利条件を定義
 3-2. 勝利条件を満たした場合にtrueを返す

4.computerMove関数の定義
 4-1. コンピュータがランダムに空いているマスを選ぶための関数
  4-1-1. 空いているマスをリストとして格納
  4-1-2. ランダムに選択されたマスに「×」を配置
  4-1-3. コンピュータが選んだマス番号を表示

5.main関数の定義
 5-1. 9要素の配列boardを宣言し、ゲームボードを初期化
 5-2. ゲームの開始メッセージを表示
 5-3. 無限ループでゲームを進行する
  5-3-1. 現在のボードの状態を表示
  5-3-2. プレイヤーに1〜9の番号を入力させ、その位置に「○」を配置
  5-3-3. プレイヤーの勝利条件をチェックし、勝利の場合はゲーム終了
  5-3-4. 空きマスがない場合は引き分けと判定
  5-3-5. コンピュータのターンを実行し、ランダムに「×」を配置
  5-3-6. コンピュータの勝利条件をチェックし、勝利の場合はゲーム終了

ヒント2【穴埋め問題にする】

以下のコードをコピーし、コメントに従ってコードを完成させて下さい。

package main

import (
	"fmt"
	"math/rand"
	"time"
)

// ゲームボードを表示する関数
func printBoard(board [9]string) {
	fmt.Println(board[0], "|", board[ 1], "|", board[ 2])
	fmt.Println("--+---+--")
	fmt.Println(board[ 3], "|", board[ 4], "|", board[5])
	fmt.Println("--+---+--")
	fmt.Println(board[6], "|", board[7], "|", board[8])
}

// 勝利条件をチェックする関数
func checkWin(board [9]string, mark string) bool {
	/*【穴埋め問題1】 
	ここで勝利条件を定義してください(横、縦、斜めのラインを含むパターン)
	*/
	for _, pattern := range winPatterns {
		if board[pattern[0]] == mark && board[pattern[ 1]] == mark && board[pattern[ 2]] == mark {
			return true
		}
	}
	return false
}

// コンピュータがマスを選ぶ関数
func computerMove(board *[9]string) {
	available := []int{}
	for i, v := range board {
		if v != "○" && v != "×" {
			available = append(available, i)
		}
	}
	rand.Seed(time.Now().UnixNano())
	/*【穴埋め問題2】 
	ここでランダムに空いているマスを選び、そのマスに「×」を配置してください
	*/
	fmt.Println("コンピュータが選んだマス:", move+1)
}

// メイン関数
func main() {
	var board = [9]string{"1", "2", "3", "4", "5", "6", "7", "8", "9"}
	var playerMove int

	fmt.Println("マルバツゲームを開始します!")

	for {
		// 現在のボードの状態を表示
		printBoard(board)

		// プレイヤーに入力を促す
		fmt.Print("どのマスに置きますか?(1-9で入力してください)> ")
		fmt.Scan(&playerMove)

		/*【穴埋め問題3】 
		ここで入力チェックを行い、無効な入力の場合は再度入力を促してください
		*/

		// プレイヤーのマークを配置
		board[playerMove-1] = "○"

		// 勝利チェック
		if checkWin(board, "○") {
			printBoard(board)
			fmt.Println("プレイヤーの勝ちです!")
			break
		}

		// 引き分けチェック
		draw := true
		for _, v := range board {
			if v != "○" && v != "×" {
				draw = false
				break
			}
		}
		if draw {
			printBoard(board)
			fmt.Println("引き分けです!")
			break
		}

		// コンピュータのターン
		computerMove(&board)

		// コンピュータの勝利チェック
		if checkWin(board, "×") {
			printBoard(board)
			fmt.Println("コンピュータの勝ちです!")
			break
		}
	}
}

 

このヒントを見てもまだ回答を導き出すのが難しいと感じる場合は、先に正解のコードと解説を見て内容を理解するようにしましょう。

解答例と解説

この問題の一つの正解例とそのコードの解説を以下に示します。

正解コードの例

例えば以下のようなプログラムが考えられます。

package main

import (
	"fmt"
	"math/rand"
	"time"
)

// ゲームボードを表示する関数
func printBoard(board [9]string) {
	fmt.Println(board[0], "|", board[ 1], "|", board[ 2])
	fmt.Println("--+---+--")
	fmt.Println(board[ 3], "|", board[ 4], "|", board[5])
	fmt.Println("--+---+--")
	fmt.Println(board[6], "|", board[7], "|", board[8])
}

// 勝利条件をチェックする関数
func checkWin(board [9]string, mark string) bool {
	winPatterns := [][ 3]int{
		{0, 1, 2}, {3, 4, 5}, {6, 7, 8}, // 横のライン
		{0, 3, 6}, {1, 4, 7}, {2, 5, 8}, // 縦のライン
		{0, 4, 8}, {2, 4, 6},            // 斜めのライン
	}

	for _, pattern := range winPatterns {
		if board[pattern[0]] == mark && board[pattern[ 1]] == mark && board[pattern[ 2]] == mark {
			return true
		}
	}
	return false
}

// コンピュータがマスを選ぶ関数
func computerMove(board *[9]string) {
	available := []int{}
	for i, v := range board {
		if v != "○" && v != "×" {
			available = append(available, i)
		}
	}
	rand.Seed(time.Now().UnixNano())
	move := available[rand.Intn(len(available))]
	board[move] = "×"
	fmt.Println("コンピュータが選んだマス:", move+1)
}

// メイン関数
func main() {
	var board = [9]string{"1", "2", "3", "4", "5", "6", "7", "8", "9"}
	var playerMove int

	fmt.Println("マルバツゲームを開始します!")

	for {
		// 現在のボードの状態を表示
		printBoard(board)

		// プレイヤーに入力を促す
		fmt.Print("どのマスに置きますか?(1-9で入力してください)> ")
		fmt.Scan(&playerMove)

		// 入力チェック
		if playerMove < 1 || playerMove > 9 || board[playerMove-1] == "○" || board[playerMove-1] == "×" {
			fmt.Println("そのマスはすでに埋まっています。別のマスを選んでください。")
			continue
		}

		// プレイヤーのマークを配置
		board[playerMove-1] = "○"

		// 勝利チェック
		if checkWin(board, "○") {
			printBoard(board)
			fmt.Println("プレイヤーの勝ちです!")
			break
		}

		// 引き分けチェック
		draw := true
		for _, v := range board {
			if v != "○" && v != "×" {
				draw = false
				break
			}
		}
		if draw {
			printBoard(board)
			fmt.Println("引き分けです!")
			break
		}

		// コンピュータのターン
		computerMove(&board)

		// コンピュータの勝利チェック
		if checkWin(board, "×") {
			printBoard(board)
			fmt.Println("コンピュータの勝ちです!")
			break
		}
	}
}

正解コードの解説

このGoコードは、プレイヤーとコンピュータが対戦するマルバツゲームを実装したものです。コードをブロックごとに解説していきます。

import文

import (
    "fmt"
    "math/rand"
    "time"
)
  • fmt パッケージは、入力や出力を扱うために使います。
  • math/rand パッケージはランダムな数値を生成するために使用します。
  • time パッケージは、乱数を時間に基づいて生成するために使用します。

ゲームボードを表示する関数

func printBoard(board [9]string) {
    fmt.Println(board[0], "|", board[ 1], "|", board[ 2])
    fmt.Println("--+---+--")
    fmt.Println(board[ 3], "|", board[ 4], "|", board[5])
    fmt.Println("--+---+--")
    fmt.Println(board[6], "|", board[7], "|", board[8])
}

この関数は、現在のゲームボードを表示するためのもので、3×3のボードの各セルにマーク(○、×)や番号を表示します。

勝利条件をチェックする関数

func checkWin(board [9]string, mark string) bool {
    winPatterns := [][]int{
        {0, 1, 2}, {3, 4, 5}, {6, 7, 8}, // 横のライン
        {0, 3, 6}, {1, 4, 7}, {2, 5, 8}, // 縦のライン
        {0, 4, 8}, {2, 4, 6},            // 斜めのライン
    }
    for _, pattern := range winPatterns {
        if board[pattern[0]] == mark && board[pattern[ 1]] == mark && board[pattern[ 2]] == mark {
            return true
        }
    }
    return false
}

checkWin 関数は、指定されたマーク(プレイヤーは○、コンピュータは×)が縦、横、斜めのいずれかで揃っているかを確認します。

揃っていれば true を返し、そうでなければ false を返します。

コンピュータがマスを選ぶ関数

func computerMove(board *[9]string) {
    available := []int{}
    for i, v := range board {
        if v != "○" && v != "×" {
            available = append(available, i)
        }
    }
    rand.Seed(time.Now().UnixNano())
    move := available[rand.Intn(len(available))]
    board[move] = "×"
    fmt.Println("コンピュータが選んだマス:", move+1)
}

この関数は、空いているマスからコンピュータがランダムに選択し、「×」を置く処理を行います。

メイン関数

func main() {
    var board = [9]string{"1", "2", "3", "4", "5", "6", "7", "8", "9"}
    var playerMove int

    fmt.Println("マルバツゲームを開始します!")

    for {
        // 現在のボードの状態を表示
        printBoard(board)

        // プレイヤーに入力を促す
        fmt.Print("どのマスに置きますか?(1-9で入力してください)> ")
        fmt.Scan(&playerMove)

この部分は、プレイヤーが1〜9の番号でマスを選択するよう促します。fmt.Scan でプレイヤーの入力を受け取ります。

入力チェックと勝利判定

        if playerMove < 1 || playerMove > 9 || board[playerMove-1] == "○" || board[playerMove-1] == "×" {
            fmt.Println("そのマスはすでに埋まっています。別のマスを選んでください。")
            continue
        }

        // プレイヤーのマークを配置
        board[playerMove-1] = "○"

        // 勝利チェック
        if checkWin(board, "○") {
            printBoard(board)
            fmt.Println("プレイヤーの勝ちです!")
            break
        }

この部分は、プレイヤーが入力したマスが正しいかを確認し、すでに埋まっている場合は再入力を促します。

入力が正しい場合、プレイヤーのマーク「○」を配置し、勝利を確認します。

引き分けの判定

        draw := true
        for _, v := range board {
            if v != "○" && v != "×" {
                draw = false
                break
            }
        }
        if draw {
            printBoard(board)
            fmt.Println("引き分けです!")
            break
        }

すべてのマスが埋まっているかを確認し、埋まっていれば引き分けとします。

コンピュータのターンと勝利判定

        computerMove(&board)

        if checkWin(board, "×") {
            printBoard(board)
            fmt.Println("コンピュータの勝ちです!")
            break
        }
    }
}

最後に、コンピュータのターンでランダムに「×」を置き、コンピュータが勝利したかを確認します。

<<前の問題 問題集Top 次の問題>>

この問題への質問・コメント

この問題を作成するにあたりAIを活用しています。

問題ないことは確認していますが、もし間違いや表現の違和感などありましたら、ご指摘頂けると大変助かります。






    Go練習問題集へ戻る
    トップページへ戻る