2.3 각 카드의 유효패를 계산하여 가장 효율적인 타패를 찾음
구현 기능 목록
1. 마작게임의 뼈대를 구현 : )
2. 유효패를 계산하여 가장 효율적인 조패를 계산
2-1. 유효패에 대한 개념 정하기
2-2. 패의 샨텐을 계산하는 기능 구현
2-3 각 카드의 유효패를 계산하여 가장 효율적인 타패를 찾음
3. 화료 시 점수를 계산
4. 타가들의 버림 배를 구현.
5. 다른 여러 기능을 구현. (리치 퐁 깡 등)
6. 1인 마작 게임 구현.
클래스 정리표
Class | |||||||||
Card | String type | int number | int idCode | ||||||
Deck | List<Card> cards | int[ ] [ ] cardInformation |
|||||||
Game | Deck deck | List<Card> east_hand | 샨텐 계산 Methods |
2.3 각 카드의 유효패를 계산하여 가장 효율적인 타패를 찾음
구현기능 목록이.. 자꾸 늘어나게 되네요
그래도 착실하게 하다보면..끝이나겠죠?
서론
현재 내가 카드들의 현재 샨텐수를 계산하는 기능까지 구현을 했습니다.
이제 카드를 가장 빠른 화료를 위한 타패를 가장 효율적인 카드를 계산하여야 합니다.
(앞서 말씀드렸다시피, 가장 빠르게 화료하는 것을 목표합니다.)
구현 계획
Method calculateShentan을 수행 후, 블럭에 들어가지 못한 카드들 중 1. 가장 인접패가 적은 것을 찾는다.
*블럭간의 중요도는 추후 구현을 해보도록 해보자!
인접패의 계산을 위해, 주관적인 2. 카드 카운팅이 필요함.(자신이 카드를 뽑을 때나 버릴 때)
카드 카운팅을 구현하기 앞서, Game 클래스를 분리하기로 하였다.
클래스 분리의 이유
1. Game이라는 클래스의 네이밍에 비해 직관적으로 연관되지 않는 기능들이 점점 많아지는 것을 느꼈다.
2. 클래스 내부의 큰 부분이 독립적으로 나누어지는 것을 느꼈다. (주관적인 패산의 카운팅은 4명의 사람에 따라 다 따로 시행되어야 함과 같은)
3. 추후 기능을 더 추가헀을때, 기능의 범위가 너무 커질 것 같아서.(4인의 각 패와 게임의 진행 등..)
Class | |||||||||
Card | String type | int number | int idCode | ||||||
Deck | List<Card> cards | int[ ] [ ] cardInformation |
|||||||
Game | Deck deck | ||||||||
User | List<Card> east_hand | 샨텐 계산 Methods | 화료 |
구현과정
새로운 클래스 User를 구현 후. 코드를 대규모 리팩토링이 있었다. (Test 코드까지)
큰 흐름은 카드 < 유저 < 덱 < 게임 이렇게로 정리할 수 있을 것 같다.(다음 표에는 이걸 반영해야겠구나)
추가된 기능은 주관적인 카드 카운팅
public void updateCardCounting(Card card){
int idCode = card.idCode;
cardCounting[getIdcodeType(card)][getIdcodeNumber(card)] --;
}
private int getIdcodeType(Card card){
return card.idCode/10;
}
private int getIdcodeNumber(Card card){
return card.idCode%10;
}
- Card의 idCode를 통해 index로 변환하는 private method 생성
- User 내 배열인 cardCounting 에 반영.
- 카드를 뽑을 때, updateCardCounting method를 실행
카운팅을 통한 버릴 후보패 중 가장 효율적인 선택을 하는 기능이다
public Card findUnnecessaryCard(){
int maxAdjacent = 12;
int targetIndex = -1;
for(int i=0;i<myCards.size();i++){
if(!isBlock[i]){
int adjacent =0;
int type =getIdcodeType(myCards.get(i));
int number = getIdcodeNumber(myCards.get(i));
adjacent+=cardCounting[type][number];
if(number>1 && number <9){
adjacent+=cardCounting[type][number+1];
adjacent+=cardCounting[type][number-1];
}
if(number==1){
adjacent+=cardCounting[type][number+1];
}
if(number==9){
adjacent+=cardCounting[type][number-1];
}
if(adjacent<maxAdjacent){
maxAdjacent = adjacent;
targetIndex = i;
}
}
}
return myCards.get(targetIndex);
}
- isBlock을 순회하며, 필요하 없는 패들을 서치.
- search된 패들의 인접패를 cardCounting을 통해 계산
- 가장 적은 인접패를 가진 카드를 return (그냥 index 그 자체를 return해주는 것이 어떨까?)
- 한계점 : 같은 인접패들 중 가장 먼저 비교된 패를 버림패로 계산하게 된다.
마치며
'왜! 클래스를 분류했을까' 에 대한 이유를 생각해 보며 그래도 나만의 기준을 잡은 것 같아 뿌듯했다. 다시 간단하게 정리해 보자면,
1) 클래스의 네이밍과 기능이 직관적으로 연관이 되지 않을 때
2) 클래스에서 하나의 큰 부분이 중복(반복)될 때
3) 구현의 전체적인 측면을 생각했을 때.
무엇인가를 바꾸고 싶을 때는 원인이나 이유를 정리해 보는 시간을 갖는 것도 좋을 것 같다.
다음에는 드디어 화료에 관한 기능을 구현을 해볼 것 같다. 9/22일까지 블로그 포스팅을 하는 것을 목표로.. 안 하면 바보.