점수 산출 방식
야추 다이스 보드게임은 플레이어가 굴린 5개의 주사위의 눈금에 따라 점수를 얻을 수 있다.
이 때 점수 산출 방식은 야추 다이스 보드게임만의 특별한 룰에 따른다.
야추 다이스의 점수 산출 방식은 다음 이미지와 링크를 참고한다.
야추 다이스 점수 산출 방식
따라서 해당 주사위 눈금에서 산출될 수 있는 모든 점수의 값을 구하고,
플레이어가 그 중에서 원하는 점수로 기록할 수 있도록 만들어야 한다.
점수 산출 알고리즘
YachtGame Class
class YachtGame @Inject constructor() {
private val smalls = arrayOf(listOf(1,2,3,4), listOf(2,3,4,5), listOf(3,4,5,6))
private val larges = arrayOf(listOf(1,2,3,4,5), listOf(2,3,4,5,6))
fun getScore(...) : ..
fun getRankScore(...) : ..
suspend fun rollDice(..) : ..
}
점수를 산출할 수 있는 프로퍼티 및 메서드를 가진 클래스
suspend fun rollDice(Array<Int>, Array<Boolean>) : Array<Int>
suspend fun rollDice(dices:Array<Int>, keepIdx:Array<Boolean>) : Array<Int> = withContext(Dispatchers.Default) {
//val random = SecureRandom.getInstanceStrong()
val list1 = listOf(1,2,3,4,5,6)
val list2 = listOf(1,2,3,4,5,6)
val list3 = listOf(1,2,3,4,5,6)
val list4 = listOf(1,2,3,4,5,6)
val list5 = listOf(1,2,3,4,5,6)
val array = arrayOf(
if(!keepIdx[0]) list1.shuffled().first() else dices[0],
if(!keepIdx[1]) list2.shuffled().first() else dices[1],
if(!keepIdx[2]) list3.shuffled().first() else dices[2],
if(!keepIdx[3]) list4.shuffled().first() else dices[3],
if(!keepIdx[4]) list5.shuffled().first() else dices[4])
array
}
함수의 반환형은 Array이며, dices와 keepIdx 두 개의 매개변수를 받습니다. dices는 주사위의 현재 값이 저장된 배열이고, keepIdx는 어떤 주사위를 굴릴 것인지 선택하는 배열입니다.
withContext 함수를 사용하여 코루틴의 실행 컨텍스트를 지정합니다. 여기서는 Dispatchers.Default를 사용하여 백그라운드 스레드에서 실행되도록 합니다.
그 다음, list1부터 list5까지 1에서 6까지의 숫자를 포함하는 리스트를 만듭니다. 이 리스트들은 나중에 주사위를 굴릴 때 사용됩니다.
다음으로, array 배열을 만듭니다. 이 배열의 요소는 다음과 같이 만듭니다.
keepIdx[0]이 false이면 list1을 섞은 뒤 첫 번째 값을, true이면 dices[0]을 사용합니다.
keepIdx[1]이 false이면 list2을 섞은 뒤 첫 번째 값을, true이면 dices[1]을 사용합니다.
keepIdx[2]이 false이면 list3을 섞은 뒤 첫 번째 값을, true이면 dices[2]을 사용합니다.
keepIdx[3]이 false이면 list4을 섞은 뒤 첫 번째 값을, true이면 dices[3]을 사용합니다.
keepIdx[4]이 false이면 list5을 섞은 뒤 첫 번째 값을, true이면 dices[4]을 사용합니다.
최종적으로 array를 반환합니다.
따라서 이 함수는 keepIdx 배열에 따라 일부 주사위의 값을 유지하거나 새로운 값을 생성하여 반환합니다.
fun getScore(Array<Int>) : Board // fun getRankScore(Array<Int>, Map<Int, Int>) : IntArray
해당 함수들은 게임에서 주어진 다이스를 이용하여 각각의 보드 점수를 계산하여 반환하는 함수입니다.
fun getScore(dices:Array<Int>) : Board {
val numberCnt = dices.groupingBy { it }.eachCount()
val ranks = getRankScore(dices, numberCnt)
val result = IntArray(15){0}
for((key,value) in numberCnt.toSortedMap(compareBy{ it })) {
result[key-1] = (value*key)
}
result[8] = ranks[0]//choice
result[9] = ranks[1]//fourCard
result[10] = ranks[2]//fullHouse
result[11] = ranks[3]//s.straight
result[12] = ranks[4]//l.straight
result[13] = ranks[5]//yacht
//idx 6 = sum , idx 7 = bonus , idx 14 = total
return Board(result)
}
- getScore 함수: 다이스를 인자로 받아 각각의 보드 점수를 계산하여 반환하는 함수입니다.
- 숫자 카운트를 구합니다.
- getRankScore 함수를 호출하여 각 랭크 점수를 계산합니다.
- 1부터 6까지 각각의 숫자에 대해 등장 횟수를 곱한 값을 배열에 저장합니다.
- 8~13 인덱스에 getRankScore 함수에서 계산한 랭크 점수를 저장합니다.
- 6번 인덱스에는 1~6까지의 숫자 합을 저장합니다.
- 7번 인덱스에는 63점 이상인 경우 35점을, 그렇지 않은 경우 0점을 저장합니다.
- 14번 인덱스에는 모든 점수의 합을 저장합니다.
- 계산된 보드를 반환합니다.
private fun getRankScore(dices: Array<Int>, numberCnt: Map<Int, Int>): IntArray {
var ranks = intArrayOf()
//1.chocie
ranks += dices.sumOf { it }
//2.4 of a kind
val kind = numberCnt.filter { it.value >= 4 }.takeIf { it.isNotEmpty() }?.map { it.key }?.first() ?: 0
ranks += if (kind > 0) {
val notKind = dices.filter{ it != kind }.takeIf { it.isNotEmpty() }?.first() ?: kind
(kind*4) + notKind
} else 0
//3.full house
val triple = numberCnt.filter { it.value == 3}.takeIf { it.isNotEmpty() }?.map { it.key }?.first() ?: 0
val twoPair = numberCnt.filter { it.value == 2}.takeIf { it.isNotEmpty() }?.map{it.key}?.first() ?: 0
ranks += if(triple>0 && twoPair >0) triple*3 + twoPair*2 else 0
//4.small Straight
val s = dices.sorted().takeIf { it.containsAll(smalls[0]) || it.containsAll(smalls[1]) || it.containsAll(smalls[2])} !=null
ranks += if(s) 15 else 0
//5.large Straight
val l = dices.sorted().takeIf { it.containsAll(larges[0]) || it.containsAll(larges[1]) } != null
ranks += if(l) 30 else 0
//6.yacht
val yacht = numberCnt.filter { it.value == 5}.takeIf { it.isNotEmpty() } != null
ranks += if(yacht) 50 else 0
return ranks
}
- getRankScore 함수: 다이스와 각 숫자별 등장 횟수를 받아서 보드 점수 중에서 랭크 점수를 계산하여 반환하는 함수입니다.
- choice: 다이스의 모든 값을 더한 값을 반환합니다.
- 4 of a kind: 다이스 중 4개의 값이 모두 같은 경우 4개 값의 합과 다른 값 하나를 더한 값을 반환합니다.
- full house: 다이스 중 3개의 값이 같고 2개의 값이 같은 경우 3개 값과 2개 값의 합을 반환합니다.
- small straight: 다이스의 값 중 작은 스트레이트(1,2,3,4 또는 2,3,4,5)가 있으면 15를 반환합니다.
- large straight: 다이스의 값 중 큰 스트레이트(1,2,3,4,5 또는 2,3,4,5,6)가 있으면 30을 반환합니다.
- yacht: 다이스 중 모든 값이 같은 경우 50을 반환합니다.
실행
fun main() {
val yachtGame = YachtGame()
var keepList = listOf<Int>()
for(chance in 1..3) {
keepList = yachtGame.rollDice(keepList, 5-keepList.size, chance)
}
println(yachtGame.solution(keepList))
}
Keep 기능을 간단하게 구현하여 위의 클래스를 실행한 결과
[6, 2, 2, 2, 4]
keep하고 싶은 주사위의 개수와 idx를 입력
keep하고 싶은 주사위의 개수 : 3
0 1 2
[6, 2, 2, 6, 4]
keep하고 싶은 주사위의 개수와 idx를 입력
keep하고 싶은 주사위의 개수 : 4
0 1 2 3
[6,2,2,6,1]
1: 1
2 : 4
6 : 12
choice : 17
four of a kind : 0
full house : 0
s.Straight : 0
l.Straight : 0
yacht : 0
소스 코드
class YachtGame @Inject constructor() {
private val smalls = arrayOf(listOf(1,2,3,4), listOf(2,3,4,5), listOf(3,4,5,6))
private val larges = arrayOf(listOf(1,2,3,4,5), listOf(2,3,4,5,6))
/**
* 주사위를 k개를 굴려 나온 숫자들을 list(+keep)에 담아서 반환.
* */
suspend fun rollDice(dices:Array<Int>, keepIdx:Array<Boolean>) : Array<Int> = withContext(Dispatchers.Default) {
//val random = SecureRandom.getInstanceStrong()
val list1 = listOf(1,2,3,4,5,6)
val list2 = listOf(1,2,3,4,5,6)
val list3 = listOf(1,2,3,4,5,6)
val list4 = listOf(1,2,3,4,5,6)
val list5 = listOf(1,2,3,4,5,6)
val array = arrayOf(
if(!keepIdx[0]) list1.shuffled().first() else dices[0],
if(!keepIdx[1]) list2.shuffled().first() else dices[1],
if(!keepIdx[2]) list3.shuffled().first() else dices[2],
if(!keepIdx[3]) list4.shuffled().first() else dices[3],
if(!keepIdx[4]) list5.shuffled().first() else dices[4])
array
}
fun getScore(dices:Array<Int>) : Board {
val numberCnt = dices.groupingBy { it }.eachCount()
val ranks = getRankScore(dices, numberCnt)
val result = IntArray(15){0}
for((key,value) in numberCnt.toSortedMap(compareBy{ it })) {
result[key-1] = (value*key)
}
result[8] = ranks[0]//choice
result[9] = ranks[1]//fourCard
result[10] = ranks[2]//fullHouse
result[11] = ranks[3]//s.straight
result[12] = ranks[4]//l.straight
result[13] = ranks[5]//yacht
//idx 6 = sum , idx 7 = bonus , idx 14 = total
return Board(result)
}
private fun getRankScore(dices: Array<Int>, numberCnt: Map<Int, Int>): IntArray {
var ranks = intArrayOf()
//1.chocie
ranks += dices.sumOf { it }
//2.4 of a kind
val kind = numberCnt.filter { it.value >= 4 }.takeIf { it.isNotEmpty() }?.map { it.key }?.first() ?: 0
ranks += if (kind > 0) {
val notKind = dices.filter{ it != kind }.takeIf { it.isNotEmpty() }?.first() ?: kind
(kind*4) + notKind
} else 0
//3.full house
val triple = numberCnt.filter { it.value == 3}.takeIf { it.isNotEmpty() }?.map { it.key }?.first() ?: 0
val twoPair = numberCnt.filter { it.value == 2}.takeIf { it.isNotEmpty() }?.map{it.key}?.first() ?: 0
ranks += if(triple>0 && twoPair >0) triple*3 + twoPair*2 else 0
//4.small Straight
val s = dices.sorted().takeIf { it.containsAll(smalls[0]) || it.containsAll(smalls[1]) || it.containsAll(smalls[2])} !=null
ranks += if(s) 15 else 0
//5.large Straight
val l = dices.sorted().takeIf { it.containsAll(larges[0]) || it.containsAll(larges[1]) } != null
ranks += if(l) 30 else 0
//6.yacht
val yacht = numberCnt.filter { it.value == 5}.takeIf { it.isNotEmpty() } != null
ranks += if(yacht) 50 else 0
return ranks
}
}
.
'개발 > 야추 다이스 (Yacht Dice)' 카테고리의 다른 글
게임 효과음 구현 (0) | 2023.03.14 |
---|---|
주사위에 Animation 구현해서 굴림 효과 만들기. (0) | 2023.03.13 |
게임 보드판 구현하기 (1) | 2023.03.13 |