【Go】スライスを利用してビンゴ(抽選)システムを作成してみる

どもども、新居に設置する家具を爆買いした結果、翌月の引き落としが怖い僕です。


本日は、僕が現在絶賛挑戦中の"エキスパートたちのGo言語“から学んだスライスに関する知識を定着させるため、スライスを利用したビンゴ(抽選)システムを作成してみようと思います。


今回の記事で書籍の内容について触れることはないので、気になる方は購入してみてください!


ちなみに成果物は以下のように動きます。

処理の流れ

今回のビンゴシステムは下記の流れで処理されます。

①抽選される数字の上限値を受け取る
②1から上限値までの数字が格納されたスライスを作成する
③結果を格納する空のスライスを作成しておく
④0から上限値までの範囲でランダムな数字を生成する
⑤生成された数値に該当するインデックスの要素を②で作成したスライスから取り出す(これが抽選された数字となる)
⑥抽選された数字を②で作成したスライスから削除する
⑦抽選された数字を③で作成したスライスへ追加する
⑧最初に指定した上限値まで④~⑦を繰り返す
⑨上限値に到達したら終了する

また、今回は抽選を繰り返す際にこのまま抽選を続けるかを問うステップも実装します。yで続行、nでキャンセル、それ以外はyかnを入力するようアナウンスしもう一度問いかけるといった具合です。

それでは、今回作成した実際のコードの中身をご紹介します。

抽選される数字の上限値から抽選される数字が格納されるスライスを作成する

①~②の処理を実装していきます。今回はターミナル上で起動するので上限値はfmt.Scan()で受け取ることにしました。

func main() {
	var num int
	fmt.Println("###何番までのビンゴを行いますか?###")
	fmt.Scan(&num)
	fmt.Println("-------------------------------")
	bingo := MakeBingo(num)
}

受け取った上限値をもとに1から上限値までの数字が格納された配列を作成します。MakeBingo関数として実装してみたのが以下です。

func MakeBingo(n int) []int {
	bingo := []int{}

	for i := 0; i < n; i++ {
		bingo = append(bingo, i+1)
	}
	return bingo
}

やっていることは非常にシンプルでfor文を使用して受け取った上限値までの数字をインクリメンタルに格納しているだけです。これで抽選対象のスライスが完成しました!

抽選を続行するかを問う部分の実装

次に抽選を実行するか、キャンセルするかを問う部分を実装していきます。まずはこの処理も含めて関数として実装したいのでmain関数に新しく呼び出す関数を追加します。

func main() {
	var num int
	fmt.Println("###何番までのビンゴを行いますか?###")
	fmt.Scan(&num)
	fmt.Println("-------------------------------")
	bingo := MakeBingo(num)
	StartBingo(num, bingo) //追加
}

StartBingo関数として実装していきましょう!こちらも実装はよくみる非常にシンプルなものです。

func StartBingo(n int, bingo []int) {
	var response string

	for i := 1; i <= n; i++ {
		fmt.Printf("###%v回目の抽選を行いますか?(y/n)###\n", i)
		fmt.Scan(&response)
		if response == "y" {
    // 抽選する処理
		} else if response == "n" {
			break
		} else {
			fmt.Println("###yかnを入力してください###")
			i--
			fmt.Println("-------------------------------")
			continue
		}
	}
	fmt.Println("###ビンゴ終了###")
}

先ほど上限を入力した時と同じようにfmt.Scan()で続行するかどうかを聞いていきます。入力された文字列がy/n/その他でそのように処理するかをifで分岐させます。

その他の文字を入力された場合だけ注意が必要です。実際に抽選を行う部分はforで上限値になるまで回していきますが、単にcontinueだけ実行すると抽選は行われていないのに実施回数だけ増えた状態でy/n入力ステップに戻ってしまいます。

iの数値を減らしてcontinueしているのはこの状態を回避するためです。

これで抽選を続行するかを問う部分は実装できました。いよいよ抽選を実施する処理を実装していきましょう。

抽選を実施する処理

それではStartBingo関数に抽選する処理を実装していきます。

