Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

はじめに

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

1 カードの扱い

元の記事、Haskellで実装した方Kotlinで実装した方などとは変えています。
・カードは0~51で表す
・13で割った商をカードの種類に割り当て
・13で割った余り+1を数字に割り当てました。
こうすることでカード自体を1次元で割り当て可能です。
またネックになりやすい重複がない等の確認がしやすい他、処理も簡単になると考えました。
また0~51(以降カード番号)のカード番号からカードの種類、数字は容易に求めることができます。
なお、商とカード種類は
・0:ハート
・1:ダイヤ
・2:スペード
・3:グラブ
に割り当てました(根拠はありません。適当です。)
ここまでの実装は以下の通り。

/* カードの数字を取得 */
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でブラックジャック作ってみた
ランダム数の取得
スライスソート

soshi8
現コボラーだけどブロックチェーンとかAIなんかに手を出すような無謀な行為に挑戦中。 いくつかの株主総会にも出没、システム関連の質問をしていたりしています。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away