Refacor Mastermind and implement functional style

This commit is contained in:
Martin Berg Alstad
2025-07-31 15:46:05 +02:00
parent 1b8704ef43
commit 6ef68a5e05
4 changed files with 83 additions and 30 deletions

View File

@ -0,0 +1,3 @@
package assignment.week2.mastermind
data class Evaluation(val rightPosition: Int, val wrongPosition: Int)

View File

@ -1,6 +1,4 @@
package mastermind
data class Evaluation(val rightPosition: Int, val wrongPosition: Int)
package assignment.week2.mastermind
enum class Match {
RIGHT_POSITION,
@ -13,31 +11,57 @@ fun evaluateGuess(secret: String, guess: String): Evaluation {
val secretMatches = createMatches(secret)
for ((index, guessChar) in guess.withIndex()) {
if (secretMatches[index].char == guessChar) {
if (secretMatches[index].match == Match.WRONG_POSITION) {
for ((index, secretChar) in secret.drop(index + 1).withIndex()) {
if (secretChar == guessChar) {
secretMatches[index].match = Match.WRONG_POSITION
break
}
}
}
secretMatches[index].match = Match.RIGHT_POSITION
val secretMatch = secretMatches[index]
if (secretMatch.char == guessChar) {
handleMatch(secretMatches, index)
continue
}
for ((index, secretChar) in secret.withIndex()) {
if (secretMatches[index].match == null && secretChar == guessChar) {
secretMatches[index].match = Match.WRONG_POSITION
break
}
}
handleNotMatch(secretMatches, guessChar)
}
return Evaluation(
secretMatches.filter { it.match == Match.RIGHT_POSITION }.size,
secretMatches.filter { it.match == Match.WRONG_POSITION }.size
secretMatches.countMatch(Match.RIGHT_POSITION),
secretMatches.countMatch(Match.WRONG_POSITION)
)
}
fun createMatches(secret: String): List<CharMatch> = secret.map { CharMatch(it, null) }
private fun handleNotMatch(
secretMatches: List<CharMatch>,
guessChar: Char
) {
for (secretMatch in secretMatches) {
if (secretMatch.match == null && secretMatch.char == guessChar) {
secretMatch.match = Match.WRONG_POSITION
return
}
}
}
private fun handleMatch(secretMatches: List<CharMatch>, index: Int) {
val secretMatch = secretMatches[index]
if (secretMatch.match == Match.WRONG_POSITION) {
moveWrongPosition(
secretMatches.drop(index + 1).map { it.char },
secretMatch.char,
secretMatches
)
}
secretMatch.match = Match.RIGHT_POSITION
}
private fun List<CharMatch>.countMatch(match: Match): Int = filter { it.match == match }.size
private fun moveWrongPosition(
chars: List<Char>,
guessChar: Char,
secretMatches: List<CharMatch>
) {
for ((index, secretChar) in chars.withIndex()) {
if (secretChar == guessChar) {
secretMatches[index].match = Match.WRONG_POSITION
return
}
}
}
fun createMatches(secret: String): List<CharMatch> = secret.map { CharMatch(it, null) }

View File

@ -0,0 +1,22 @@
package assignment.week2.mastermind
fun evaluateGuessFunctional(secret: String, guess: String): Evaluation {
val rightPositions = secret.zip(guess).count { (sChar, gChar) -> sChar == gChar }
val commonLetters = "ABCDEF".sumOf { ch ->
secret.count { it == ch }.coerceAtMost(guess.count { it == ch })
}
return Evaluation(rightPositions, commonLetters - rightPositions)
}
fun main() {
val result = Evaluation(rightPosition = 1, wrongPosition = 1)
evaluateGuessFunctional("BCDF", "ACEB") eq result
evaluateGuessFunctional("AAAF", "ABCA") eq result
evaluateGuessFunctional("ABCA", "AAAF") eq result
}
private infix fun <T> T.eq(other: T) {
if (this != other) throw AssertionError("Expected $this to equal $other")
println("OK")
}

View File

@ -1,4 +1,4 @@
package mastermind
package assignment.week2.mastermind
import kotlin.random.Random
@ -11,8 +11,8 @@ fun main() {
}
fun playMastermind(
differentLetters: Boolean,
secret: String = generateSecret(differentLetters)
differentLetters: Boolean,
secret: String = generateSecret(differentLetters)
) {
var evaluation: Evaluation
@ -20,17 +20,21 @@ fun playMastermind(
print("Your guess: ")
var guess = readLine()!!
while (hasErrorsInInput(guess)) {
println("Incorrect input: $guess. " +
"It should consist of $CODE_LENGTH characters from $ALPHABET. " +
"Please try again.")
println(
"Incorrect input: $guess. " +
"It should consist of $CODE_LENGTH characters from $ALPHABET. " +
"Please try again."
)
guess = readLine()!!
}
evaluation = evaluateGuess(secret, guess)
if (evaluation.isComplete()) {
println("The guess is correct!")
} else {
println("Right positions: ${evaluation.rightPosition}; " +
"wrong positions: ${evaluation.wrongPosition}.")
println(
"Right positions: ${evaluation.rightPosition}; " +
"wrong positions: ${evaluation.wrongPosition}."
)
}
} while (!evaluation.isComplete())