Josh Skeen, David Greenhalgh著　『Kotlinプログラミング』　章末問題に挑戦(第１５章)

　[Josh Skeen, David Greenhalgh著『Kotlinプログラミング』](https://www.shoeisha.co.jp/book/detail/9784798160191)には章末問題がある。残念なことに解答がないので、[第１３章](https://qiita.com/guijiu/items//5eda8682b1c05fa48c7d)に引き続き本を読み進めながら答案を記述する。
　もし回答に間違いや意見があれば、ぜひご指摘いただきたい。

#第１５章　章末問題 回答案

##15.9　チャレンジ！　quitコマンド
　ゲームを終了するためのquitコマンド、exitコマンドを追加するために、本書p260ページまでのコードの次のように変更した。

まず、ゲームを続行するためのフラグisContinueをGameオブジェクト宣言に追加した。

```kotlin:Game.kt#main()
object Game {
    private val player = Player("Madrigal")
    private var currentRoom: Room = TownSquare()
    private var isContinue = true   // ＜＝＝ゲームを継続するためのフラグを追加

// 以下省略
}
```

while()の括弧内をtrueからisContinueに変更する。isContinueがfalseになるとループを抜けることができ、ゲームが終了する。

```kotlin:Game.kt#play()
    fun play() {
        while (isContinue) {    // ＜＝＝ケームを継続するためのループ条件を true から isContinue に変更
                                // isContinue が falseの時ループから抜ける
            // Play NyetHack
            println(currentRoom.description())
            println(currentRoom.load())

            // Player Status
            printPlayerStatus(player)

            print("> Enter your command: ")
            println(GameInput(readLine()).processCommand())    // ここでreadLine()にexitまたはquitが入力されると
                                                               // GameInput()でisContinueがfalseに変わる
        }
    }

```

　GameInput()クラスの中の fun processCommand()のwhen()のブランチに"quit", "exit"をついかした。
　このブランチを選択したときに冒険者へのメッセージ"さようなら！"を返すとともに、ゲーム継続のフラグisContinue を falseに変更する。このメソッドを終了したときに、play()の中のwhileループに戻るが、ループ条件がfalseになるため、ゲームは終了する。 

```kotlin:Game.kt#GameInput()
    private class GameInput(arg: String?) {
        private val input = arg ?: ""
        val command = input.split(" ")[0]
        val argument = input.split(" ").getOrElse(1, { "" })

        fun processCommand() = when (command.toLowerCase()) {
            "move" -> move(argument)
            "quit", "exit" -> {
                isContinue = false  // ゲームを終了するためにisContinueフラグをfalseに変更
                "さようなら！"        // 冒険者への別れのメッセージ
            }
            else -> commandNotFound()
        }

        private fun commandNotFound() =
            "あなたの意図かよくわかりません!"
    }
}

```
##15.10　チャレンジ！　ワールドマップを実装する
ワールドマップを実装するために、まずmapコマンドをGameInputクラスのprocessCommand()に実装する。ゲーム上でプレイヤーがmapと入力すると、map()関数が呼ばれる。

```kotlin:Game.kt#GameInputクラス
    private class GameInput(arg: String?) {
        private val input = arg ?: ""
        val command = input.split(" ")[0]
        val argument = input.split(" ").getOrElse(1, { "" })

        fun processCommand() = when (command.toLowerCase()) {
            "move" -> move(argument)
            "quit", "exit" -> {
                isContinue = false  // ゲームを終了するためにisContinueフラグをfalseに変更
                "さようなら！"        // 冒険者への別れのメッセージ
            }
            "map" -> map()     // ＜＝＝ mapコマンドを実装するために追加
            else -> commandNotFound()
        }

        private fun commandNotFound() =
            "あなたの意図かよくわかりません!"
    }
```

　次にmap()関数を作る。worldMapをひとつひとつ調べ、プレイヤーがいる場所currentRoomかどうかを調べる。for(worldMapY in worldMap)でworldMapのリストArrayからまずY成分を取り出し、次にfor(locationXY in worldMapY)でX成分を取り出す。

```kotlin:Game.kt#map()関数
object Game {

    // 前部分省略

    fun play() {
        // 省略
    }

    private fun map(): String{
        var locationMap = ""    // プレイヤーが部屋にいるかいないかを表す地図

        for(worldMapY in worldMap) {
            for(locationXY in worldMapY){
                if(locationXY == currentRoom){
                    locationMap += "X"      // プレイヤーがいる時
                } else locationMap += "O"   // プレイヤーがいない時
            }
            locationMap += "\n"
        }
        return locationMap
    }

    private fun move(directionInput: String): String {
        // 省略
    }

    // 以下省略

```

　この結果次のように表示される。

```
> Enter your command: map
XOO
OO

Room: Town Square
```
## 15.11　チャレンジ！　鐘を鳴らす
　「鐘を鳴らすring」を実装するために、まずringコマンドをGameInputクラスのprocessCommand()に実装する。ゲーム上でプレイヤーがmapと入力すると、map()関数が呼ばれる。

```Game.kt#GameInputクラス
    private class GameInput(arg: String?) {
        private val input = arg ?: ""
        val command = input.split(" ")[0]
        val argument = input.split(" ").getOrElse(1, { "" })

        fun processCommand() = when (command.toLowerCase()) {
            "move" -> move(argument)
            "quit", "exit" -> {
                isContinue = false  // ゲームを終了するためにisContinueフラグをfalseに変更
                "さようなら！"        // 冒険者への別れのメッセージ
            }
            "map" -> map()
            "ring" -> ringBell()
            else -> commandNotFound()
        }

        private fun commandNotFound() =
            "あなたの意図かよくわかりません!"
    }

```

　次にring()関数を作る。現在位置が町の広場（Town Squere）ならばベルを鳴らすようにし、町の広場でなければその旨を告げました。
　ここで、ひとつ疑問なのですが、本書のヒントにringBell関数をpublicにするように書いてありますが、その理由がわかりません。本当はGame.kt以外の別のクラスから呼び出さなければいけないのでしょうか？

```kotlin:Game.kt#ringBell()
    private fun ringBell(): String {
//        if(currentRoom.name == Room("Town Square").name)
        if (currentRoom == worldMap[0][0])
            return "Ringing Bell!"
        else return "This is not Town Square."
    }
```





[第２章チャレンジ！答案](https://qiita.com/guijiu/items/dd3313f2a73401e29bc4)
[第３章チャレンジ！答案](https://qiita.com/guijiu/items/672b8daf014d4e6bb346)
[第４章チャレンジ！答案](https://qiita.com/guijiu/items/08dfa397b989b7ecdb3a)
[第７章チャレンジ！答案](https://qiita.com/guijiu/items/452cfa51bee6c4a4af9d)
[第８章チャレンジ！答案](https://qiita.com/guijiu/items/9a7871b1e41bdabd85e7)
[第１０章チャレンジ！答案](https://qiita.com/guijiu/items/9619abd4d5bf3488e58f)
[第１３章チャレンジ！答案](https://qiita.com/guijiu/items/5eda8682b1c05fa48c7d)
[第１５章チャレンジ！答案](https://qiita.com/guijiu/items/0f67138483e12641dfc3)
[第１８章チャレンジ！答案](https://qiita.com/guijiu/items/18e904913a429aaa58ea)