初心者卒業試験のブラックジャック作成に挑戦してみました（Go編）

#はじめに
[「プログラミング入門者からの卒業試験は『ブラックジャック』を開発すべし」](https://qiita.com/hirossyi73/items/cf8648c31898216312e5)という記事を見て、勉強中のGo言語で試してみました。
※本業は今時珍しいコボラーです。

#１　カードの扱い
元の記事、[Haskellで実装した方](https://qiita.com/KNaito/items/385fb1701f70ffb8ffd4)、[Kotlinで実装した方](https://qiita.com/matyahiko2831/items/901554ba7c5680c06bc9)などとは変えています。
・カードは0～51で表す
・13で割った商をカードの種類に割り当て
・13で割った余り＋１を数字に割り当てました。
　※４で割っても同等です。商がカードの数字、余りがカードの種類になるだけです。
こうすることでカード自体を１次元で割り当て可能です。
またネックになりやすい重複がない等の確認がしやすい他、処理も簡単になると考えました。
また0～51（以降カード番号）のカード番号からカードの種類、数字は容易に求めることができます。
なお、商とカード種類は
 ・0：ハート
 ・1：ダイヤ
 ・2：スペード
 ・3：グラブ
に割り当てました（根拠はありません。適当です。）

　※カードの種類、数字を持つ構造体を定義した場合、確かに4種類の種類で13までの数値の中から選ぶことができます。ただし、「重複がなく52枚存在すること」を満たす方法が全く思いつきませんでした。カードの番号であれば扱いやすいし、0～51の連番さえ振ってしまえば自動的に重複がなくなります。

ここまでの実装は以下の通り。

```
/* カードの数字を取得 */
func getNumber(cardNo uint8) uint8 {
	ret := cardNo%13 + 1
	return ret
}

/* カードのマークを取得 */
// 0：ハート
// 1：ダイヤ
// 2:スペード
// 3:グラブ
func getMarkNo(cardNo uint8) uint8 {
	ret := cardNo / 13
	return ret
}

/* カードのマーク（名称）を取得 */
func getMarkName(markNo uint8) string {

	var ret string = ""
	switch markNo {
	case 0:
		ret = "ハート"
	case 1:
		ret = "ダイヤ"
	case 2:
		ret = "スペード"
	case 3:
		ret = "クラブ"
	}
	return ret
}
```

#2 手札の点数確認
エースを1、11どちらでもできるようにしないといけません。
実装はいったん11として加算し、21を超えた場合に最大エースの枚数分まで10を引くループで実現しました。

```
/* 手札から得点を取得 */
func getPoint(tefuda []uint8) uint8 {
	var cntAce uint8 = 0
	var sumP uint8 = 0
	for i := 0; i < len(tefuda); i++ {

		switch {
		case getNumber(tefuda[i]) == 1:
			sumP += 11
			cntAce++
		case getNumber(tefuda[i]) >= 2 && getNumber(tefuda[i]) <= 10:
			sumP += getNumber(tefuda[i])
		case getNumber(tefuda[i]) >= 11:
			sumP += 10
		}
	}

	// エース独自処理。いったん11とカウントしておき、
	// 最大でエースの枚数分10を引けるようにする
	for i := 0; i < int(cntAce); i++ {
		if sumP > 21 {
			sumP = sumP - 10
		}
	}
	return sumP
}
```


#3 山積みカードの作成
やり方をさんざん考えて（カード番号、ランダム数）のスライスを作り
そのあとランダム数でソートしカード番号をランダムに並べました。
ランダム数は適当に10000程度としました。このくらいあればまず重複しないだろうと。
Sortは・・・はまりました。
sort.Sortで実行しようとして何度やってもエラー・・・
結局sort.Sliceという方法があると知り試したらできました。
Sortができたらそれを１次元のスライスに渡して山積みの完成です。

```
// カードをシャッフルさせるために使用
// 独自の構造体
type Card struct {
	idx       uint8
	intRandum int32
}
type allCard []Card


/* カードをシャッフルした山積みを取得 */
func shufleCard() []uint8 {

	wkyama := make([]Card, 52)
	var i uint8
	retyama := make([]uint8, 52)

	// idxには連番を（０～５１）を設定する
	// これをカードと見立てる。
	// intRandumにはランダムの数字を設定する。
	rand.Seed(time.Now().UnixNano())
	for i = 0; i < 52; i++ {
		wkyama[i].idx = i
		wkyama[i].intRandum = rand.Int31n(10000)
	}

	//ソート
	sort.Slice(wkyama, func(i, j int) bool { return wkyama[i].intRandum < wkyama[j].intRandum })
	for j := 0; j < 52; j++ {
		//	fmt.Printf("idx-%d  val-%d\n", j, wkyama[j].idx)
		retyama[j] = wkyama[j].idx
	}
	return retyama
}

```

#4 カードをめくる処理
カードをめくる処理は山積みからめくった場合、スライスを削除するのではなくめくられた箇所のカード番号に99を入れる方法としました。
（例）

|| 前 | 後 |
|:------------------:|:------------------:|:------------------:|
|山積み|(**13**,12,44,・・・)|(**99**,12,44,・・・)|
|手札|(05,40)|(05,40,**13**)|

実際のソースはこちら

```
/* 山積みからカードを１枚取得し手札に加える */
func getCard(tefuda []uint8, yama []uint8) ([]uint8, []uint8) {
	// 引いたあとは99としておく。
	for i := range yama {
		if yama[i] == 99 {
			continue
		} else {
			tefuda = append(tefuda, yama[i])
			yama[i] = 99
			break
		}
	}
	return tefuda, yama
}
```

#5 その他
全体のソースは[こちら](https://github.com/soshi8/BlackJack/blob/master/BlackJack.go)

#6 感想
ソートでかなり時間がかかりました。
非に少しづつだったにせよ全体で20時間くらいかかったかもしれません。
最低でも1/3はソートです。

カードの扱いは多分他の人のやり方より楽だと思っています。

#7 参考
・[「プログラミング入門者からの卒業試験は『ブラックジャック』を開発すべし」](https://qiita.com/hirossyi73/items/cf8648c31898216312e5)
・[初心者卒業試験のブラックジャック作成に挑戦してみました（Haskell編）](https://qiita.com/KNaito/items/385fb1701f70ffb8ffd4)
・[Kotlinでブラックジャック作ってみた](https://qiita.com/matyahiko2831/items/901554ba7c5680c06bc9)
・[ランダム数の取得](https://takeshiyako.blogspot.com/2015/10/go-lang-rand.html)
・[スライスソート](https://qiita.com/Sekky0905/items/2d5ccd6d076106e9d21c)