LoginSignup
2
2

More than 5 years have passed since last update.

ブラックジャックをGoで実装してみた。

Posted at

プログラミング入門者からの卒業試験は『ブラックジャック』を開発すべし」という気合の入った記事が投稿されていたので実際にやってみた。

元記事ではC#を使うことを想定していたようだが、普段使っているGoを選択した。

ソースコード

github.com/aimof/blackjack

ぱっとやってみた感じそれっぽく動いています。

元記事内縦長のかわいいAA以降は読んでいません(より正確に言うならルールすらちゃんと読んでなかった)。
2〜3時間のやっつけ仕事でテストも最低限なので、ちょっと見せびらかすには物足りない…。

ポイント

指摘されていたポイントを詰めていきます。

カードの管理

0~51の整数で管理しました。

type card struct {
    id int
}

func (c card) suit() string {
    switch c.id % 4 {
    case 0:
        return "C"
    case 1:
        return "D"
    case 2:
        return "H"
    default:
        return "S"
    }
}

// Aの場合1を返す
func (c card) strength() int {
    if c.id >= 40 {
        return 10
    } else {
        return c.id/4 + 1
    }
}

func (c card) number() string {
    switch c.id / 4 {
    case 0:
        return "A"
    case 9:
        return "T"
    case 10:
        return "J"
    case 11:
        return "Q"
    case 12:
        return "K"
    default:
        return strconv.Itoa(c.id/4 + 1)
    }
}

func (c card) toString() string {
    var s string
    s += c.suit()
    s += c.number()
}

それぞれ、

  • suitメソッド:スート(CDHS)を返す
  • strengthメソッド:強さ(Aのときは1)を返す
  • numberメソッド:気持ちの悪い名前をしているが、表示用のカード名を返す(ATJQKはアルファベット)
  • toStringメソッド:スートとランクをくっつけた文字列を返す

山札について

type deck struct {
    // if true, the card is drawn.
    cards [52]bool
}

func newDeck() *deck {
    return &deck{}
}

func (d *deck) draw() int {
    var drawnN int
    for {
        n := rand.Intn(52)
        if d.cards[n] {
            continue
        } else {
            d.cards[n] = true
            drawnN = n
            break
        }
    }
    return drawnN
}

こんな感じで配列での実装です。
カードドローの実装は、デッキにないものをひこうとした場合には再抽選という原始的な方法。
ここは実装を変えたい。

ちなみに、デックはグローバル変数にしてある。
この規模ならわかりやすいかと思ったが果たしてどうなのだろうか?

Aは1か11か問題

type cards []card

func (c cards) reveal() (sumExceptAce, countAce int) {
    for _, card := range c {
        if card.strength() == 1 {
            countAce++
        } else {
            sumExceptAce += card.strength()
        }
    }
    return sumExceptAce, countAce
}

func (c cards) strength() int {
    var sum = 0
    sumExceptAce, countAce := c.reveal()
    switch countAce {
    case 0:
        sum = sumExceptAce
    case 1:
        if sumExceptAce <= 10 {
            sum = sumExceptAce + 11
        } else {
            sum = sumExceptAce + 1
        }
    default:
        if sumExceptAce+countAce > 11 {
            sum = sumExceptAce + countAce
        } else {
            sum = sumExceptAce + countAce + 10
        }
    }
    if sum > 21 {
        return 0
    }
    return sum
}

ちょっと複雑になってしまいました。エース以外の和とエースの枚数で計算しています。

playerとdealerの実装について

type dealer struct {
    cards cards
}

func (d *dealer) play() {
    for d.cards.strength() < 17 && d.cards.strength() > 0 {
        d.cards = append(d.cards, card{id: usedDeck.draw()})
    }
}

type player struct {
    cards cards
}

func (p *player) play() {
    p.cards = append(p.cards, card{id: usedDeck.draw()})
}

元記事ではいろいろと議論が起こっているようだが、プレイヤーは手札を持ってプレイだけしていればいいと思う。

まとめ

結構楽しかった。工夫のしどころが結構多かったです。

ご意見お待ちしています。

2
2
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
2
2