오늘은 선택한 카드를 기반으로 족보를 확인하고 Chip과 Mult를 활용한 카드 계산 구현에 대한 내용을 정리해보겠습니다.
먼저 카드 계산에 대한 흐름은 다음과 같이 기획하였습니다.
1. 선택한 카드가 존재할 경우에만 카드 계산이 이루어지도록 한다.
2. 카드 계산이 동작하면 카드 계산이 실질적으로 이루어질 함수를 호출한다.
3. 족보별 설정되는 기본 Chip, Mult를 계산한다.
4. 해당 족보를 완성하는데 기여한 카드를 기반으로 각 카드의 Chip을 계산한다.
5. 조커슬롯에 배치된 조커 효과를 기반으로 Chip, Mult를 추가한다.
6. 최종 추가된 Chip * Mult를 TotalScore에 추가하고 Round별 TargetScore와 비교하여 승/패 판정을 진행한다.
다음 카드 계산에 있어 중요한 선택한 카드 기반 족보를 판정할 기준을 다음과 같이 기획하였습니다.
1. 선택된 카드를 기준으로 2중 for문을 돌려 i와 j가 동일한지(PairCount)파악하고 숫자를 늘린다.
2. PairCount가 있을 경우 Pair관련 족보(원페어, 투페어, 트리플, 포카드, 풀하우스)계산 함수를 호출한다.
2-1. PairCount가(1, 2, 3, 4, 6)에 따라 족보가 나뉘기 때문에 Switch를 이용하여 구분한다.
3. PairCount가 없을 경우 UnPair관련 족보(하이카드, 스트레이트, 플러쉬, 스트레이트 플러쉬) 계산 함수를 호출한다.
3-1. 선택한 카드들을 랭크순으로 정렬한다.
3-1. 이후 특정 조건에 충족하는지 확인한다.
- (selectCards[selectCards.Count -1] - selectCards[0] =4) : 스트레이트 조건
- (selectCards[0].suit와 동일여부) : 플러쉬 조건
- 아무것도 해당되지 않음 : 하이카드
- 모두 해당됨: 스트레이트 플러쉬
4. 족보 판정이 완료되면 족보 판정에 기여한 카드들을 scoreCards에 저장한다.(추후, 카드별 칩 계산에 사용)
기획한 흐름을 기반으로 작성한 코드는 다음과 같습니다.
CardCalculator.cs
<style>
using System;
using UnityEngine;
public class CardCalculator
{
public int chip;
public float mult;
public int currentScore;
public int totalScore = 0;
public static event Action<int> scoreChanged;
public static event Action<int, float> chipChanged;
public void CalculateScore(HandEvaluator handEvaluator)
{
//Debug.Log($"CalculateScore, chip: {chip}, mult: {mult}");
JokerScoreRecord score = new JokerScoreRecord(chip, mult);
currentScore = 0;
if (handEvaluator == null) {
Debug.Log("handEvaluator가 없습니다.");
return;
}
if (!RoundManager.Instance.ConsumeHandCount()) return;
SetHandBaseScore(handEvaluator.handRanking);
CheckCardChip(handEvaluator);
JokerSlot jokerSlot = UnityEngine.Object.FindAnyObjectByType<JokerSlot>();
if(jokerSlot != null ) jokerSlot.CalculateJokerEffect(score);
float calculateScore = (float)(score.totalChip * score.totalMult);
//Debug.Log($"CalculateScore, calculateScore : {calculateScore}");
currentScore = Mathf.RoundToInt(calculateScore);
//Debug.Log($"CalculateScore, currentScore : {totalScore}");
totalScore += currentScore;
//Debug.Log($"CalculateScore, totalScore : {totalScore}");
UpdateScoreUI();
RoundManager.Instance.IsReachedTargetScore();
}
public void CheckCardChip(HandEvaluator handEvaluator)
{
if (handEvaluator == null)
{
Debug.Log("CehckCardChip, handEvaluator가 존재하지 않음");
return;
}
if (handEvaluator.scoreCards == null)
{
Debug.Log("CehckCardChip, scoreCard가 존재하지 않음");
return;
}
for (int i = 0; i < handEvaluator.scoreCards.Count; i++)
{
//Debug.Log($"CheckCardChip : {handEvaluator.scoreCards[i].baseChip}");
chip += handEvaluator.scoreCards[i].baseChip;
}
}
public void SetHandBaseScore(HandRanking cardRanking)
{
chip = 0;
mult = 0;
if (cardRanking == HandRanking.None) return;
switch(cardRanking){
case HandRanking.HighCard: chip = chip + 5; mult = 1; break;
case HandRanking.OnePair: chip = chip + 10; mult = 2; break;
case HandRanking.TwoPair: chip = chip + 20; mult = 2; break;
case HandRanking.Triple: chip = chip + 30; mult = 3; break;
case HandRanking.Straight: chip = chip + 30; mult = 4; break;
case HandRanking.Flush: chip = chip + 35; mult = 4; break;
case HandRanking.FullHouse: chip = chip + 40; mult = 4; break;
case HandRanking.FourCard: chip = chip + 60; mult = 7; break;
case HandRanking.StraightFlush: chip = chip + 100; mult = 8; break;
}
UpdateChipAndMult();
//Debug.Log($" SetHandBaseScore: Ranking: {cardRanking}, Chip: {chip}, Mult: {mult}");
// 족보별 애니메이션 처리(chip, mult 세팅)
}
private void UpdateScoreUI()
{
scoreChanged?.Invoke(totalScore);
}
private void UpdateChipAndMult()
{
chipChanged?.Invoke(chip, mult);
}
}
</style>
HandEvaluator.cs
<style>
using System;
using System.Collections.Generic;
using UnityEngine;
public class HandEvaluator
{
//bool hasFourJoker = false;
public HandRanking handRanking = HandRanking.None;
public List<PlayCard> scoreCards = new List<PlayCard>();
public CardCalculator cardCalculator;
public static event Action<HandRanking> rankingChanged;
public void CalculatePairCount(List<PlayCard> selectCardList)
{
//Debug.Log($"CalculatePairCount, {selectCardList}");
scoreCards.Clear();
int pairCount = 0;
List<PlayCard> card = selectCardList;
//Debug.Log($"CalculatePairCount, {card}");
if (card == null || card.Count == 0)
{
handRanking = HandRanking.None;
return;
}
for (int i = 0; i < card.Count; i++)
{
for (int j = i + 1; j < card.Count; j++)
{
if (card[i].rank == card[j].rank)
{
pairCount++;
if (!scoreCards.Contains(card[i])) scoreCards.Add(card[i]);
if (!scoreCards.Contains(card[j])) scoreCards.Add(card[j]);
}
}
}
Debug.Log($"CalculatePairCount, pairCount: {pairCount}");
if (pairCount == 0)
{
EvaluateNotPairRanking(card);
}
else
{
EvaluatePairRanking(pairCount);
}
UpdateRankingUI();
cardCalculator.SetHandBaseScore(handRanking);
}
public void EvaluateNotPairRanking(List<PlayCard> card)
{
scoreCards.Clear();
card.Sort((a, b) => a.rank.CompareTo(b.rank));
bool isStraight = false;
bool isMountain = false;
bool isFlush = false;
// Straight, Flush 충족 여부
if (card.Count == 5)
{
isStraight = card[card.Count - 1].rank - card[0].rank == 4 ? true : false;
isMountain = (card[0].rank == 1 && card[1].rank == 10 && card[4].rank == 13);
if (isMountain) isStraight = true;
isFlush = (card[0].suit == card[1].suit && card[0].suit == card[2].suit && card[0].suit == card[3].suit && card[0].suit == card[4].suit) ? true : false;
}
if (isStraight)
{
// 스티플
if (isFlush)
{
handRanking = HandRanking.StraightFlush;
}
else
{
handRanking = HandRanking.Straight;
}
}
else if (isFlush)
{
handRanking = HandRanking.Flush;
}
// 하이카드
else
{
handRanking = HandRanking.HighCard;
scoreCards.Add(card[card.Count - 1]);
}
if (isStraight || isFlush)
{
foreach(PlayCard c in card)
{
scoreCards.Add(c);
}
}
Debug.Log($"EvaluateNotPairRanking, handRanking: {handRanking}");
}
public void EvaluatePairRanking(int count)
{
switch (count)
{
case 1: handRanking = HandRanking.OnePair; break;
case 2: handRanking = HandRanking.TwoPair; break;
case 3: handRanking = HandRanking.Triple; break;
case 4: handRanking = HandRanking.FullHouse; break;
case 6: handRanking = HandRanking.FourCard; break;
}
Debug.Log($"EvaluatePairRanking, handRanking: {handRanking}");
}
private void UpdateRankingUI()
{
rankingChanged?.Invoke(handRanking);
}
}
</style>
오늘은 카드 계산에 대한 기능과 카드 계산의 핵심인 족보 판정에 대한 기능을 구현해보았습니다.
Pair 케이스에 대한 족보 판정은 비교적 쉬웠지만 UnPair 케이스에 대한 족보 판정시에 당연시 여겼던 부분들(ex. 카드 계산이 항상 5장일 것이다 .. )로 인한 오류가 발생했었을 때, 처음에는 왜 문제인지 찾지 못했던 이슈가 있었습니다.
결과적으로 오류를 찾아 해결했지만 개발을 하면서 다양한 상황에 대하여 고려하고 방어코드를 작성했어야 하는데 그런 부분에 있어 많이 미흡하다는 것을 느꼈습니다.