Skip to content

Instantly share code, notes, and snippets.

@mosaicer
Created December 30, 2017 17:26
Show Gist options
  • Save mosaicer/11ce29ad66d755ec425c6ca057ea7f9b to your computer and use it in GitHub Desktop.
Save mosaicer/11ce29ad66d755ec425c6ca057ea7f9b to your computer and use it in GitHub Desktop.
[2017-11-14] Challenge #340 [Intermediate] Walk in a Minefield
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