「プログラミング入門者からの卒業試験は『ブラックジャック』を開発すべし」という気合の入った記事が投稿されていたので実際にやってみた。
元記事ではC#を使うことを想定していたようだが、普段使っているGoを選択した。
ソースコード
ぱっとやってみた感じそれっぽく動いています。
元記事内縦長のかわいい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()})
}
元記事ではいろいろと議論が起こっているようだが、プレイヤーは手札を持ってプレイだけしていればいいと思う。
まとめ
結構楽しかった。工夫のしどころが結構多かったです。
ご意見お待ちしています。