Go言語でのブラックジャックの作り方
作成手順は
- 全体構造の設計、タスク出し
- 必要な構造体、関数の作成
- main関数の作成
である。今回はGoの文法などの基礎知識は前提として話を進める。
全体構造の設計、タスク出し
今回作るブラックジャックのルールやゲームの内容を決め、必要なタスクを書き出す。
ルール、ゲーム内容
- AからKまでの13枚の数字が書かれたカードが4色ある、52枚のカードを使用する。
- 実行開始時、ディーラーとプレイヤー全員に2枚ずつカードが配られる。
- プレイヤーは自分のカードの合計値が21に近づくよう、カードを追加するか、追加しないかを決める。
- カードの合計値が21を超えてしまった時点で、その場で負けが確定する。
- ディーラーはカードの合計値が17を超えるまでカードを追加する。
- 双方が21を超えなかった場合は手の大きさを比較し21に近い方を勝利とする。
- 各カードの点数は以下のように決定する。
- 2から9までは、書かれている数の通りの点数
- 10,J,Q,Kは10点
- Aは1点あるいは11点として、手の点数が最大となる方で数える
ゲームのルールと内容を決めたので、次はタスク出しを行う。
タスク出しは上記を実装するために必要な作業を洗い出し、次にやることを明確にするために行うものである。
必要なタスク
- トランプのデッキを作る(1はA、11はJ、12はQ、13はK)
- デッキをシャッフルする
- デッキからカードを引く
- プレイヤーとディーラーの構造体を作る
- 得点を記録する(Aを1か11のどちらで扱うかも決める)
- 21より大きいかどうかの確認をする
main関数内
- プレイヤーとディーラーがカードを引く
- プレイヤーはカードをさらに引くか引かないか選べる
- 21超えたらその場で負け
- ディーラーは17未満の時カードを引き続ける。
- 21を超えたら負け
- 双方21を超えてないなら値を比べ、21に近い方を勝利とする
必要な構造体、関数の作成
ではこれから必要な構造体や関数を作っていく。
まず、トランプのデッキの作成をする。これには、スート(絵柄)とバリュー(数字)を持つ構造体を作りそこに絵柄や数字を付け加えていく。この時数字はAやKを含むので、全て文字列として扱い、得点を計算する時に数字に直す。
構造体
// struct of Card
type Card struct {
Suit string
Value string
}
// struct of Deck
type Deck struct {
Cards []Card
}
デッキを作る関数
// making Deck
func NewDeck() *Deck {
deck := &Deck{}
suits := []string{"ハート", "スペード", "クラブ", "ダイヤ"}
values := []string{"A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"}
for _, suit := range suits {
for _, value := range values {
deck.Cards = append(deck.Cards, Card{Suit: suit, Value: value})
}
}
return deck
}
次にデッキをシャッフルする。これは現在の時間を読み込んでそれに対応してカードの位置を変える、というものを採用する。
シャッフル
// shuffle
func (deck *Deck) Shuffle() {
src := rand.NewSource(time.Now().UnixNano())
rng := rand.New(src)
rng.Shuffle(len(deck.Cards), func(i, j int) {
deck.Cards[i], deck.Cards[j] = deck.Cards[j], deck.Cards[i]
})
}
デッキからカードを引くときは1番最初のカードを引き、残りはデッキとして保存し直す。
一枚引く
// draw 1 card
func (deck *Deck) Draw() Card {
card := deck.Cards[0]
deck.Cards = deck.Cards[1:]
return card
}
プレイヤーとディラーに共有する構造体を作る。このとき保存する値は、引いたカード、現在の得点、Aを何枚引いたか、とする。Aの枚数を数えるのは、以下にある得点計算の時に、1か11のどちらで数えるかに使用するためである。
ゲームのプレイヤーの構造体とそれらを追加するときの関数(ディーラーも等しい)
// struct of player
type Player struct {
Cards []Card
Score int
CountA int
}
// making new player
func NewPlayer() Player {
return Player{CountA: 0}
}
得点を合算する。このとき、J、Q、Kは10とし、Aは1か11のどちらかを自動で設定するようにする。
得点計算
// add card to player
func (player *Player) AddCard(card Card) {
player.Cards = append(player.Cards, card)
//strconv
tmp := 0
switch card.Value {
case "A":
tmp = 11
player.CountA++
case "J", "Q", "K":
tmp = 10
default:
tmp, _ = strconv.Atoi(card.Value)
}
player.Score += tmp
//adjust A
if player.Score > 21 && player.CountA > 0 {
player.Score -= 10
player.CountA--
}
}
21を超えてないかどうかのチェックをする関数
21より小さいかのチェック
// check under 21
func checkOver21(player *Player) bool {
if player.Score > 21 {
return true
}
return false
}
main関数を作る
最後にmain関数内を作っていく。
- まず初めにデッキを作りそれらを引く。
- プレイヤーとディーラーがカードを引く
- プレイヤーはカードをさらに引くか引かないか選べる
- 21超えたらその場で負け
- ディーラーは17未満の時カードを引き続ける。
- 21を超えたら負け
- 双方21を超えてないなら値を比べ、21に近い方を勝利とする
main
func main() {
//making deck
deck := NewDeck()
deck.Shuffle()
//making player
player := Player{}
dealer := Player{}
//distribute a card
player.AddCard(deck.Draw())
dealer.AddCard(deck.Draw())
player.AddCard(deck.Draw())
dealer.AddCard(deck.Draw())
fmt.Println("ブラックジャックを開始します。")
fmt.Printf("あなたの引いたカードは%sの%sです。\n", player.Cards[0].Suit, player.Cards[0].Value)
fmt.Printf("あなたの引いたカードは%sの%sです。\n", player.Cards[1].Suit, player.Cards[1].Value)
fmt.Printf("ディーラーの引いたカードは%sの%sです。\n", dealer.Cards[0].Suit, dealer.Cards[0].Value)
fmt.Println("ディーラーの引いた2枚目のカードはわかりません。")
//player draw
i := 2
for {
fmt.Printf("あなたの現在の得点は%dです.カードを引きますか?(Y/N)\n", player.Score)
which := ""
fmt.Scan(&which)
if which == "N" {
break
} else if which == "Y" {
player.AddCard(deck.Draw())
fmt.Printf("あなたの引いたカードは%sの%sです。\n", player.Cards[i].Suit, player.Cards[i].Value)
i++
}
}
if checkOver21(&player) {
fmt.Println("21を超えました\nあなたの負けです。")
} else {
fmt.Printf("ディーラーの引いた2枚目のカードは%sの%sでした。\n", dealer.Cards[1].Suit, dealer.Cards[1].Value)
//dealer draw
i = 2
for dealer.Score < 17 {
fmt.Printf("ディーラーの現在の得点は%dです\n", dealer.Score)
dealer.AddCard(deck.Draw())
fmt.Println("17より小さいのでカードを引きます。")
fmt.Printf("ディーラーの引いたカードは%sの%sでした。\n", dealer.Cards[i].Suit, dealer.Cards[i].Value)
i++
}
if checkOver21(&dealer) {
fmt.Println("21を超えました。\nあなたの勝ちです。")
} else {
//confirm the values
fmt.Printf("あなたの得点は%dです。\n", player.Score)
fmt.Printf("ディーラーの得点は%dです\n", dealer.Score)
if (21 - dealer.Score) > (21 - player.Score) {
fmt.Println("あなたの勝ちです!")
} else if (21 - dealer.Score) == (21 - player.Score) {
fmt.Println("引き分けです.")
} else {
fmt.Println("ディーラーの勝ちです.")
}
fmt.Println("ブラックジャックを終了します。")
}
}
}
これで全ての工程が終了した。
最後に
コードの内容自体はそこまで難しくないので、タスク出しをちゃんと行うことがとても大切である。
以下に全文を貼り付けて置く。
BlackJack
BlackJackpackage main
import (
"fmt"
"math/rand"
"strconv"
"time"
)
// struct of Card
type Card struct {
Suit string
Value string
}
// struct of Deck
type Deck struct {
Cards []Card
}
// making Deck
func NewDeck() *Deck {
deck := &Deck{}
suits := []string{"ハート", "スペード", "クラブ", "ダイヤ"}
values := []string{"A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"}
for _, suit := range suits {
for _, value := range values {
deck.Cards = append(deck.Cards, Card{Suit: suit, Value: value})
}
}
return deck
}
// shuffle
func (deck *Deck) Shuffle() {
src := rand.NewSource(time.Now().UnixNano())
rng := rand.New(src)
rng.Shuffle(len(deck.Cards), func(i, j int) {
deck.Cards[i], deck.Cards[j] = deck.Cards[j], deck.Cards[i]
})
}
// draw 1 card
func (deck *Deck) Draw() Card {
card := deck.Cards[0]
deck.Cards = deck.Cards[1:]
return card
}
// struct of player
type Player struct {
Cards []Card
Score int
CountA int
}
// making new player
func NewPlayer() Player {
return Player{CountA: 0}
}
// add card to player
func (player *Player) AddCard(card Card) {
player.Cards = append(player.Cards, card)
//strconv
tmp := 0
switch card.Value {
case "A":
tmp = 11
player.CountA++
case "J", "Q", "K":
tmp = 10
default:
tmp, _ = strconv.Atoi(card.Value)
}
player.Score += tmp
//adjust A
if player.Score > 21 && player.CountA > 0 {
player.Score -= 10
player.CountA--
}
}
// check under 21
func checkOver21(player *Player) bool {
if player.Score > 21 {
return true
}
return false
}
func main() {
//making deck
deck := NewDeck()
deck.Shuffle()
//making player
player := Player{}
dealer := Player{}
//distribute a card
player.AddCard(deck.Draw())
dealer.AddCard(deck.Draw())
player.AddCard(deck.Draw())
dealer.AddCard(deck.Draw())
fmt.Println("ブラックジャックを開始します。")
fmt.Printf("あなたの引いたカードは%sの%sです。\n", player.Cards[0].Suit, player.Cards[0].Value)
fmt.Printf("あなたの引いたカードは%sの%sです。\n", player.Cards[1].Suit, player.Cards[1].Value)
fmt.Printf("ディーラーの引いたカードは%sの%sです。\n", dealer.Cards[0].Suit, dealer.Cards[0].Value)
fmt.Println("ディーラーの引いた2枚目のカードはわかりません。")
//player draw
i := 2
for {
fmt.Printf("あなたの現在の得点は%dです.カードを引きますか?(Y/N)\n", player.Score)
which := ""
fmt.Scan(&which)
if which == "N" {
break
} else if which == "Y" {
player.AddCard(deck.Draw())
fmt.Printf("あなたの引いたカードは%sの%sです。\n", player.Cards[i].Suit, player.Cards[i].Value)
i++
}
}
if checkOver21(&player) {
fmt.Println("21を超えました\nあなたの負けです。")
} else {
fmt.Printf("ディーラーの引いた2枚目のカードは%sの%sでした。\n", dealer.Cards[1].Suit, dealer.Cards[1].Value)
//dealer draw
i = 2
for dealer.Score < 17 {
fmt.Printf("ディーラーの現在の得点は%dです\n", dealer.Score)
dealer.AddCard(deck.Draw())
fmt.Println("17より小さいのでカードを引きます。")
fmt.Printf("ディーラーの引いたカードは%sの%sでした。\n", dealer.Cards[i].Suit, dealer.Cards[i].Value)
i++
}
if checkOver21(&dealer) {
fmt.Println("21を超えました。\nあなたの勝ちです。")
} else {
//confirm the values
fmt.Printf("あなたの得点は%dです。\n", player.Score)
fmt.Printf("ディーラーの得点は%dです\n", dealer.Score)
if (21 - dealer.Score) > (21 - player.Score) {
fmt.Println("あなたの勝ちです!")
} else if (21 - dealer.Score) == (21 - player.Score) {
fmt.Println("引き分けです.")
} else {
fmt.Println("ディーラーの勝ちです.")
}
fmt.Println("ブラックジャックを終了します。")
}
}
}