まずは③の結果を格納する空スライスの準備と④のランダムな数字を生成する準備を追加しましょう。

func StartBingo(n int, bingo []int) {
	var response string
	rand.Seed(time.Now().UnixNano()) // ランダムな数字を選ぶ部分の準備
	result := []int{} // 結果を格納する空スライス

	for i := 1; i <= n; i++ {
		fmt.Printf("###%v回目の抽選を行いますか?(y/n)###\n", i)
		fmt.Scan(&response)
		if response == "y" {
    // 抽選する処理
		} else if response == "n" {
			break
		} else {
			fmt.Println("###yかnを入力してください###")
			i--
			fmt.Println("-------------------------------")
			continue
		}
	}
	fmt.Println("###ビンゴ終了###")
}

今回はresultという空スライスを用意して結果を格納していくようにします。それでは抽選を行う部分を実装していきます。

func StartBingo(n int, bingo []int) {
	var response string
	rand.Seed(time.Now().UnixNano())
	result := []int{}

	for i := 1; i <= n; i++ {
		fmt.Printf("###%v回目の抽選を行いますか?(y/n)###\n", i)
		fmt.Scan(&response)
		if response == "y" {
			index := rand.Intn(len(bingo))
			choiced := bingo[index]
			fmt.Printf("-> 選ばれたのは<<%v>>です!\n", choiced)
			bingo = append(bingo[:index], bingo[index+1:]...)
			result = append(result, choiced)
			fmt.Println("###現在の結果###")
			fmt.Println(result)
			fmt.Println("-------------------------------")
		} else if response == "n" {
			break
		} else {
			fmt.Println("###yかnを入力してください###")
			i--
			fmt.Println("-------------------------------")
			continue
		}
	}
	fmt.Println("###ビンゴ終了###")
}

こちらも実際の実装は非常にシンプルです。④~⑦の処理をそのまま実装した形となります。特定の要素(抽選された数字)をbingo配列から削除する際にappendを使用したスライスの結合を使用している部分だけ少し実装に戸惑いました。

非常に便利なスライスの使用方法だと思うのでしっかり押さえておきたいですね!

rand.Intnで引数に与えた数値を上限としてランダムな数字を生成してくれるので、現時点でのbingoスライスの長さを引数として渡しています。

これで無事スライスを使用したビンゴシステムの実装が完了しました!最後に今回のコード全貌を載せておきます。(もっと良い方法があるよ!間違ってるよ!とかあれば是非ご指摘頂きたいです!)

package main

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

func MakeBingo(n int) []int {
	bingo := []int{}

	for i := 0; i < n; i++ {
		bingo = append(bingo, i+1)
	}
	return bingo
}

func StartBingo(n int, bingo []int) {
	var response string
	rand.Seed(time.Now().UnixNano())
	result := []int{}

	for i := 1; i <= n; i++ {
		fmt.Printf("###%v回目の抽選を行いますか?(y/n)###\n", i)
		fmt.Scan(&response)
		if response == "y" {
			index := rand.Intn(len(bingo))
			choiced := bingo[index]
			fmt.Printf("-> 選ばれたのは<<%v>>です!\n", choiced)
			bingo = append(bingo[:index], bingo[index+1:]...)
			result = append(result, choiced)
			fmt.Println("###現在の結果###")
			fmt.Println(result)
			fmt.Println("-------------------------------")
		} else if response == "n" {
			break
		} else {
			fmt.Println("###yかnを入力してください###")
			i--
			fmt.Println("-------------------------------")
			continue
		}
	}
	fmt.Println("###ビンゴ終了###")
}

func main() {
	var num int
	fmt.Println("###何番までのビンゴを行いますか?###")
	fmt.Scan(&num)
	fmt.Println("-------------------------------")
	bingo := MakeBingo(num)
	StartBingo(num, bingo)
}

さいごに

今回はスライスを利用したビンゴシステムを作成してみました!スライス…本当に奥が深いですね…


エキスパートたちのGo言語“はまだ途中ですが良書だと思います!是非チェックしてみてください!


最後まで読んでいただき、ありがとうございました!

Golang

Posted by CY