Codewars style ranking system, CodeWars Kata 자바 솔루션
Codewars style ranking system, CodeWars Kata
Write a class called User that is used to calculate the amount that a user will progress through a ranking system similar to the one Codewars uses.
Business Rules:
- A user starts at rank -8 and can progress all the way to 8.
- There is no 0 (zero) rank. The next rank after -1 is 1.
- Users will complete activities. These activities also have ranks.
- Each time the user completes a ranked activity the users rank progress is updated based off of the activity's rank
- The progress earned from the completed activity is relative to what the user's current rank is compared to the rank of the activity
- A user's rank progress starts off at zero, each time the progress reaches 100 the user's rank is upgraded to the next level
- Any remaining progress earned while in the previous rank will be applied towards the next rank's progress (we don't throw any progress away). The exception is if there is no other rank left to progress towards (Once you reach rank 8 there is no more progression).
- A user cannot progress beyond rank 8.
- The only acceptable range of rank values is -8,-7,-6,-5,-4,-3,-2,-1,1,2,3,4,5,6,7,8. Any other value should raise an error.
The progress is scored like so:
- Completing an activity that is ranked the same as that of the user's will be worth 3 points
- Completing an activity that is ranked one ranking lower than the user's will be worth 1 point
- Any activities completed that are ranking 2 levels or more lower than the user's ranking will be ignored
- Completing an activity ranked higher than the current user's rank will accelerate the rank progression. The greater the difference between rankings the more the progression will be increased. The formula is
10 * d * d
whered
equals the difference in ranking between the activity and the user.
Logic Examples:
- If a user ranked -8 completes an activity ranked -7 they will receive 10 progress
- If a user ranked -8 completes an activity ranked -6 they will receive 40 progress
- If a user ranked -8 completes an activity ranked -5 they will receive 90 progress
- If a user ranked -8 completes an activity ranked -4 they will receive 160 progress, resulting in the user being upgraded to rank -7 and having earned 60 progress towards their next rank
- If a user ranked -1 completes an activity ranked 1 they will receive 10 progress (remember, zero rank is ignored)
Code Usage Examples:
User user = new User();
user.rank; // => -8
user.progress; // => 0
user.incProgress(-7);
user.progress; // => 10
user.incProgress(-5); // will add 90 progress
user.progress; // => 0 // progress is now zero
user.rank; // => -7 // rank was upgraded to -7
Note: In Java some methods may throw an IllegalArgumentException
.
Note: Codewars no longer uses this algorithm for its own ranking system. It uses a pure Math based solution that gives consistent results no matter what order a set of ranked activities are completed at.
문제 해석
일반적인 Kata들이 솔루션 메소드를 작성하는 문제인데 반해 이 문제는 클래스를 작성하는 문제입니다. 클래스명은 User
이고요. 아마도 이전에 코드워즈에서 사용한 적이 있는 랭킹 시스템을 일부변형하여 문제로 출제한 모양입니다.
User
클래스는 rank
와 progress
를 멤버 속성으로 가집니다. public
으로 접근 가능한 속성들입니다. 그리고 incProgress()
메소드를 멤버 메소드로 가집니다. 이 메소드는 유저가 Kata를 풀었을 때 현재 User의 레벨과 Kata의 레벨 차이를 통해서 progress
를 증가시켜주는 역할을 합니다. 문제풀이를 통해 progress
의 값이 100을 넘길 때마다 progress
는 0으로 초기화되고 rank
가 올라가게 됩니다. 따라서 progress
의 범위는 0~99가 됩니다.
랭크는 -8부터 8까지 범위를 가지고요. 8이 최고 레벨입니다. 최고 레벨이 되면 더이상 progress
가 증가하지 않습니다. 0레벨은 존재하지 않아서 -1 레벨 다음은 1레벨이 됩니다.
My Solution
먼저 클래스 템플릿을 작성해주고요. 필요한 멤버 속성과 멤버 메소드를 작성해줍니다. rank는 시작 랭크인 -8로 초기화 해줍니다.
랭크에 0랭크가 없기 때문에 배열 int[] rankTable
을 이용해서 처리했습니다. rankTable에 없는 랭크가 입력되면 오류를 일으켜야 합니다.
int incProgress(int rank)
에서는 먼저 주어진 문제를 풀었을 때 얻게 되는 포인트를 int calcPoint(int actRank)
를 통해서 계산했습니다. 그런 다음 상승시켜야 할 랭크를 int calcRankPlus(int point)
메소드를 통해서 구합니다. 현재 랭크에서 상승할 랭크를 더했을 때 최고레벨을 넘어 간다면 최고 레벨로 진급시킵니다. 그런 다음 progress
를 계산해 주었습니다.
의외로 고려해야 할 것들이 많은 문제였습니다. 최고 랭크 이후로는 progress가 더 이상 증가하지 않기 때문에 별도 처리해주어야 했고요. 레벨 증가 후 progress가 100 이하여야 하기 때문에 이 부분도 처리해야 합니다. 그리고 유저 rank를 임의로 조작한 후 incProgress를 다시 호출할 수도 있기 때문에 이 부분도 주의해야 합니다.
import java.util.Arrays;
public class User {
private int[] rankTable = new int[] { -8, -7, -6, -5, -4, -3, -2, -1, 1, 2, 3, 4, 5, 6, 7, 8 };
public int rank = -8;
public int progress;
public int incProgress(int rank) {
if (Arrays.binarySearch(rankTable, rank) < 0) {
int exc = rankTable[Arrays.binarySearch(rankTable, rank)];// on purpose raise exception;
}
int point = calcPoint(rank);
int rankPlus = calcRankPlus(point);
//this.progress = progress % 100;
this.rank = getRank(getRankIndex(this.rank) + rankPlus);
if(this.rank != rankTable[rankTable.length - 1]){
this.progress += point - (rankPlus * 100);
}
else{
this.progress = 0;
}
return this.rank;
}
private int getRank(int rankIndex) {
if(rankIndex > rankTable.length - 1){
return rankTable[rankTable.length - 1];
}
return rankTable[rankIndex];
}
private int calcRankPlus(int point) {
int remain = this.progress % 100;
return (remain + point) / 100;
}
public int calcPoint(int actRank){
int userIndex = getRankIndex(this.rank);
int actIndex = getRankIndex(actRank);
int diff = actIndex - userIndex;
int point = 0;
if(diff < -1){
point = 0;
}else if(diff == -1){
point = 1;
}else if(diff == 0){
point = 3;
}else{
point = 10 * diff * diff;
}
return point;
}
private int getRankIndex(int rank) {
return Arrays.binarySearch(rankTable, rank);
}
}
Best Practice
BP에서는 while문을 이용해서 progress 증가값을 해결했습니다. 이렇게 간단하게 해결될 수도 있군요.
import java.util.Arrays;
public class User {
private int[] ranks = {-8, -7, -6, -5, -4, -3, -2, -1, 1, 2, 3, 4, 5, 6, 7, 8};
private int curRank = 0;
public int progress = 0;
public int rank = -8;
public void incProgress(int kRank) {
kRank = Arrays.binarySearch(ranks, kRank);
if(kRank < 0) throw new RuntimeException("Invalid rank");
if(ranks[curRank] == 8) return;
if(kRank == curRank) progress += 3;
else if(kRank == curRank - 1) progress++;
else if(kRank > curRank) {
int diff = kRank - curRank;
progress += 10 * diff * diff;
}
while(progress >= 100) {
curRank++;
updateRank();
progress -= 100;
if(ranks[curRank] == 8) {
progress = 0;
return;
}
}
}
private void updateRank() {
rank = ranks[curRank];
}
}