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

# Go言語でのブラックジャックの作り方
作成手順は

1. 全体構造の設計、タスク出し
2. 必要な構造体、関数の作成
3. main関数の作成

である。今回はGoの文法などの基礎知識は前提として話を進める。

# 全体構造の設計、タスク出し
今回作るブラックジャックのルールやゲームの内容を決め、必要なタスクを書き出す。

**ルール、ゲーム内容**
* AからKまでの１３枚の数字が書かれたカードが4色ある、52枚のカードを使用する。
* 実行開始時、ディーラーとプレイヤー全員に２枚ずつカードが配られる。
* プレイヤーは自分のカードの合計値が21に近づくよう、カードを追加するか、追加しないかを決める。
* カードの合計値が21を超えてしまった時点で、その場で負けが確定する。
* ディーラーはカードの合計値が17を超えるまでカードを追加する。
* 双方が２１を超えなかった場合は手の大きさを比較し２１に近い方を勝利とする。
<br>

* 各カードの点数は以下のように決定する。
    * 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を含むので、全て文字列として扱い、得点を計算する時に数字に直す。
```Go:構造体
// struct of Card
type Card struct {
	Suit  string
	Value string
}

// struct of Deck
type Deck struct {
	Cards []Card
}
```
```Go:デッキを作る関数
// 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
}
```
<br>
次にデッキをシャッフルする。これは現在の時間を読み込んでそれに対応してカードの位置を変える、というものを採用する。

```Go:シャッフル
// 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]
	})
}
```
<br>
デッキからカードを引くときは1番最初のカードを引き、残りはデッキとして保存し直す。

```Go:一枚引く
// draw 1 card
func (deck *Deck) Draw() Card {
	card := deck.Cards[0]
	deck.Cards = deck.Cards[1:]
	return card
}
```
<br>
プレイヤーとディラーに共有する構造体を作る。このとき保存する値は、引いたカード、現在の得点、Aを何枚引いたか、とする。Aの枚数を数えるのは、以下にある得点計算の時に、1か11のどちらで数えるかに使用するためである。

```Go:ゲームのプレイヤーの構造体とそれらを追加するときの関数(ディーラーも等しい)
// struct of player
type Player struct {
	Cards  []Card
	Score  int
	CountA int
}

// making new player
func NewPlayer() Player {
	return Player{CountA: 0}
}
```

<br>
得点を合算する。このとき、J、Q、Kは10とし、Aは1か11のどちらかを自動で設定するようにする。

```Go:得点計算
// 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--
	}
}
```
<br>
21を超えてないかどうかのチェックをする関数

```Go:２１より小さいかのチェック
// check under 21
func checkOver21(player *Player) bool {
	if player.Score > 21 {
		return true
	}
	return false
}

```


# main関数を作る
最後にmain関数内を作っていく。
* まず初めにデッキを作りそれらを引く。　
* プレイヤーとディーラーがカードを引く
* プレイヤーはカードをさらに引くか引かないか選べる
* 21超えたらその場で負け
* ディーラーは17未満の時カードを引き続ける。
* 21を超えたら負け
* 双方21を超えてないなら値を比べ、21に近い方を勝利とする
```Go: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("ディーラーの引いた２枚目のカードはわかりません。")

	//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("ディーラーの引いた２枚目のカードは%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("ブラックジャックを終了します。")
		}
	}
}
```
これで全ての工程が終了した。
# 最後に
コードの内容自体はそこまで難しくないので、タスク出しをちゃんと行うことがとても大切である。
以下に全文を貼り付けて置く。
```Go: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("ディーラーの引いた２枚目のカードはわかりません。")

	//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("ディーラーの引いた２枚目のカードは%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("ブラックジャックを終了します。")
		}
	}
}

```