From 86e1c357ddc2fb758b943bf0bc822477ac29a671 Mon Sep 17 00:00:00 2001 From: Roman Makeev <57789105+makeevrserg@users.noreply.github.com> Date: Sun, 11 Aug 2024 22:01:40 +0000 Subject: [PATCH] More chat games (#71) * #59 add chat based games * #59 fix ignore case for answer * add arrow ascii * up version 2.24.0 * fix reward * add min max amount for money reward * add anagram * add quadratic equation --- gradle.properties | 2 +- .../module/chatgame/di/ChatGameModule.kt | 8 +- .../aspekt/module/chatgame/job/ChatGameJob.kt | 5 +- .../aspekt/module/chatgame/model/ChatGame.kt | 11 +++ .../module/chatgame/model/ChatGameData.kt | 9 ++ .../module/chatgame/store/ChatGameStore.kt | 4 +- .../chatgame/store/ChatGameStoreImpl.kt | 7 +- .../module/chatgame/store/RiddleGenerator.kt | 99 +++++++++++++++---- .../aspekt/plugin/PluginTranslation.kt | 20 +++- 9 files changed, 133 insertions(+), 32 deletions(-) create mode 100644 modules/chatgame/src/main/kotlin/ru/astrainteractive/aspekt/module/chatgame/model/ChatGameData.kt diff --git a/gradle.properties b/gradle.properties index 66fe5c8..dbb1322 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,7 +12,7 @@ makeevrserg.java.ktarget=21 # Project makeevrserg.project.name=AspeKt makeevrserg.project.group=ru.astrainteractive.aspekt -makeevrserg.project.version.string=2.24.0 +makeevrserg.project.version.string=2.25.0 makeevrserg.project.description=Essentials plugin for EmpireProjekt makeevrserg.project.developers=makeevrserg|Makeev Roman|makeevrserg@gmail.com makeevrserg.project.url=https://empireprojekt.ru diff --git a/modules/chatgame/src/main/kotlin/ru/astrainteractive/aspekt/module/chatgame/di/ChatGameModule.kt b/modules/chatgame/src/main/kotlin/ru/astrainteractive/aspekt/module/chatgame/di/ChatGameModule.kt index b1be3a0..f06338f 100644 --- a/modules/chatgame/src/main/kotlin/ru/astrainteractive/aspekt/module/chatgame/di/ChatGameModule.kt +++ b/modules/chatgame/src/main/kotlin/ru/astrainteractive/aspekt/module/chatgame/di/ChatGameModule.kt @@ -5,6 +5,7 @@ import ru.astrainteractive.aspekt.module.chatgame.command.ChatGameCommand import ru.astrainteractive.aspekt.module.chatgame.job.ChatGameJob import ru.astrainteractive.aspekt.module.chatgame.model.ChatGameConfig import ru.astrainteractive.aspekt.module.chatgame.store.ChatGameStoreImpl +import ru.astrainteractive.aspekt.module.chatgame.store.RiddleGenerator import ru.astrainteractive.astralibs.lifecycle.Lifecycle import ru.astrainteractive.astralibs.serialization.StringFormatExt.parseOrDefault import ru.astrainteractive.astralibs.serialization.StringFormatExt.writeIntoFile @@ -22,7 +23,11 @@ interface ChatGameModule { } private val chatGameStore by lazy { ChatGameStoreImpl( - chatGameConfigProvider = { config.value } + chatGameConfigProvider = { config.value }, + riddleGenerator = RiddleGenerator( + configProvider = { config.value }, + translationProvider = { coreModule.translation.value } + ) ) } private val chatGameJob by lazy { @@ -30,7 +35,6 @@ interface ChatGameModule { chatGameStore = chatGameStore, chatGameConfigProvider = { config.value }, kyoriComponentSerializerProvider = { coreModule.kyoriComponentSerializer.value }, - translationProvider = { coreModule.translation.value } ) } private val command by lazy { diff --git a/modules/chatgame/src/main/kotlin/ru/astrainteractive/aspekt/module/chatgame/job/ChatGameJob.kt b/modules/chatgame/src/main/kotlin/ru/astrainteractive/aspekt/module/chatgame/job/ChatGameJob.kt index 4a45bb1..058a1c1 100644 --- a/modules/chatgame/src/main/kotlin/ru/astrainteractive/aspekt/module/chatgame/job/ChatGameJob.kt +++ b/modules/chatgame/src/main/kotlin/ru/astrainteractive/aspekt/module/chatgame/job/ChatGameJob.kt @@ -4,7 +4,6 @@ import org.bukkit.Bukkit import ru.astrainteractive.aspekt.job.ScheduledJob import ru.astrainteractive.aspekt.module.chatgame.model.ChatGameConfig import ru.astrainteractive.aspekt.module.chatgame.store.ChatGameStore -import ru.astrainteractive.aspekt.plugin.PluginTranslation import ru.astrainteractive.astralibs.kyori.KyoriComponentSerializer import ru.astrainteractive.klibs.kdi.Provider import ru.astrainteractive.klibs.kdi.getValue @@ -13,9 +12,7 @@ internal class ChatGameJob( private val chatGameStore: ChatGameStore, chatGameConfigProvider: Provider, kyoriComponentSerializerProvider: Provider, - translationProvider: Provider ) : ScheduledJob("ChatGameJob") { - private val translation by translationProvider private val kyoriComponentSerializer by kyoriComponentSerializerProvider private val chatGameConfig by chatGameConfigProvider override val delayMillis: Long @@ -29,7 +26,7 @@ internal class ChatGameJob( chatGameStore.startNextGame() val state = chatGameStore.state.value as? ChatGameStore.State.Started ?: return with(kyoriComponentSerializer) { - translation.chatGame.gameStarted(state.chatGame.question.raw) + state.chatGame.question .component .run(Bukkit::broadcast) } diff --git a/modules/chatgame/src/main/kotlin/ru/astrainteractive/aspekt/module/chatgame/model/ChatGame.kt b/modules/chatgame/src/main/kotlin/ru/astrainteractive/aspekt/module/chatgame/model/ChatGame.kt index 3c74c57..9e7a3b3 100644 --- a/modules/chatgame/src/main/kotlin/ru/astrainteractive/aspekt/module/chatgame/model/ChatGame.kt +++ b/modules/chatgame/src/main/kotlin/ru/astrainteractive/aspekt/module/chatgame/model/ChatGame.kt @@ -28,4 +28,15 @@ internal sealed interface ChatGame { @SerialName("EQUATION_EASY") @Serializable class EquationEasy(override val reward: Reward? = null) : ChatGame + + @SerialName("EQUATION_QUADRATIC") + @Serializable + class QuadraticEquation(override val reward: Reward? = null) : ChatGame + + @SerialName("ANAGRAM") + @Serializable + class Anagram( + val words: List, + override val reward: Reward? = null + ) : ChatGame } diff --git a/modules/chatgame/src/main/kotlin/ru/astrainteractive/aspekt/module/chatgame/model/ChatGameData.kt b/modules/chatgame/src/main/kotlin/ru/astrainteractive/aspekt/module/chatgame/model/ChatGameData.kt new file mode 100644 index 0000000..7bde156 --- /dev/null +++ b/modules/chatgame/src/main/kotlin/ru/astrainteractive/aspekt/module/chatgame/model/ChatGameData.kt @@ -0,0 +1,9 @@ +package ru.astrainteractive.aspekt.module.chatgame.model + +import ru.astrainteractive.astralibs.string.StringDesc + +internal data class ChatGameData( + val question: StringDesc.Raw, + val answers: List, + val reward: Reward +) diff --git a/modules/chatgame/src/main/kotlin/ru/astrainteractive/aspekt/module/chatgame/store/ChatGameStore.kt b/modules/chatgame/src/main/kotlin/ru/astrainteractive/aspekt/module/chatgame/store/ChatGameStore.kt index 12ee82c..780a9eb 100644 --- a/modules/chatgame/src/main/kotlin/ru/astrainteractive/aspekt/module/chatgame/store/ChatGameStore.kt +++ b/modules/chatgame/src/main/kotlin/ru/astrainteractive/aspekt/module/chatgame/store/ChatGameStore.kt @@ -1,7 +1,7 @@ package ru.astrainteractive.aspekt.module.chatgame.store import kotlinx.coroutines.flow.StateFlow -import ru.astrainteractive.aspekt.module.chatgame.model.ChatGame +import ru.astrainteractive.aspekt.module.chatgame.model.ChatGameData internal interface ChatGameStore { val state: StateFlow @@ -13,6 +13,6 @@ internal interface ChatGameStore { sealed interface State { data object Pending : State - data class Started(val chatGame: ChatGame.Riddle) : State + data class Started(val chatGame: ChatGameData) : State } } diff --git a/modules/chatgame/src/main/kotlin/ru/astrainteractive/aspekt/module/chatgame/store/ChatGameStoreImpl.kt b/modules/chatgame/src/main/kotlin/ru/astrainteractive/aspekt/module/chatgame/store/ChatGameStoreImpl.kt index 70563b7..c756d6a 100644 --- a/modules/chatgame/src/main/kotlin/ru/astrainteractive/aspekt/module/chatgame/store/ChatGameStoreImpl.kt +++ b/modules/chatgame/src/main/kotlin/ru/astrainteractive/aspekt/module/chatgame/store/ChatGameStoreImpl.kt @@ -10,7 +10,8 @@ import ru.astrainteractive.klibs.kdi.Provider import ru.astrainteractive.klibs.kdi.getValue internal class ChatGameStoreImpl( - chatGameConfigProvider: Provider + chatGameConfigProvider: Provider, + private val riddleGenerator: RiddleGenerator ) : ChatGameStore, Logger by JUtiltLogger("ChatGameStore") { private val chatGameConfig by chatGameConfigProvider @@ -26,13 +27,13 @@ internal class ChatGameStoreImpl( error { "#startNextGame could not start chat game" } return } - val game = RiddleGenerator.generate(nextGame) + val game = riddleGenerator.generate(nextGame) _state.value = ChatGameStore.State.Started(game) } override fun isAnswerCorrect(answer: String): Boolean { val currentGame = state.value as? ChatGameStore.State.Started ?: return false - return answer.equals(currentGame.chatGame.answer, true) + return currentGame.chatGame.answers.any { it.equals(answer, true) } } override fun isGameStarted(): Boolean { diff --git a/modules/chatgame/src/main/kotlin/ru/astrainteractive/aspekt/module/chatgame/store/RiddleGenerator.kt b/modules/chatgame/src/main/kotlin/ru/astrainteractive/aspekt/module/chatgame/store/RiddleGenerator.kt index dfa05b6..1bea1ef 100644 --- a/modules/chatgame/src/main/kotlin/ru/astrainteractive/aspekt/module/chatgame/store/RiddleGenerator.kt +++ b/modules/chatgame/src/main/kotlin/ru/astrainteractive/aspekt/module/chatgame/store/RiddleGenerator.kt @@ -1,46 +1,111 @@ package ru.astrainteractive.aspekt.module.chatgame.store import ru.astrainteractive.aspekt.module.chatgame.model.ChatGame -import ru.astrainteractive.aspekt.module.chatgame.model.ChatGame.Riddle -import ru.astrainteractive.astralibs.string.StringDesc +import ru.astrainteractive.aspekt.module.chatgame.model.ChatGameConfig +import ru.astrainteractive.aspekt.module.chatgame.model.ChatGameData +import ru.astrainteractive.aspekt.plugin.PluginTranslation +import ru.astrainteractive.klibs.kdi.Provider +import ru.astrainteractive.klibs.kdi.getValue +import kotlin.math.pow +import kotlin.math.sign +import kotlin.math.sqrt import kotlin.random.Random -internal object RiddleGenerator { - fun generate(instance: ChatGame): ChatGame.Riddle { +internal class RiddleGenerator( + configProvider: Provider, + translationProvider: Provider +) { + private val config by configProvider + private val translation by translationProvider + + @Suppress("LongMethod", "CyclomaticComplexMethod") + fun generate(instance: ChatGame): ChatGameData { return when (instance) { is ChatGame.EquationEasy -> { val first = Random.nextInt(0, 99) val second = Random.nextInt(0, 99) - return Riddle( - question = StringDesc.Raw( + return ChatGameData( + question = translation.chatGame.solveExample( when (Random.nextBoolean()) { true -> "x+$first=$second" false -> "x-$second=-$first" } ), - answer = "${second - first}", - reward = instance.reward + answers = listOf("${second - first}"), + reward = instance.reward ?: config.defaultReward ) } - is ChatGame.Riddle -> instance + is ChatGame.Riddle -> ChatGameData( + question = translation.chatGame.solveRiddle(instance.question.raw), + answers = listOf(instance.answer), + reward = instance.reward ?: config.defaultReward + ) + is ChatGame.SumOfTwo -> { val first = Random.nextInt(0, 200) val second = Random.nextInt(0, 200) - return Riddle( - question = StringDesc.Raw("$first+$second"), - answer = "${first + second}", - reward = instance.reward + return ChatGameData( + question = translation.chatGame.solveExample("$first+$second"), + answers = listOf("${first + second}"), + reward = instance.reward ?: config.defaultReward ) } is ChatGame.TimesOfTwo -> { val first = Random.nextInt(0, 30) val second = Random.nextInt(0, 30) - return Riddle( - question = StringDesc.Raw("$first*$second"), - answer = "${first * second}", - reward = instance.reward + return ChatGameData( + question = translation.chatGame.solveExample("$first*$second"), + answers = listOf("${first * second}"), + reward = instance.reward ?: config.defaultReward + ) + } + + is ChatGame.Anagram -> { + val word = instance.words.random() + val anagram: String = buildString { + word.indices.shuffled().forEach { i -> + this.append(word[i]) + } + } + ChatGameData( + question = translation.chatGame.solveAnagram(anagram), + answers = listOf(word), + reward = instance.reward ?: config.defaultReward + ) + } + + is ChatGame.QuadraticEquation -> { + val nonZeroRandom = ((-12..12).toList() - 0) + val b = nonZeroRandom.random() + val a = nonZeroRandom.random() + val c = nonZeroRandom.random().let { c -> + if (a < 0) c * c.sign else -1 + } + val equation = buildString { + append(a) + append("x²") + if (a != 0 && b.sign == 1) append("+") + append(b) + append("x") + if (c.sign == 1 && (a != 0 || b != 0)) append("+") + append(c) + } + + fun Double.roundDec(dec: Int): Double { + val tens = 10.0.pow(dec) + return kotlin.math.round(this * tens) / tens + } + + val d = b * b - 4 * a * c + val sqrtD = sqrt(d.toDouble()) + val root1 = (-b + sqrtD).div(2 * a).roundDec(2) + val root2 = (-b - sqrtD).div(2 * a).roundDec(2) + ChatGameData( + question = translation.chatGame.solveQuadratic(equation), + answers = listOf(root1.toString(), root2.toString()).also { println(it) }, + reward = instance.reward ?: config.defaultReward ) } } diff --git a/modules/core/src/main/kotlin/ru/astrainteractive/aspekt/plugin/PluginTranslation.kt b/modules/core/src/main/kotlin/ru/astrainteractive/aspekt/plugin/PluginTranslation.kt index a551c86..5258e36 100644 --- a/modules/core/src/main/kotlin/ru/astrainteractive/aspekt/plugin/PluginTranslation.kt +++ b/modules/core/src/main/kotlin/ru/astrainteractive/aspekt/plugin/PluginTranslation.kt @@ -29,8 +29,18 @@ class PluginTranslation( ) { @Serializable class ChatGame( - @SerialName("game_started") - private val gameStarted: StringDesc.Raw = StringDesc.Raw("&7[&#DBB72BКВИЗ&7] %quiz% → &2/quiz ОТВЕТ"), + @SerialName("solve_riddle") + private val solveRiddle: StringDesc.Raw = StringDesc.Raw("&7[&#DBB72BКВИЗ&7] Загадка: %quiz% → &2/quiz ОТВЕТ"), + @SerialName("solve_example") + private val solveExample: StringDesc.Raw = StringDesc.Raw("&7[&#DBB72BКВИЗ&7] Пример: %quiz% → &2/quiz ОТВЕТ"), + @SerialName("solve_anagram") + private val solveAnagram: StringDesc.Raw = StringDesc.Raw( + "&7[&#DBB72BКВИЗ&7] Анаграмма: %quiz% → &2/quiz ОТВЕТ" + ), + @SerialName("solve_quadratic") + private val solveQuadratic: StringDesc.Raw = StringDesc.Raw( + "&7[&#DBB72BКВИЗ&7] Квадратное уравнение: %quiz% → &2/quiz ОТВЕТ &7Любой вариант ответа с точностью до сотой. Например: 0.02, 0.3, 1.0" + ), @SerialName("no_quiz_available") val noQuizAvailable: StringDesc.Raw = StringDesc.Raw( "&7[&#DBB72BКВИЗ&7] &#db2c18В данный момент нет активного квиза!" @@ -42,7 +52,11 @@ class PluginTranslation( "&7[&#DBB72BКВИЗ&7] &6%player% &7угадал верный ответ! И получил &6%amount% &7монет!" ), ) { - fun gameStarted(quiz: String) = gameStarted.replace("%quiz%", quiz) + fun solveRiddle(quiz: String) = solveRiddle.replace("%quiz%", quiz) + fun solveExample(quiz: String) = solveExample.replace("%quiz%", quiz) + fun solveAnagram(quiz: String) = solveAnagram.replace("%quiz%", quiz) + fun solveQuadratic(quiz: String) = solveQuadratic.replace("%quiz%", quiz) + fun gameEndedMoneyReward(player: String, amount: Number) = gameEndedMoneyReward .replace("%player%", player) .replace("%amount%", amount.toString())