Камень-Ножницы-Бумага-Ящерица-Спок в Котлине

Я сделал простое приложение Kotlin под названием Rock-Paper-Scissor-Lizard-Spock. Приложение принимает вводимые пользователем данные и проверяет, принадлежит ли он заданному набору массива. Если он принадлежит, он сравнивает ввод пользователя и случайное значение из массива для генерации результата. В противном случае он печатает ошибку и снова запрашивает ввод пользователя. Мой код работает нормально. Можно ли упростить мой код?

    fun getUserChoice(userChoice: Array<String>): String {

    var isValidChoice = false
    var userValue = ""
    while (!isValidChoice) {
        // Ask the user for their choice
        println("Please enter one of the following:")

        for ((index, item) in userChoice.withIndex()) {
            println("${index + 1} . $item")
        }
        val userInput = readLine().toString()
        println("You have chosen $userInput")

        // validate the user input
        if (userInput in userChoice
        ) {
            isValidChoice = true
            userValue = userInput
        } else (println(ERROR_MESSAGE))

        // If the choice is invalid inform the user
        if (!isValidChoice) {
            ERROR_MESSAGE


        }


    }
    return userValue

}


fun getGameChoice(randomChoice: String) = randomChoice

fun printResult(gamePrint: String, userPrint: String) {

    println("I have chosen $gamePrint")
    if (gamePrint == userPrint) {
        println("It is a draw")
    } else if (gamePrint == "Rock" && userPrint == "Paper") {
        println("$userPrint covers $gamePrint. $userPrint wins!!")
    } else if (gamePrint == "Lizard" && userPrint == "Paper") {
        println("$gamePrint eats $userPrint. $gamePrint wins!!")
    } else if (gamePrint == "Scissor" && userPrint == "Paper") {
        println("$gamePrint cuts $userPrint. $gamePrint wins!!")
    } else if (gamePrint == "Spock" && userPrint == "Paper") {
        println("$userPrint disproves $gamePrint. $userPrint wins!!")
    } else if (gamePrint == "Rock" && userPrint == "Scissor") {
        println("$userPrint crushes $gamePrint. $userPrint wins!!")
    } else if (gamePrint == "Rock" && userPrint == "Lizard") {
        println("$userPrint crushes $gamePrint. $userPrint wins!!")
    } else if (gamePrint == "Rock" && userPrint == "Spock") {
        println("$gamePrint vaporizes $userPrint. $gamePrint wins!!")
    } else if (gamePrint == "Paper" && userPrint == "Scissor") {
        println("$gamePrint cuts $userPrint. $gamePrint wins!!")
    } else if (gamePrint == "Spock" && userPrint == "Scissor") {
        println("$gamePrint smashes $userPrint. $gamePrint wins!!")
    } else if (gamePrint == "Lizard" && userPrint == "Scissor") {
        println("$userPrint cuts $gamePrint. $userPrint wins!!")
    } else if (gamePrint == "Paper" && userPrint == "Lizard") {
        println("$userPrint eats $gamePrint. $userPrint wins!!")
    } else if (gamePrint == "Spock" && userPrint == "Lizard") {
        println("$userPrint poisons $gamePrint. $userPrint wins!!")
    } else if (gamePrint == "Paper" && userPrint == "Spock") {
        println("$gamePrint disproves $userPrint. $gamePrint wins!!")
    } else if (gamePrint == "Scissor" && userPrint == "Lizard") {
        println("$gamePrint decapitates $userPrint. $gamePrint wins!!")
    } else if (gamePrint == "Scissor" && userPrint == "Spock") {
        println("$userPrint smashes $gamePrint. $userPrint wins!!")
    } else if (gamePrint == "Lizard" && userPrint == "Spock") {
        println("$gamePrint poisons $userPrint. $gamePrint wins!!")
    } else if (gamePrint == "Spock" && userPrint == "Rock") {
        println("$gamePrint vaporizes $userPrint. $gamePrint wins!!")
    } else if (gamePrint == "Paper" && userPrint == "Rock") {
        println("$gamePrint covers $userPrint. $gamePrint wins!!")
    } else if (gamePrint == "Lizard" && userPrint == "Rock") {
        println("$userPrint crushes $gamePrint. $userPrint wins!!")
    } else if (gamePrint == "Scissor" && userPrint == "Rock") {
        println("$userPrint crushes $gamePrint. $userPrint wins!!")
    } else (ERROR_MESSAGE)


}

const val ERROR_MESSAGE = "You must enter a valid choice"

