『ブラックジャック』を実装した By Scala

せっかくのGWなので、新しいものにチャレンジ！ということで`Scala`を触ってみた。
1年くらい前にJavaで実装したブラックジャックをScalaで書き直した。

* [『ブラックジャック』を実装した。 - Qiita](https://qiita.com/aky100200/items/0854ef299e5fdbb11048)

## 開発環境

* Windows10
* IntelliJ IDEA : 2019.1.2
* Scala : 2.12.7
* SBT : 1.3.10

## Scalaと私

2年目くらい前にこれからは関数型言語だ！Scalaだ！と意気込んでいたことがあったのだが、eclipseで環境が作れずに挫折した経験あり。
環境が作れなかったのでScala経験はなし。

## 環境構築とチュートリアル

以下を参照。Scalaの開発環境ってこんなに簡単に作れるようになったんだな。

* [Intellij で Scala を始める | Scala Documentation](https://docs.scala-lang.org/ja/getting-started/intellij-track/getting-started-with-scala-in-intellij.html)

## ソース
ソースコードはgithubにアップした。
指摘募集中。

* [aky100200/blackjack](https://github.com/aky100200/blackjack/tree/develop/scala)

## ソース全文

### Main.scala

`App`を継承すると`main`メソッド的なことができる。

```scala

object Main extends App {
  val blackJackGame = new BlackJackGame
  blackJackGame.start()
}
```

### BlackJackGame.scala
三項演算子がない。

```scala

class BlackJackGame {
  def start(): Unit = {
    println("★☆★☆★☆★☆★☆★☆　ブラックジャックにようこそ！　★☆★☆★☆★☆★☆★☆\n")
    println("ゲームを開始します。\n")

    val deck: Deck = new Deck
    val user: AbstractPlayer = new User("あなた")
    val dealer: AbstractPlayer = new Dealer("ディーラー")

    user.initCardList(deck)
    dealer.initCardList(deck)

    user.drawCard(deck)
    if (!user.isBust) {
      dealer.drawCard(deck)
    }

    printGameResult(user, dealer)

    println("\nブラックジャック終了！また遊んでね★")
  }

  def printGameResult(player1: AbstractPlayer, player2: AbstractPlayer): Unit = {
    if (player1.calcScore() == player2.calcScore()) {
      println("引き分けです。")
      return
    }
    val winner: AbstractPlayer = if (!player1.isBust && (player2.isBust || player1.calcScore() > player2.calcScore())) {
      player1
    } else {
      player2
    }
    println(winner.name + "の勝ちです！")
  }
}

```

### Card.scala
switchがなく、代わりにmatchというものがある。

```scala

class Card(val suit: Suit, val rank: Int) {

  def toDisplayValue(): String = {
    rank match {
      case 1 => "A"
      case 11 => "J"
      case 12 => "Q"
      case 13 => "K"
      case _ => rank.toString
    }
  }

  def getPoint(): Int = {
    if (rank > 10) 10 else rank
  }

  override def toString: String = {
    suit.getMark() + "の" + toDisplayValue()
  }
}

```

### Deck.scala

Javaで書いたソースがstreamを多用していたので変換するのが大変だった。
streamみたいなライブラリあるのかな。
for式、List、Listのシャッフルとけっこうハマりポイントがあった。

```scala

import scala.util.Random

class Deck {
  var bill = {
    var cards: List[Card] = List()
    for (s <- Suit.values) {
      for (i <- 1 to 13) {
        cards = cards.::(new Card(s, i))
      }
    }
    Random.shuffle(cards)
  }

  def draw(): Card = {
    val c: Card = bill.head
    bill = bill.filterNot(_ == c)
    c
  }
}
```

### Suit.scala
enumがない。
enumもどきを作った。

```scala

sealed abstract class Suit(val mark: String) {
  def getMark(): String = mark

}

object Suit {

  case object SPADE extends Suit("スペード")

  case object HEART extends Suit("ハート")

  case object DIAMOND extends Suit("ダイヤ")

  case object CLUB extends Suit("クラブ")

  def values: List[Suit] = List(SPADE, HEART, DIAMOND, CLUB)
}
```

### AbstractPlayer.scala
抽象メソッドにabstract修飾子はいらない。

```scala

abstract class AbstractPlayer(val name: String) {
  val BUST_POINT: Int = 21
  var cardList: List[Card] = List()
  var isBust: Boolean = false

  private def addCardList(card: Card) = {
    cardList = cardList.::(card)
  }

  def calcScore(): Int = {
    val score: Int = {
      var score: Int = 0
      val filterCardList: List[Card] = cardList.filter(_.getPoint() > 1)
      for (c <- filterCardList) {
        score = score + c.getPoint()
      }
      score
    }
    val aceCardCount: Int = cardList.filter(_.getPoint() == 1).size
    if (aceCardCount == 0) return score
    val borderScore: Int = 11 - aceCardCount
    if (score > borderScore) {
      score + aceCardCount
    } else {
      score + 10 + aceCardCount
    }
  }

  def draw(deck: Deck): Unit = {
    draw(deck, false)
  }

  def draw(deck: Deck, isHidden: Boolean): Unit = {
    val card: Card = deck.draw()
    addCardList(card)
    if (calcScore() > BUST_POINT) isBust = true
    val msg: String = {
      if (isHidden) {
        name + "の引いたカードはわかりません。"
      } else {
        name + "の引いたカードは" + card.toString() + "です。"
      }
    }
  }

  def initCardList(deck: Deck): Unit

  def drawCard(deck: Deck): Unit
}
```

### User.scala
readLineが非推奨だった。何使えばいいのかわからなかったのでそのまま。

```scala

class User(name: String) extends AbstractPlayer(name) {
  override def initCardList(deck: Deck): Unit = {
    draw(deck)
    draw(deck)
  }

  override def drawCard(deck: Deck): Unit = {
    println(name + "の現在の得点は" + calcScore() + "点です。\n")
    var line: String = null
    while (!isBust && "N" != line) {
      println("カードを引きますか？引く場合はYを引かない場合はNを入力してください。")
      line = readLine()
      if ("Y" == line) {
        draw(deck)
        println(name + "の現在の得点は" + calcScore() + "点です。\n")
      } else if ("N" != line) {
        println("Y/N以外が入力されました。")
      }
    }
  }
}
```

### Dealer.scala

```scala

class Dealer(name: String) extends AbstractPlayer(name) {
  override def initCardList(deck: Deck): Unit = {
    draw(deck)
    draw(deck, true)
  }

  override def drawCard(deck: Deck): Unit = {
    println(name + "の現在の得点は" + calcScore() + "点です。\n")
    while (calcScore() < 17) {
      draw(deck)
      println(name + "の現在の得点は" + calcScore() + "点です。\n")
    }
  }
}
```

## まとめ
Javaとけっこう書き方が違った。
動くところまでは持って行けたがもう少し調べてリファクタリングしていきたい。

FizzBuzzなどと違って構文を色々と調べないと書けないので、ほどよい負荷で勉強できた。
他のサーバサイド言語を勉強するときもまた書こうと思う。
次はNode.jsで挑戦しようかな。