Today's

길을 나서지 않으면 그 길에서 만날 수 있는 사람을 만날 수 없다

모바일 앱(안드로이드)

Kotlin으로 복식 경기 Round-Robin 매칭 구성하기

Billcorea 2025. 7. 29. 15:45
반응형

 

🎾 Kotlin으로 복식 경기 Round-Robin 매칭 구성하기

라운드 로빈 구현해 보기

 

이 글은 Kotlin과 Jetpack Compose를 사용하는 Android 앱에서 복식 경기 매칭을 어떻게 구성할 수 있는지 기록한 개발자 경험 공유입니다. 예제 코드는 초보자도 이해할 수 있도록 주석과 함께 설명합니다.

🧩 사용 시나리오

앱 사용자는 4명 이상의 참여자를 등록한 후, 복식 경기 방식으로 매칭을 자동 생성합니다.

  • 경기 방식은 Round-Robin 방식 (모든 가능한 조합을 구성)
  • 같은 팀 또는 상대가 중복되지 않도록 구성
  • 선수 수가 홀수인 경우 마지막 한 명을 제외
  • 참가자 수가 4명일 경우에도 다양한 팀 구성을 고려

🧠 핵심 데이터 구조


// 경기 참가자
data class Contestant(
    val pk: String,         // 고유 ID
    val tokenId: String,
    val enterTime: Long,
    val exitTime: Long
)

// 경기 매칭
data class Match(
    val pk: String,                  // "Round_0", "Round_1" 등
    val startTime: Long,
    val endTime: Long,
    val teamA: List,     // 2인 1조
    val teamB: List,
    val isDoubles: Boolean = true
)

⚙️ 매칭 로직 구현

복식 경기를 위한 모든 가능한 조합을 만들고, 이전 경기에 바로 참여한 선수는 다음 경기에서 쉬도록 구성합니다.


// 복식 팀 조합을 생성하는 함수
fun generateDoublesTeamPairs(players: List): List<List<Contestant>> {
    val validPlayers = if (players.size % 2 != 0) players.dropLast(1) else players
    val teamPairs = mutableListOf<List<Contestant>>()

    // 모든 2명 조합 생성
    for (i in validPlayers.indices) {
        for (j in i + 1 until validPlayers.size) {
            teamPairs.add(listOf(validPlayers[i], validPlayers[j]))
        }
    }

    return teamPairs
}

이후 이 조합으로 가능한 모든 2팀 매칭을 구성합니다.


// 복식 경기를 위한 가능한 Match 조합 생성
fun generateDoublesMatches(players: List<Contestant>): List<Match> {
    val validPlayers = if (players.size % 2 != 0) players.dropLast(1) else players
    val teamPairs = generateDoublesTeamPairs(validPlayers)
    val matchList = mutableListOf<Match>()
    var idx = 0

    val usedMatches = mutableSetOf<Set<String>>()

    for (i in teamPairs.indices) {
        for (j in i + 1 until teamPairs.size) {
            val teamA = teamPairs[i]
            val teamB = teamPairs[j]

            // 팀원이 겹치지 않는지 확인
            if (teamA.intersect(teamB.toSet()).isEmpty()) {
                val allPlayers = (teamA + teamB).map { it.pk }.toSet()

                // 동일한 구성의 경기인지 중복 확인
                if (allPlayers !in usedMatches) {
                    usedMatches.add(allPlayers)

                    // 실제 Match 객체 생성
                    val match = Match(
                        pk = "Round_${idx++}",
                        startTime = System.currentTimeMillis(),
                        endTime = System.currentTimeMillis() + 10 * 60 * 1000,
                        teamA = teamA,
                        teamB = teamB,
                        isDoubles = true
                    )

                    matchList.add(match)
                }
            }
        }
    }

    return matchList
}
✨ 위 함수는 4명이 참여했을 때도 3가지 가능한 조합 중 1경기로 끝나는 일이 없도록 팀 구성 중복 여부를 고려해 최소 2경기 이상이 생성될 수 있도록 보완하였습니다.

🔍 로그 출력 예시 (디버깅용)


Log.e("MatchGen", "Valid players (${validPlayers.size}): ${validPlayers.map { it.pk }}")
Log.e("MatchGen", "Generated ${teamPairs.size} teams")

for (match in matchList) {
    Log.e("MatchGen", "Match: ${match.teamA.map { it.pk }} vs ${match.teamB.map { it.pk }}")
}

✅ 결과 예시 (4명일 때)


Match: A, B vs C, D
Match: A, C vs B, D

📌 마무리

이 방식은 경기 운영 자동화라운드로빈 방식의 배치 자동화에 유용하며, 참여자 수의 변화에 따라 유연하게 대응할 수 있는 점이 큰 장점입니다.

필요에 따라 매칭 조건(예: 직전 경기 제외, 최소 휴식 시간 보장 등)을 더 정교하게 설계해 나갈 수 있습니다.


문의나 개선 아이디어가 있다면 댓글로 남겨주세요! 😊

 

어디까지나... chatGPT 의 의견은 아직 100% 신뢰도를 보장 하지 않습니다. 

반응형