Created
December 30, 2017 17:26
-
-
Save mosaicer/11ce29ad66d755ec425c6ca057ea7f9b to your computer and use it in GitHub Desktop.
[2017-11-14] Challenge #340 [Intermediate] Walk in a Minefield
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import java.io.BufferedReader | |
| import java.io.InputStreamReader | |
| fun main(args: Array<String>) { | |
| // 地雷原を作成 | |
| val minefield = makeMinefield() | |
| // 地雷原をコンソールに出力 | |
| println(minefield.getField()) | |
| println() | |
| // ロボットを作成 | |
| val robot = Robot() | |
| // ロボットの位置を地雷原から算出して初期化 | |
| robot.init(minefield.findRobotCoordinateByRobotChar(Robot.ROBOT)) | |
| // 一連のコマンドを取得 | |
| val bf = BufferedReader(InputStreamReader(System.`in`)) | |
| val commands = bf.readLine() | |
| // 実行済みのコマンドを格納しておく変数 | |
| var executedCommands = "" | |
| // 各コマンドでループを回す | |
| commands.map { it -> Command(it) }.forEach { | |
| // コンソール出力をクリア | |
| clearConsole() | |
| // ロボットにコマンドを適用 | |
| it.apply(robot) | |
| // 実行済みコマンドを更新 | |
| executedCommands += it.type | |
| // コマンド適用後のロボットの座標を取得 | |
| val coordinate = robot.getCoordinate() | |
| // ロボットの座標が地雷の座標と同じ、つまり、ロボットが地雷を踏んだ場合 | |
| if (minefield.isMineCoordinate(coordinate)) { | |
| // 最新のロボットの座標で地雷原を更新する | |
| minefield.update(coordinate, Robot.ROBOT) | |
| // 地雷原・ロボットを破壊した地雷の座標・一連のコマンドをコンソールに出力 | |
| outputResult(minefield, commands, "The coordinate of the mine that destroyed the robot is (${coordinate.first + 1}, ${coordinate.second + 1}).") | |
| // 処理を中断する | |
| return | |
| } | |
| // ロボットの座標が地雷原上で有効ではない場合 | |
| if (!minefield.isValidCoordinate(coordinate)) { | |
| // ロボットに適用したコマンドを取り消す | |
| it.redo(robot) | |
| } | |
| // 最新のロボットの座標で地雷原を更新する | |
| minefield.update(robot.getCoordinate(), Robot.ROBOT) | |
| // 地雷原・実行済みコマンドをコンソールに出力 | |
| outputResult(minefield, executedCommands) | |
| // 段階的に出力させるため、処理を1秒待たせる | |
| Thread.sleep(1000) | |
| } | |
| // コンソール出力をクリア | |
| clearConsole() | |
| // 地雷原・ロボットが地雷原を抜けられたかどうか・一連のコマンドをコンソールに出力 | |
| outputResult(minefield, commands, if (!robot.working && robot.getCoordinate() == minefield.exitCoordinate) "Success!" else "Failed.") | |
| } | |
| fun makeMinefield(): Minefield { | |
| val bf = BufferedReader(InputStreamReader(System.`in`)) | |
| val builder = Minefield.Builder(Robot.ROBOT) | |
| while (true) { | |
| val line = bf.readLine() | |
| if (builder.append(line).ended) { | |
| return builder.build() | |
| } | |
| } | |
| } | |
| fun clearConsole() { | |
| print("\r") | |
| } | |
| fun outputResult(minefield: Minefield, commands: String) { | |
| println(minefield.getField()) | |
| println() | |
| print(commands) | |
| } | |
| fun outputResult(minefield: Minefield, commands: String, message: String) { | |
| println(minefield.getField()) | |
| println(message) | |
| println() | |
| print(commands) | |
| } | |
| class Minefield(private var field: String, val exitCoordinate: Pair<Int, Int>) { | |
| companion object { | |
| const val WALL = '+' | |
| const val MINE = '*' | |
| fun isWallLine(line: String): Boolean = """\$WALL+""".toRegex().matches(line) | |
| } | |
| fun getField() = field | |
| fun isValidCoordinate(coordinate: Pair<Int, Int>): Boolean { | |
| val cell = findCellByCoordinate(coordinate) | |
| return if (cell == null) false else cell != WALL | |
| } | |
| fun isMineCoordinate(coordinate: Pair<Int, Int>): Boolean { | |
| val cell = findCellByCoordinate(coordinate) | |
| return if (cell == null) false else cell == MINE | |
| } | |
| fun update(coordinate: Pair<Int, Int>, robotChar: Char) { | |
| val builder = Builder(robotChar) | |
| field.split("\n").forEachIndexed { lineNum, line -> | |
| if (lineNum == coordinate.second) { | |
| builder.append(line.replaceRange(coordinate.first, coordinate.first + 1, robotChar.toString())) | |
| } else { | |
| builder.append(line) | |
| } | |
| } | |
| field = builder.build().getField() | |
| } | |
| fun findRobotCoordinateByRobotChar(robotChar: Char): Pair<Int, Int> { | |
| val coordinate = findCoordinateByCell(robotChar) | |
| if (coordinate == null) { | |
| throw IllegalStateException("This minefield does not have the specified '$robotChar'.") | |
| } else { | |
| return coordinate | |
| } | |
| } | |
| private fun findCellByCoordinate(coordinate: Pair<Int, Int>): Char? { | |
| field.split("\n").forEachIndexed { lineNum, line -> | |
| if (lineNum == coordinate.second && coordinate.first > -1 && line.length > coordinate.first) { | |
| return line[coordinate.first] | |
| } | |
| } | |
| return null | |
| } | |
| private fun findCoordinateByCell(cell: Char): Pair<Int, Int>? { | |
| field.split("\n").forEachIndexed { lineNum, line -> | |
| if (line.contains(cell)) { | |
| return Pair(line.indexOf(cell), lineNum) | |
| } | |
| } | |
| return null | |
| } | |
| class Builder(private val robotChar: Char) { | |
| private val sb = StringBuilder() | |
| private var height = 0 | |
| private lateinit var exitCoordinate: Pair<Int, Int> | |
| private val started: Boolean | |
| get() = !sb.isEmpty() | |
| var ended: Boolean = false | |
| private set(value) { field = value} | |
| fun append(line: String): Builder { | |
| ended = started && isWallLine(line) | |
| return if (ended) { | |
| appendLine(line) | |
| } else { | |
| appendLine(line, true) | |
| } | |
| } | |
| fun build(): Minefield = Minefield(sb.toString(), exitCoordinate) | |
| private fun appendLine(line: String, needNewLine: Boolean = false): Builder { | |
| if (needNewLine) { | |
| sb.appendln(line) | |
| } else { | |
| sb.append(line) | |
| } | |
| height++ | |
| if (line.last() == '0' || line.last() == robotChar) { | |
| exitCoordinate = Pair(line.length - 1, height - 1) | |
| } | |
| return this | |
| } | |
| } | |
| } | |
| class Robot { | |
| companion object { | |
| const val ROBOT = 'M' | |
| } | |
| var working = false | |
| private var positionX = 0 | |
| private var positionY = 0 | |
| fun init(coordinate: Pair<Int, Int>) { | |
| positionX = coordinate.first | |
| positionY = coordinate.second | |
| } | |
| fun getCoordinate(): Pair<Int, Int> { | |
| return Pair(positionX, positionY) | |
| } | |
| fun moveUp() { | |
| if (working) { | |
| positionY-- | |
| } | |
| } | |
| fun moveDown() { | |
| if (working) { | |
| positionY++ | |
| } | |
| } | |
| fun moveLeft() { | |
| if (working) { | |
| positionX-- | |
| } | |
| } | |
| fun moveRight() { | |
| if (working) { | |
| positionX++ | |
| } | |
| } | |
| } | |
| class Command(val type: Char) { | |
| companion object { | |
| const val UP = 'N' | |
| const val DOWN = 'S' | |
| const val RIGHT = 'E' | |
| const val LEFT = 'O' | |
| const val START = 'I' | |
| const val STOP = '-' | |
| } | |
| fun apply(robot: Robot) { | |
| when (type) { | |
| UP -> robot.moveUp() | |
| DOWN -> robot.moveDown() | |
| RIGHT -> robot.moveRight() | |
| LEFT -> robot.moveLeft() | |
| START -> robot.working = true | |
| STOP -> robot.working = false | |
| } | |
| } | |
| fun redo(robot: Robot) { | |
| when (type) { | |
| UP -> robot.moveDown() | |
| DOWN -> robot.moveUp() | |
| RIGHT -> robot.moveLeft() | |
| LEFT -> robot.moveRight() | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment