mirror of https://github.com/hykilpikonna/AquaDX
[F] Fix detailed ranks
parent
0100140dc0
commit
e85533686e
|
@ -76,4 +76,5 @@ gradle-app.setting
|
|||
### Gradle Patch ###
|
||||
# Java heap dump
|
||||
*.hprof
|
||||
.jpb
|
||||
.jpb
|
||||
src/main/resources/meta/*/*.json
|
||||
|
|
|
@ -7,7 +7,9 @@ import io.ktor.client.plugins.contentnegotiation.*
|
|||
import io.ktor.serialization.kotlinx.json.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonNamingStrategy
|
||||
import org.apache.tika.Tika
|
||||
import org.apache.tika.mime.MimeTypes
|
||||
import org.springframework.http.HttpStatus
|
||||
|
@ -47,12 +49,15 @@ val emailRegex = "^(?=.{1,64}@)[\\p{L}0-9_-]+(\\.[\\p{L}0-9_-]+)*@[^-][\\p{L}0-9
|
|||
fun Str.isValidEmail(): Bool = emailRegex.matches(this)
|
||||
|
||||
// Global tools
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
val JSON = Json {
|
||||
ignoreUnknownKeys = true
|
||||
isLenient = true
|
||||
namingStrategy = JsonNamingStrategy.SnakeCase
|
||||
}
|
||||
val HTTP = HttpClient(CIO) {
|
||||
install(ContentNegotiation) {
|
||||
json(Json {
|
||||
ignoreUnknownKeys = true
|
||||
isLenient = true
|
||||
})
|
||||
json(JSON)
|
||||
}
|
||||
}
|
||||
val TIKA = Tika()
|
||||
|
|
|
@ -19,7 +19,7 @@ class Chusan(
|
|||
val userPlaylogRepository: UserPlaylogRepository,
|
||||
val userDataRepository: UserDataRepository,
|
||||
val userGeneralDataRepository: UserGeneralDataRepository
|
||||
): GameApiController
|
||||
): GameApiController("chu3")
|
||||
{
|
||||
override suspend fun trend(@RP username: Str): List<TrendOut> = us.cardByName(username) { card ->
|
||||
findTrend(userPlaylogRepository.findByUser_Card_ExtId(card.extId)
|
||||
|
|
|
@ -19,7 +19,7 @@ class Maimai2(
|
|||
val userPlaylogRepository: UserPlaylogRepository,
|
||||
val userDataRepository: UserDataRepository,
|
||||
val userGeneralDataRepository: UserGeneralDataRepository
|
||||
): GameApiController
|
||||
): GameApiController("mai2")
|
||||
{
|
||||
override suspend fun trend(@RP username: Str): List<TrendOut> = us.cardByName(username) { card ->
|
||||
findTrend(userPlaylogRepository.findByUser_Card_ExtId(card.extId)
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package icu.samnyan.aqua.net.games
|
||||
|
||||
import ext.API
|
||||
import ext.JSON
|
||||
import ext.RP
|
||||
import icu.samnyan.aqua.net.utils.IGenericGamePlaylog
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
data class TrendOut(val date: String, val rating: Int, val plays: Int)
|
||||
|
||||
|
@ -19,6 +21,7 @@ data class GenericGameSummary(
|
|||
val rating: Int,
|
||||
val ratingHighest: Int,
|
||||
val ranks: List<RankCount>,
|
||||
val detailedRanks: Map<Int, Map<String, Int>>,
|
||||
val maxCombo: Int,
|
||||
val fullCombo: Int,
|
||||
val allPerfect: Int,
|
||||
|
@ -46,15 +49,35 @@ data class GenericRankingPlayer(
|
|||
val lastSeen: String
|
||||
)
|
||||
|
||||
interface GameApiController {
|
||||
@Serializable
|
||||
data class GenericMusicMeta(
|
||||
val name: String?,
|
||||
val ver: String,
|
||||
val notes: List<GenericNoteMeta>
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class GenericNoteMeta(
|
||||
val lv: Double,
|
||||
val lvId: Int
|
||||
)
|
||||
|
||||
abstract class GameApiController(name: String) {
|
||||
val musicMapping: Map<Int, GenericMusicMeta> = GameApiController::class.java
|
||||
.getResourceAsStream("/meta/$name/music.json")
|
||||
.use { it?.reader()?.readText() }
|
||||
?.let { JSON.decodeFromString<Map<String, GenericMusicMeta>>(it) }
|
||||
?.mapKeys { it.key.toInt() }
|
||||
?: emptyMap()
|
||||
|
||||
@API("trend")
|
||||
suspend fun trend(@RP username: String): List<TrendOut>
|
||||
abstract suspend fun trend(@RP username: String): List<TrendOut>
|
||||
@API("user-summary")
|
||||
suspend fun userSummary(@RP username: String): GenericGameSummary
|
||||
abstract suspend fun userSummary(@RP username: String): GenericGameSummary
|
||||
@API("ranking")
|
||||
suspend fun ranking(): List<GenericRankingPlayer>
|
||||
abstract suspend fun ranking(): List<GenericRankingPlayer>
|
||||
@API("playlog")
|
||||
suspend fun playlog(@RP id: Long): IGenericGamePlaylog
|
||||
abstract suspend fun playlog(@RP id: Long): IGenericGamePlaylog
|
||||
@API("recent")
|
||||
suspend fun recent(@RP username: String): List<IGenericGamePlaylog>
|
||||
abstract suspend fun recent(@RP username: String): List<IGenericGamePlaylog>
|
||||
}
|
|
@ -17,7 +17,7 @@ class Ongeki(
|
|||
val userPlaylogRepository: UserPlaylogRepository,
|
||||
val userDataRepository: UserDataRepository,
|
||||
val userGeneralDataRepository: UserGeneralDataRepository
|
||||
): GameApiController {
|
||||
): GameApiController("ongeki") {
|
||||
override suspend fun trend(username: String) = us.cardByName(username) { card ->
|
||||
findTrend(userPlaylogRepository.findByUser_Card_ExtId(card.extId)
|
||||
.map { TrendLog(it.playDate, it.playerRating) })
|
||||
|
|
|
@ -3,10 +3,7 @@ package icu.samnyan.aqua.net.utils
|
|||
import ext.isoDate
|
||||
import ext.millis
|
||||
import ext.minus
|
||||
import icu.samnyan.aqua.net.games.GenericGameSummary
|
||||
import icu.samnyan.aqua.net.games.GenericRankingPlayer
|
||||
import icu.samnyan.aqua.net.games.RankCount
|
||||
import icu.samnyan.aqua.net.games.TrendOut
|
||||
import icu.samnyan.aqua.net.games.*
|
||||
import icu.samnyan.aqua.sega.general.model.Card
|
||||
import org.springframework.data.jpa.repository.JpaRepository
|
||||
import org.springframework.data.repository.NoRepositoryBean
|
||||
|
@ -83,7 +80,7 @@ interface GenericPlaylogRepo {
|
|||
|
||||
fun List<IGenericGamePlaylog>.acc() = if (isEmpty()) 0.0 else sumOf { it.achievement }.toDouble() / size / 10000.0
|
||||
|
||||
fun genericUserSummary(
|
||||
fun GameApiController.genericUserSummary(
|
||||
card: Card,
|
||||
userDataRepo: GenericUserDataRepo<*, *>,
|
||||
userPlaylogRepo: GenericPlaylogRepo,
|
||||
|
@ -95,11 +92,23 @@ fun genericUserSummary(
|
|||
val user = userDataRepo.findByCard(card) ?: (404 - "Game data not found")
|
||||
val plays = userPlaylogRepo.findByUserCardExtId(card.extId)
|
||||
|
||||
// O(6n) ranks algorithm: Loop through the entire list of plays,
|
||||
// count the number of each rank
|
||||
val ranks = shownRanks.associate { (_, v) -> v to 0 }.toMutableMap()
|
||||
plays.forEach {
|
||||
shownRanks.find { (s, _) -> it.achievement > s }?.let { (_, v) -> ranks[v] = ranks[v]!! + 1 }
|
||||
// Detailed ranks: Find the number of each rank in each level category
|
||||
// map<level, map<rank, count>>
|
||||
val rankMap = shownRanks.associate { (_, v) -> v to 0 }
|
||||
val detailedRanks = HashMap<Int, MutableMap<String, Int>>()
|
||||
plays.forEach { play ->
|
||||
val lvl = musicMapping[play.musicId]?.notes?.getOrNull(if (play.level == 10) 0 else play.level)?.lv ?: return@forEach
|
||||
shownRanks.find { (s, _) -> play.achievement > s }?.let { (_, v) ->
|
||||
val ranks = detailedRanks.getOrPut(lvl.toInt()) { rankMap.toMutableMap() }
|
||||
ranks[v] = ranks[v]!! + 1
|
||||
}
|
||||
}
|
||||
|
||||
// Collapse detailed ranks to get non-detailed ranks map<rank, count>
|
||||
val ranks = shownRanks.associate { (_, v) -> v to 0 }.toMutableMap().also { ranks ->
|
||||
plays.forEach { play ->
|
||||
shownRanks.find { (s, _) -> play.achievement > s }?.let { (_, v) -> ranks[v] = ranks[v]!! + 1 }
|
||||
}
|
||||
}
|
||||
|
||||
return GenericGameSummary(
|
||||
|
@ -111,6 +120,7 @@ fun genericUserSummary(
|
|||
rating = user.playerRating,
|
||||
ratingHighest = user.highestRating,
|
||||
ranks = ranks.map { (k, v) -> RankCount(k, v) },
|
||||
detailedRanks = detailedRanks,
|
||||
maxCombo = plays.maxOfOrNull { it.maxCombo } ?: 0,
|
||||
fullCombo = plays.count { it.isFullCombo },
|
||||
allPerfect = plays.count { it.isAllPerfect },
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
#!/usr/bin/env bash
|
||||
curl "https://aquadx.net/d/ongeki/00/all-music.json" -o "ongeki/music.json"
|
||||
curl "https://aquadx.net/d/mai2/00/all-music.json" -o "mai2/music.json"
|
||||
curl "https://aquadx.net/d/chu3/00/all-music.json" -o "chu3/music.json"
|
Loading…
Reference in New Issue