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