LoginSignup
1
0

More than 3 years have passed since last update.

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

Posted at

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

開発環境

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

Scalaと私

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

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

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

ソース

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

ソース全文

Main.scala

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


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

BlackJackGame.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というものがある。


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のシャッフルとけっこうハマりポイントがあった。


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もどきを作った。


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修飾子はいらない。


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が非推奨だった。何使えばいいのかわからなかったのでそのまま。


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


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で挑戦しようかな。

1
0
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
1
0