All Downloads are FREE. Search and download functionalities are using the official Maven repository.

xyz.cssxsh.arknights.mine.Question.kt Maven / Gradle / Ivy

There is a newer version: 2.3.1
Show newest version
package xyz.cssxsh.arknights.mine

import kotlinx.serialization.*
import xyz.cssxsh.arknights.bilibili.*
import xyz.cssxsh.arknights.excel.*
import java.time.*
import java.time.format.*

@Serializable
data class Question(
    @SerialName("problem")
    val problem: String,
    @SerialName("options")
    val options: Map,
    @SerialName("answer")
    val answer: Set,
    @SerialName("tips")
    val tips: String? = null,
    @SerialName("coin")
    val coin: Int,
    @SerialName("timeout")
    val timeout: Long,
    @SerialName("type")
    val type: QuestionType
)

enum class QuestionType(val description: String, private val load: (QuestionDataLoader) -> QuestionBuild) {
    BUILDING("基建相关", { randomBuildingQuestion(it.excel().buffs) }),
    PLAYER("玩家相关", { randomPlayerQuestion(it.excel().const) }),
    TALENT("天赋相关", { randomTalentQuestion(it.excel().characters) }),
    POSITION("位置相关", { randomPositionQuestion(it.excel().characters) }),
    PROFESSION("职业相关", { randomProfessionQuestion(it.excel().characters) }),
    RARITY("星级相关", { randomRarityQuestion(it.excel().characters) }),
    POWER("政权相关", { randomPowerQuestion(it.excel().powers) }),
    ILLUST("立绘相关", { randomIllustQuestion(it.excel().handbooks) }),
    VOICE("声优相关", { randomCharacterVoiceQuestion(it.excel().handbooks) }),
    INFO("档案相关", { randomInfoQuestion(it.excel().handbooks) }),
    SKILL("技能相关", { randomSkillQuestion(it.excel().skills) }),
    STORY("剧情相关", { randomStoryQuestion(it.excel().stories) }),
    ENEMY("敌方相关", { randomEnemyInfoQuestion(it.excel().enemies) }),
    WEEKLY("周常相关", { randomWeeklyQuestion(it.excel().weeks) }),
    MUSIC("音乐相关", { randomMusicQuestion(it.video().music) }),
    OTHER("自选相关", { requireNotNull(it.others().values.randomOrNull()) { "题目集为空" } });

    fun random(loader: QuestionDataLoader): Question = load(loader).build(this)
}

private fun Boolean.Companion.random() = listOf(true, false).random()

private val formatter = DateTimeFormatter.ofPattern("yy-MM-dd HH:mm:ss")

private val prefix get() = listOf(-1L, 1L).random()

private fun OffsetDateTime.randomDays(offset: Int = 1) = plusDays(offset * prefix)

private fun OffsetDateTime.randomMinutes(offset: Int = 30) = plusMinutes(offset * prefix)

private val DefaultChoiceRange = 'A'..'D'

data class QuestionDataLoader(
    val excel: () -> ExcelData,
    val video: () -> VideoData,
    val others: () -> Map,
)

sealed class QuestionBuild {
    abstract fun build(type: QuestionType): Question
}

data class ChoiceQuestion(
    val meaning: Pair,
    val range: CharRange,
    val relation: () -> List>
) : QuestionBuild() {
    override fun build(type: QuestionType): Question {
        val reversal = Boolean.random()
        val key = { pair: Pair -> if (reversal) pair.second else pair.first }
        val value = { pair: Pair -> if (reversal) pair.first else pair.second }

        val relation = relation().toMutableList().apply { shuffle() }
        val map = relation.groupBy(key, value)

        val options = (range zip map.entries).toMap()
        val random = options.values.flatMap { it.value }.random()
        val answer = options.filter { (_, list) -> random in list.value }.keys
        val problem = "下列选项中" + (if (reversal) "%1\$s[%3\$s]的%2\$s是" else "有%2\$s[%3\$s]的%1\$s是")
        return Question(
            problem = problem.format(meaning.first, meaning.second, random),
            options = options.mapValues { (_, entry) -> entry.key },
            answer = answer,
            coin = options.size * 100,
            timeout = options.size * 10_000L,
            type = type
        )
    }
}

@Serializable
data class CustomQuestion(
    @SerialName("problem")
    val problem: String,
    @SerialName("options")
    val options: Map,
    @SerialName("coin")
    val coin: Int,
    @SerialName("tips")
    val tips: String,
    @SerialName("timeout")
    val timeout: Long
) : QuestionBuild() {
    constructor(
        problem: String,
        right: List,
        error: List,
        coin: Int,
        tips: String,
        timeout: Long
    ) : this(
        problem = problem,
        options = right.associateWith { true } + error.associateWith { false },
        coin = coin,
        tips = tips,
        timeout = timeout
    )

    override fun build(type: QuestionType): Question {
        val map = (('A'..'Z') zip options.entries.toMutableList().apply { shuffle() }).toMap()
        return Question(
            problem = problem,
            options = map.mapValues { (_, entry) -> entry.key },
            answer = map.filter { (_, entry) -> entry.value }.keys,
            coin = coin,
            tips = tips,
            timeout = timeout,
            type = type
        )
    }
}

data class DateTimeQuestion(
    @SerialName("problem")
    val problem: String,
    @SerialName("datetime")
    val datetime: OffsetDateTime
) : QuestionBuild() {
    override fun build(type: QuestionType): Question {
        val range = DefaultChoiceRange
        val list = listOf(
            datetime,
            datetime.randomDays(),
            datetime.randomMinutes(),
            datetime.randomDays().randomMinutes()
        )
        val map = (range zip list).toMap()
        val answer = map.entries.filter { it.value == datetime }.map { it.key }.toSet()
        return Question(
            problem = problem,
            options = map.mapValues { (_, datetime) -> datetime.format(formatter) },
            answer = answer,
            coin = 1800,
            timeout = 3 * 60_000L,
            type = type
        )
    }
}

data class JudgmentQuestion(val generate: (Boolean) -> Pair) : QuestionBuild() {
    override fun build(type: QuestionType): Question {
        val state = Boolean.random()
        val (problem, tips) = generate(state)
        return Question(
            problem = problem,
            options = mapOf('Y' to "对", 'N' to "错"),
            answer = setOf(if (state) 'Y' else 'N'),
            coin = 600,
            tips = tips,
            timeout = 60_000L,
            type = type
        )
    }
}

typealias ConstInfoQuestion = (const: ConstInfo) -> QuestionBuild

typealias BuffQuestion = (buffs: BuffMap) -> QuestionBuild

typealias CharacterQuestion = (characters: CharacterMap) -> QuestionBuild

typealias HandbookQuestion = (handbooks: HandbookMap) -> QuestionBuild

typealias SkillQuestion = (skills: SkillMap) -> QuestionBuild

typealias VideoQuestion = (videos: Collection




© 2015 - 2024 Weber Informatics LLC | Privacy Policy