Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
9e5cb52
feat(Daily Challenge Leaderboard): Added the DailyChallengeLeaderboar…
vvivek1801 Dec 31, 2022
c8ef61f
fixed the changing of core files
vvivek1801 Jan 1, 2023
6e63e65
update: Added pagination parameters to dc-leaderboard in open-api
Ram-20062003 Jan 2, 2023
943eba5
fix(): Fixed the type of score(dc)
vvivek1801 Jan 2, 2023
d67a1ea
fix(lint issues): fixed lint issues
vvivek1801 Jan 2, 2023
9037a5d
fix(Removed Unnecessary Files): Removed Entity and Repository files
vvivek1801 Jan 5, 2023
a093eb9
feat(Auto Matches): Added auto matches functionality and rating
vvivek1801 Feb 9, 2023
2b5d05c
feat(Daily Challenge Leaderboard): Added the DailyChallengeLeaderboar…
vvivek1801 Dec 31, 2022
a8062f6
feat(Daily Challenge Leaderboard): Added the DailyChallengeLeaderboar…
vvivek1801 Dec 31, 2022
c45cc47
fix(lint fixes): Fixed lint issues
vvivek1801 Feb 19, 2023
d4a9b40
update(removed tries): tries attribute was removed from automatch repo
vvivek1801 Feb 19, 2023
80d3568
feat(Daily Challenge Leaderboard): Added the DailyChallengeLeaderboar…
vvivek1801 Dec 31, 2022
408fbc1
feat(Daily Challenge Leaderboard): Added the DailyChallengeLeaderboar…
vvivek1801 Dec 31, 2022
9e91984
feat(Daily Challenge Leaderboard): Added the DailyChallengeLeaderboar…
vvivek1801 Dec 31, 2022
7e02532
fix: format
vvivek1801 Feb 20, 2023
bf39f38
fix: Removed unnecessary folders
vvivek1801 Feb 21, 2023
61e6ba5
update: added mode param to create dual match
vvivek1801 Feb 26, 2023
5d2eedb
update: added tried to automatches
vvivek1801 Mar 1, 2023
fc45240
fix: format and api fixes
vvivek1801 Mar 1, 2023
cfd79aa
fix: name of top n users function in public user service
vvivek1801 Mar 1, 2023
77845b9
update: added tier check in auto match rating changes
vvivek1801 Mar 1, 2023
87be203
fix: promote-demote-time to every 6 hours
vvivek1801 Mar 1, 2023
aadb9e8
fix: rating of automatches fix
vvivek1801 Mar 1, 2023
23eaff5
fix: top-n-players, add logging and other fixes
Ram-20062003 Mar 3, 2023
f6af6bd
fix: auto-matches for battleTV
Ram-20062003 Mar 4, 2023
8080c8a
fix: logging
Ram-20062003 Mar 4, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .runConfigurations/Format.run.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@
<DebugAllEnabled>false</DebugAllEnabled>
<method v="2" />
</configuration>
</component>
</component>
2 changes: 2 additions & 0 deletions docs/spec/CodeCharacter-API.yml
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@ paths:
description: Leaderboard Tier
description: Get leaderboard
parameters: []

/top-matches:
get:
summary: Get top matches
Expand Down Expand Up @@ -1934,6 +1935,7 @@ components:
- user
- stats
description: Leaderboard entry model

DailyChallengeGetRequest:
title: Get daily challenge
description: Get current-user daily challenge
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,24 @@ import io.swagger.v3.oas.annotations.media.*
import io.swagger.v3.oas.annotations.responses.*
import io.swagger.v3.oas.annotations.security.*
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity

import org.springframework.web.bind.annotation.*
import org.springframework.validation.annotation.Validated
import org.springframework.web.context.request.NativeWebRequest
import org.springframework.beans.factory.annotation.Autowired