fun main() {
    val choices = arrayOf("Rock", "Paper", "Scissor", "Lizard", "Spock")
    val gameChoice = getGameChoice(choices.random())
    val userChoice = getUserChoice(choices)
    printResult(gameChoice, userChoice)

}
```

2 ответа
2

Каждый раз, когда вы видите повторяющийся код, такой как очень длинный оператор if / else, вы, вероятно, сможете сделать его намного проще. В этом случае вы можете создать коллекцию для краткого хранения того, что лучше, в виде списка отношений. Это намного проще в обслуживании, если вы позже отлаживаете или обнаруживаете, что вам нужно что-то изменить. К сожалению, поскольку колесо отношений сложное, я не могу придумать способ заставить компилятор гарантировать, что мы случайно не пропустим отношения.

Я бы начал с определения перечисления возможных вариантов и списка всех возможных сравнений. Перечисление — это естественный выбор, когда у вас есть конечный предопределенный набор вариантов. Теперь компилятор обнаружит, если мы сделаем какие-либо опечатки при вводе имен выбора в любом месте, что является риском, когда вы используете их как строковые литералы.

enum class Choice {
    Rock, Paper, Scissors, Lizard, Spock;
    companion object {
        val relationships = listOf(
            Relationship(Rock, Lizard, "crushes"),
            Relationship(Rock, Scissors, "crushes"),
            Relationship(Paper, Spock, "disproves"),
            Relationship(Paper, Rock, "covers"),
            Relationship(Scissors, Paper, "cuts"),
            Relationship(Scissors, Lizard, "decapitates"),
            Relationship(Spock, Rock, "vaporizes"),
            Relationship(Spock, Scissors, "smashes"),
            Relationship(Lizard, Paper, "eats"),
            Relationship(Lizard, Spock, "poisons")
        )
    }
}
data class Relationship(val winner: Choice, val loser: Choice, val means: String)

Теперь getUserChoice функции не нужно передавать список. Нам просто нужно проверить значения перечисления. Вы можете устранить сложность isValidInput и userValue используя while (true) и возвращаясь напрямую:

fun getUserChoice(): Choice {
    while (true) {
        println("Please enter one of the following:")
        for ((index, item) in Choice.values().withIndex()) {
            println("${index + 1} . $item")
        }
        val userInput = readLine().toString()
        println("You have chosen $userInput")
        try {
            return Choice.valueOf(userInput)
        } catch (e: IllegalArgumentException) {
            println(ERROR_MESSAGE)
        }
    }
}

Теперь, когда у нас есть список отношений, printResult становится очень просто. Мы находим взаимосвязь, которая соответствует нашим двум входам. Если ничего не найдено, что обозначено нулем, мы можем предположить, что это ничья.

fun printResult(gameChoice: Choice, userChoice: Choice) {
    println("I have chosen $gameChoice")
    val relationship = Choice.relationships.firstOrNull {
        (it.winner == gameChoice && it.loser == userChoice) ||
                (it.loser == gameChoice && it.winner == userChoice)
    }
    when (relationship) {
        null -> println("It is a draw")
        else -> with (relationship) { println("$winner $means $loser. $winner wins!!") }
    }
}

Наконец, ваш getGameChoice функция не имеет смысла, потому что она просто возвращает свой аргумент. Вы должны переместить рандомизацию внутри функции, чтобы она имела смысл:

fun getGameChoice() = Choice.values().random()

Основная функция становится:

fun main() {
    val gameChoice = getGameChoice()
    val userChoice = getUserChoice()
    printResult(gameChoice, userChoice)
}

  • Я не могу придумать способ заставить компилятор гарантировать, что мы случайно не упустим отношения. — Один из вариантов — использовать вложенные switch заявления, но я думаю, что этот вариант может быть даже лучше.

    — Саймон Форсберг

  • Точно, если вы не закодируете выигрыш / проигрыш внутри операторов каким-либо другим способом, а оператор else просто переключит их, но тогда у вас будет потенциальный переполнение стека, так что … Не стоит того.

    — Саймон Форсберг

  • Поразмыслив, я все еще не понимаю, как это можно сделать. Каждый выбор имеет ровно два второстепенных выбора, которые необходимо определить. Я не вижу способа обеспечить соответствие двух других вариантов каждому выбору или гарантировать, что нет случайных повторов.

    — Tenfour04


В этих двух сценариях:

1.

} else (ERROR_MESSAGE)
if (!isValidChoice) {
    ERROR_MESSAGE
}

Фактически сообщение об ошибке не печатается. Просто напишите код ERROR_MESSAGE не печатает.

Это должны быть:

1.

} else println(ERROR_MESSAGE)
if (!isValidChoice) {
    println(ERROR_MESSAGE)
}

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *