LoginSignup
2

More than 1 year has passed since last update.

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

Last updated at Posted at 2019-06-24

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

#1 カードの扱い
元の記事、Haskellで実装した方Kotlinで実装した方などとは変えています。
・カードは0~51で表す
・13で割った商をカードの種類に割り当て
・13で割った余り+1を数字に割り当てました。
 ※4で割っても同等です。商がカードの数字、余りがカードの種類になるだけです。
こうすることでカード自体を1次元で割り当て可能です。
またネックになりやすい重複がない等の確認がしやすい他、処理も簡単になると考えました。
また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ができたらそれを1次元のスライスに渡して山積みの完成です。

// カードをシャッフルさせるために使用
// 独自の構造体
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には連番を(0~51)を設定する
	// これをカードと見立てる。
	// 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)

実際のソースはこちら

/* 山積みからカードを1枚取得し手札に加える */
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 その他
全体のソースはこちら

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

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

#7 参考
「プログラミング入門者からの卒業試験は『ブラックジャック』を開発すべし」
初心者卒業試験のブラックジャック作成に挑戦してみました(Haskell編)
Kotlinでブラックジャック作ってみた
ランダム数の取得
スライスソート

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
2