0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめてのアドベントカレンダーAdvent Calendar 2023

Day 22

Go言語でのブラックジャックの作り方

Posted at

Go言語でのブラックジャックの作り方

作成手順は

  1. 全体構造の設計、タスク出し
  2. 必要な構造体、関数の作成
  3. 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("ブラックジャックを終了します。")
		}
	}
}

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?