commonMain.dev.teogor.sudoklify.tokenizer.JEncodedCell.kt Maven / Gradle / Ivy
/*
* Copyright 2024 Teogor (Teodor Grigor)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dev.teogor.sudoklify.tokenizer
import kotlin.jvm.JvmInline
/**
* A value class representing a J-Encoded string for Sudoku cells.
* The J-Encoded string uses letters 'a' to 'j', where 'j' represents 0.
*
* Example:
* ```kotlin
* val encodedCell = JEncodedCell("E") // Represents 5
* val intValue = encodedCell.toInt() // Converts back to integer value
* ```
*/
@JvmInline
value class JEncodedCell(val value: String) {
/**
* Converts this [JEncodedCell] to its corresponding integer value.
*
* @return The integer value represented by the [JEncodedCell], using base-10 encoding.
*/
fun toInt(): Int {
return when {
value == "-" -> 0
else ->
value.map { char ->
when {
char == 'j' || char == 'J' -> {
0
}
char.isUpperCase() -> {
char - 'A' + 1
}
else -> {
char - 'a' + 1
}
}
}.fold(0) { acc, digit -> acc * 10 + digit }
}
}
override fun toString(): String = value
companion object {
val regex = """([A-I][a-z]+)|-|[A-I]""".toRegex()
/**
* Validates if a given string contains only valid JEncodedCells.
*
* @param input The string to validate.
* @return `true` if the string is valid, `false` otherwise.
*/
fun isValid(input: String): Boolean {
val matches = regex.findAll(input)
// Join all the matched parts and compare it with the original string
// This ensures that the entire string is composed of valid components
val matchedString = matches.joinToString("") { it.value }
return matchedString == input
}
/**
* Extracts individual JEncodedCells from a string.
*
* @param input The string representing the Sudoku board.
* @return A list of [JEncodedCell] extracted from the input string.
*/
fun extractCells(input: String): List {
return regex.findAll(input).map { JEncodedCell(it.value) }.toList()
}
/**
* Finds and returns the invalid components in the given string.
*
* @param input The string to check.
* @return A list of invalid components found in the string.
*/
fun findInvalidComponents(input: String): List {
val validMatches = regex.findAll(input).map { it.range }.toList()
val invalidComponents = mutableListOf()
// Identify gaps between valid matches, which correspond to invalid components.
var lastEndIndex = 0
for (range in validMatches) {
if (range.first > lastEndIndex) {
// Extract the invalid part between the previous valid end and the current valid start
invalidComponents.add(input.substring(lastEndIndex, range.first))
}
lastEndIndex = range.last + 1
}
// Check if there's any invalid component after the last valid match
if (lastEndIndex < input.length) {
invalidComponents.add(input.substring(lastEndIndex))
}
return invalidComponents
}
}
}
/**
* Converts an integer representing a Sudoku cell value to its J-Encoded string representation.
*
* Example:
* ```kotlin
* 5.toJEncodedCell() // Returns JEncodedCell("E")
* 10.toJEncodedCell() // Returns JEncodedCell("Aj")
* ```
*
* @receiver The integer representing the Sudoku cell value.
*
* @return The [JEncodedCell] string representation of the cell, using letters 'a' to 'j'
* (where 'j' is used for 0), and capitalizing the first letter.
*/
fun Int.toJEncodedCell(): JEncodedCell {
val encodedString =
when {
this == 0 -> "-"
this in 1..9 -> ('a' + this - 1).uppercase()
else -> {
var valueCopy = this
buildString {
while (valueCopy > 0) {
val digit = valueCopy % 10
append(
if (digit != 0) {
('a' + digit - 1)
} else {
'j'
},
)
valueCopy /= 10
}
reverse()
this[0] = this[0].uppercaseChar()
}
}
}
return JEncodedCell(encodedString)
}
/**
* Converts a string representation to a [JEncodedCell].
*
* This function wraps a string into a [JEncodedCell], which is a value class used for representing
* Sudoku cell values in their J-Encoded string format. The conversion is straightforward as it
* simply creates an instance of [JEncodedCell] with the provided string.
*
* **Example:**
*
* ```kotlin
* val cellString = "A" // Represents a J-Encoded cell value
*
* // Convert the string to a JEncodedCell
* val encodedCell: JEncodedCell = cellString.toJEncodedCell()
* ```
*
* @receiver The string representation of the Sudoku cell.
* @return A [JEncodedCell] instance initialized with the string.
*/
fun String.toJEncodedCell(): JEncodedCell {
return JEncodedCell(this)
}