import jakarta.validation.constraints.*
import jakarta.validation.Valid
import jakarta.validation.constraints.DecimalMax
import jakarta.validation.constraints.DecimalMin
import jakarta.validation.constraints.Email
import jakarta.validation.constraints.Max
import jakarta.validation.constraints.Min
import jakarta.validation.constraints.NotNull
import jakarta.validation.constraints.Pattern
import jakarta.validation.constraints.Size
import java.util.Date

import kotlin.collections.List
import kotlin.collections.Map

@Validated
@RequestMapping("\${api.base-path:}")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package delta.codecharacter.server.match

import org.springframework.data.annotation.Id
import org.springframework.data.mongodb.core.mapping.Document
import java.util.UUID

@Document(collection = "auto_match")
data class AutoMatchEntity(@Id val matchId: UUID, val tries: Int)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package delta.codecharacter.server.match

import org.springframework.data.mongodb.repository.MongoRepository
import org.springframework.stereotype.Repository
import java.util.UUID

@Repository interface AutoMatchRepository : MongoRepository<AutoMatchEntity, UUID>
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ import java.util.UUID
interface MatchRepository : MongoRepository<MatchEntity, UUID> {
fun findTop10ByOrderByTotalPointsDesc(): List<MatchEntity>
fun findByPlayer1OrderByCreatedAtDesc(player1: PublicUserEntity): List<MatchEntity>
fun findByIdIn(matchIds: List<UUID>): List<MatchEntity>
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import delta.codecharacter.server.logic.verdict.VerdictAlgorithm
import delta.codecharacter.server.notifications.NotificationService
import delta.codecharacter.server.user.public_user.PublicUserService
import delta.codecharacter.server.user.rating_history.RatingHistoryService
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.amqp.rabbit.annotation.RabbitListener
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.HttpStatus
Expand Down Expand Up @@ -59,9 +61,11 @@ class MatchService(
@Autowired private val dailyChallengeMatchRepository: DailyChallengeMatchRepository,
@Autowired private val jackson2ObjectMapperBuilder: Jackson2ObjectMapperBuilder,
@Autowired private val simpMessagingTemplate: SimpMessagingTemplate,
@Autowired private val mapValidator: MapValidator
@Autowired private val mapValidator: MapValidator,
@Autowired private val autoMatchRepository: AutoMatchRepository
) {
private var mapper: ObjectMapper = jackson2ObjectMapperBuilder.build()
private val logger: Logger = LoggerFactory.getLogger(MatchService::class.java)

private fun createSelfMatch(userId: UUID, codeRevisionId: UUID?, mapRevisionId: UUID?) {
val code: String
Expand Down Expand Up @@ -107,19 +111,13 @@ class MatchService(
gameService.sendGameRequest(game, code, LanguageEnum.valueOf(language.name), map)
}

fun createDualMatch(userId: UUID, opponentUsername: String) {
fun createDualMatch(userId: UUID, opponentUsername: String, mode: MatchModeEnum): UUID {
val publicUser = publicUserService.getPublicUser(userId)
val publicOpponent = publicUserService.getPublicUserByUsername(opponentUsername)
val opponentId = publicOpponent.userId

if (userId == opponentId) {
throw CustomException(HttpStatus.BAD_REQUEST, "You cannot play against yourself")
}
if (publicOpponent.tier == TierTypeDto.TIER1) {
throw CustomException(
HttpStatus.BAD_REQUEST, "Opponent cannot be a tier 1 player in manual match"
)
}
val (userLanguage, userCode) = lockedCodeService.getLockedCode(userId)
val userMap = lockedMapService.getLockedMap(userId)

Expand All @@ -135,7 +133,7 @@ class MatchService(
MatchEntity(
id = matchId,
games = listOf(game1, game2),
mode = MatchModeEnum.MANUAL,
mode = mode,
verdict = MatchVerdictEnum.TIE,
createdAt = Instant.now(),
totalPoints = 0,
Expand All @@ -146,6 +144,12 @@ class MatchService(

gameService.sendGameRequest(game1, userCode, userLanguage, opponentMap)
gameService.sendGameRequest(game2, opponentCode, opponentLanguage, userMap)
if (mode == MatchModeEnum.AUTO) {
logger.info(
"Auto match started between ${match.player1.username} and ${match.player2.username}"
)
}
return matchId
}

fun createDCMatch(userId: UUID, dailyChallengeMatchRequestDto: DailyChallengeMatchRequestDto) {
Expand Down Expand Up @@ -198,14 +202,31 @@ class MatchService(
if (createMatchRequestDto.opponentUsername == null) {
throw CustomException(HttpStatus.BAD_REQUEST, "Opponent ID is required")
}
createDualMatch(userId, createMatchRequestDto.opponentUsername!!)
createDualMatch(userId, createMatchRequestDto.opponentUsername!!, MatchModeEnum.MANUAL)
}
else -> {
throw CustomException(HttpStatus.BAD_REQUEST, "MatchMode Is Not Correct")
}
}
}

fun createAutoMatch() {
val topNUsers = publicUserService.getTopNUsers()
val userIds = topNUsers.map { it.userId }
val usernames = topNUsers.map { it.username }
logger.info("Auto matches started for users: $usernames")
autoMatchRepository.deleteAll()
userIds.forEachIndexed { i, userId ->
run {
for (j in i + 1 until userIds.size) {
val opponentUsername = usernames[j]
val matchId = createDualMatch(userId, opponentUsername, MatchModeEnum.AUTO)
autoMatchRepository.save(AutoMatchEntity(matchId, 0))
}
}
}
}

private fun mapMatchEntitiesToDtos(matchEntities: List<MatchEntity>): List<MatchDto> {
return matchEntities.map { matchEntity ->
MatchDto(
Expand Down Expand Up @@ -285,7 +306,10 @@ class MatchService(

fun getUserMatches(userId: UUID): List<MatchDto> {
val publicUser = publicUserService.getPublicUser(userId)
val matches = matchRepository.findByPlayer1OrderByCreatedAtDesc(publicUser)
val matches =
matchRepository.findByPlayer1OrderByCreatedAtDesc(publicUser).filter { match ->
match.mode != MatchModeEnum.AUTO
}
val dcMatches =
dailyChallengeMatchRepository.findByUserOrderByCreatedAtDesc(publicUser).takeWhile {
Duration.between(it.createdAt, Instant.now()).toHours() < 24 &&
Expand Down Expand Up @@ -318,6 +342,20 @@ class MatchService(
game.status == GameStatusEnum.EXECUTED || game.status == GameStatusEnum.EXECUTE_ERROR
}
) {

if (match.mode == MatchModeEnum.AUTO) {
if (match.games.any { game -> game.status == GameStatusEnum.EXECUTE_ERROR }) {
val autoMatch = autoMatchRepository.findById(match.id).get()
if (autoMatch.tries < 2) {
autoMatchRepository.delete(autoMatch)
val newMatchId =
createDualMatch(match.player1.userId, match.player2.username, MatchModeEnum.AUTO)
autoMatchRepository.save(AutoMatchEntity(newMatchId, autoMatch.tries + 1))
return
}
}
}

val player1Game = match.games.first()
val player2Game = match.games.last()
val verdict =
Expand All @@ -332,22 +370,21 @@ class MatchService(
val finishedMatch = match.copy(verdict = verdict)
val (newUserRating, newOpponentRating) =
ratingHistoryService.updateRating(match.player1.userId, match.player2.userId, verdict)
if (!(match.mode == MatchModeEnum.MANUAL && (match.player1.tier == TierTypeDto.TIER1))) {

publicUserService.updatePublicRating(
userId = match.player1.userId,
isInitiator = true,
verdict = verdict,
newRating = newUserRating
)
publicUserService.updatePublicRating(
userId = match.player2.userId,
isInitiator = false,
verdict = verdict,
newRating = newOpponentRating
)
}
if (match.mode == MatchModeEnum.MANUAL) {
if (match.player1.tier == TierTypeDto.TIER2 && match.player2.tier == TierTypeDto.TIER2) {
publicUserService.updatePublicRating(
userId = match.player1.userId,
isInitiator = true,
verdict = verdict,
newRating = newUserRating
)
publicUserService.updatePublicRating(
userId = match.player2.userId,
isInitiator = false,
verdict = verdict,
newRating = newOpponentRating
)
}
notificationService.sendNotification(
match.player1.userId,
"Match Result",
Expand All @@ -360,8 +397,37 @@ class MatchService(
} against ${match.player2.username}",
)
}

matchRepository.save(finishedMatch)

if (match.mode == MatchModeEnum.AUTO) {
if (autoMatchRepository.findAll().all { autoMatch ->
matchRepository.findById(autoMatch.matchId).get().games.all { game ->
game.status == GameStatusEnum.EXECUTED || game.status == GameStatusEnum.EXECUTE_ERROR
}
}
) {
val matches =
matchRepository.findByIdIn(autoMatchRepository.findAll().map { it.matchId })
val userIds =
matches.map { it.player1.userId }.toSet() +
matches.map { it.player2.userId }.toSet()
val newRatings =
ratingHistoryService.updateAndGetAutoMatchRatings(userIds.toList(), matches)
newRatings.forEach { (userId, newRating) ->
publicUserService.updatePublicRating(
userId = userId,
isInitiator = true,
verdict = verdict,
newRating = newRating.rating
)
}
logger.info("LeaderBoard Tier Promotion and Demotion started")
publicUserService.promoteTiers()
}
logger.info(
"Match between ${match.player1.username} and ${match.player2.username} completed with verdict $verdict"
)
}
}
} else if (dailyChallengeMatchRepository.findById(matchId).isPresent) {
val match = dailyChallengeMatchRepository.findById(matchId).get()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package delta.codecharacter.server.schedulers

import delta.codecharacter.server.match.MatchService
import delta.codecharacter.server.user.public_user.PublicUserService
import org.slf4j.Logger
import org.slf4j.LoggerFactory
Expand All @@ -8,7 +9,10 @@ import org.springframework.scheduling.annotation.Scheduled
import org.springframework.stereotype.Service

@Service
class SchedulingService(@Autowired private val publicUserService: PublicUserService) {
class SchedulingService(
@Autowired private val publicUserService: PublicUserService,
@Autowired private val matchService: MatchService
) {
private val logger: Logger = LoggerFactory.getLogger(SchedulingService::class.java)

@Scheduled(cron = "\${environment.registration-time}", zone = "GMT+5:30")
Expand All @@ -17,9 +21,9 @@ class SchedulingService(@Autowired private val publicUserService: PublicUserServ
publicUserService.resetRatingsAfterPracticePhase()
publicUserService.updateLeaderboardAfterPracticePhase()
}
@Scheduled(cron = "\${environment.update-time}", zone = "GMT+5:30")
fun promoteAndDemoteUserTiers() {
logger.info("LeaderBoard Tier Promotion and Demotion started")
publicUserService.promoteTiers()

@Scheduled(cron = "\${environment.promote-demote-time}", zone = "GMT+5:30")
fun createAutoMatch() {
matchService.createAutoMatch()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,7 @@ import java.util.UUID
interface PublicUserRepository : MongoRepository<PublicUserEntity, UUID> {
fun findByUsername(username: String): Optional<PublicUserEntity>

fun findTopnByOrderByRatingDesc(pageRequest: PageRequest): List<PublicUserEntity>

fun findAllByTier(tier: TierTypeDto?, pageRequest: PageRequest): List<PublicUserEntity>
}
Original file line number Diff line number Diff line change
Expand Up @@ -282,4 +282,9 @@ class PublicUserService(@Autowired private val publicUserRepository: PublicUserR
val updatedUser = user.copy(score = user.score + score, dailyChallengeHistory = current)
publicUserRepository.save(updatedUser)
}

fun getTopNUsers(): List<PublicUserEntity> {
val pageRequest = PageRequest.of(0, tier1Players.toInt(), Sort.by("rating"))
return publicUserRepository.findTopnByOrderByRatingDesc(pageRequest)
}
}
Loading