full basic functions

This commit is contained in:
SOR
2026-09-20 16:51:28 +08:00
parent d02eef7483
commit fca7e3b3b4
12 changed files with 252 additions and 0 deletions
@@ -0,0 +1,5 @@
package io.github.sor2171.puzzles.annotation
@Target(AnnotationTarget.FILE)
@Retention(AnnotationRetention.SOURCE)
annotation class SkipCompile(val skip: Boolean = true)
@@ -0,0 +1,33 @@
package io.github.sor2171.puzzles.service
import io.github.sor2171.puzzles.utils.CompileDetector
class PuzzleNote(
className: String,
vararg notes: PuzzleNote? = arrayOf(null)
) {
val className = CompileDetector.getColoredName(className)
val notes = notes
override fun toString(): String {
val sb = StringBuilder()
sb.append(className)
if (notes.size > 1) {
for (i in 1 until notes.size) {
sb.append("\n │")
notes[i]?.let { note ->
sb.append("\n ├── ").append(note.className)
}
}
}
val nextNote = notes.firstOrNull()
if (nextNote != null) {
sb.append("\n │\n").append(nextNote.toString())
}
return sb.toString()
}
}
@@ -0,0 +1,57 @@
package io.github.sor2171.puzzles.service
import kotlin.random.Random
import kotlin.reflect.KClass
class RandomGenerator(vararg keys: Pair<String, KClass<*>>) {
val variableMap = mutableMapOf<String, Any>()
init {
keys.forEach { (name, type) ->
variableMap[name] = when (type) {
String::class -> randomString()
Byte::class -> Random.nextInt(
Byte.MIN_VALUE.toInt(),
Byte.MAX_VALUE.toInt() + 1
).toByte()
Short::class -> Random.nextInt(
Short.MIN_VALUE.toInt(),
Short.MAX_VALUE.toInt() + 1
).toShort()
Int::class -> Random.nextInt()
Long::class -> Random.nextLong()
// [0.0, 1.0)
Float::class -> Random.nextFloat()
Double::class -> Random.nextDouble()
UByte::class -> Random.nextInt(0, UByte.MAX_VALUE.toInt() + 1).toUByte()
UShort::class -> Random.nextInt(0, UShort.MAX_VALUE.toInt() + 1).toUShort()
UInt::class -> Random.nextInt().toUInt()
ULong::class -> Random.nextLong().toULong()
else -> error("不支持的类型: ${type.qualifiedName}")
}
}
}
private fun randomString(length: Int = Random.nextInt(5, 16)): String {
val chars = ('a'..'z') + ('A'..'Z').toList() + ('0'..'9').toList()
return buildString(length) {
repeat(length) {
append(chars[Random.nextInt(chars.size)])
}
}
}
operator fun get(key: String) = variableMap[key]
operator fun set(key: String, value: Any) {
variableMap[key] = value
}
}
@@ -0,0 +1,47 @@
package io.github.sor2171.puzzles.utils
import io.github.sor2171.puzzles.service.PuzzleNote
object CatalogStructure {
enum class Classification {
ALPHABET,
HYPER,
EXTRA;
override fun toString(): String {
return super.toString().lowercase()
}
}
val puzzlesArray = arrayOf(
arrayOf(
"${Classification.ALPHABET}.First",
"${Classification.EXTRA}.FirstExtra",
),
arrayOf(
"${Classification.HYPER}.Second",
)
)
val firstPuzzleNote: PuzzleNote
get() {
var nextMainNote: PuzzleNote? = null
for (i in puzzlesArray.indices.reversed()) {
val array = puzzlesArray[i]
val children = Array(array.size) { index ->
if (index == 0) {
nextMainNote
} else {
PuzzleNote(array[index])
}
}
nextMainNote = PuzzleNote(array[0], *children)
}
return nextMainNote!!
}
fun printAllMessage() = println(firstPuzzleNote)
}
@@ -0,0 +1,24 @@
package io.github.sor2171.puzzles.utils
object CompileDetector {
const val ANSI_GREEN = "\u001b[32m"
const val ANSI_RESET = "\u001b[0m"
fun detect(fullName: String): Boolean {
return try {
Class.forName(fullName)
true
} catch (_: ClassNotFoundException) {
false
}
}
fun fullName(className: String): String =
"io.github.sor2171.puzzles.$className"
fun getColoredName(className: String): String {
val fullName = fullName(className)
return if (detect(fullName)) "$ANSI_GREEN$className$ANSI_RESET"
else className
}
}
@@ -0,0 +1,14 @@
package io.github.sor2171.puzzles
import io.github.sor2171.puzzles.utils.CatalogStructure
import org.junit.jupiter.api.Test
/**
* 运行该测试类即可查询此 Kotlin Puzzle 的详细信息。
*/
class Catalog {
@Test
fun getCatalog() {
CatalogStructure.printAllMessage()
}
}
@@ -0,0 +1,29 @@
@file:SkipCompile(skip = false)
package io.github.sor2171.puzzles.alphabet
import io.github.sor2171.puzzles.annotation.SkipCompile
import io.github.sor2171.puzzles.service.RandomGenerator
import org.junit.jupiter.api.Test
class First {
val randoms = RandomGenerator(
"a" to Int::class,
"b" to Double::class,
)
@Test
fun puzzle() {
// write start 1
val a = randoms["a"]
// write end 1
assert( a == randoms["a"])
// write start 2
val b = randoms["b"]
// write end 2
assert( b == randoms["b"])
}
}
@@ -0,0 +1,22 @@
package io.github.sor2171.puzzles.extend
import org.junit.jupiter.api.extension.AfterAllCallback
import org.junit.jupiter.api.extension.BeforeAllCallback
import org.junit.jupiter.api.extension.ExtensionContext
class TestLifecycleLoggerExtension : BeforeAllCallback, AfterAllCallback {
override fun beforeAll(context: ExtensionContext) {
val testClassName = context.requiredTestClass.simpleName
println()
println("=== [START] $testClassName ===")
println()
}
override fun afterAll(context: ExtensionContext) {
val testClassName = context.requiredTestClass.simpleName
println()
println("=== [ END ] $testClassName ===")
println()
}
}
@@ -0,0 +1,8 @@
@file:SkipCompile(skip = true)
package io.github.sor2171.puzzles.extra
import io.github.sor2171.puzzles.annotation.SkipCompile
class FirstExtra {
}
@@ -0,0 +1,11 @@
@file:SkipCompile(skip = true)
package io.github.sor2171.puzzles.hyper
import io.github.sor2171.puzzles.annotation.SkipCompile
import org.junit.jupiter.api.Test
class Second {
@Test
fun () {}
}
@@ -0,0 +1 @@
io.github.sor2171.puzzles.extend.TestLifecycleLoggerExtension
@@ -0,0 +1 @@
junit.jupiter.extensions.autodetection.enabled=true