모바일 맞고 게임8-2(간단한 A.I. 첨가 #2 - 상황 선택 기능 구현)
DevSource/맞고 2012. 12. 27. 22:19 |두장의 카드 중에서 하나의 카드를 선택해야 할때...
☆ ☆ ☆ ☆ ☆ ☆ |
/**
* 카드 둘중 하나를 선택할때...
* @param g
*/
private void drawSelectTwoCards(Graphics g)
{
byte index;
drawRunningGame(g);
/* 카드 두장 중 하나를 선택한다. */
dialog.drawBorder(g, CyDialog.SELECTTWOCARD);
// 두 종류의 카드를 보여줌.
if(room.slot[0] != room.CARD_NULL)
index = 0;
else if(room.slot[1] != room.CARD_NULL)
index = 1;
else return;
drawCard(40, 66, room.getMonth(room.slot[index])[0]);
drawCard(65, 66, room.getMonth(room.slot[index])[1]);
// 컴퓨터 턴이라면 AI가 선택.
if(curTurn == User.COMPUTER)
{
Sleep.sleep(500);
selectTwoCards(index, User.COMPUTER,
agent.selectTwoCards(index));
}
} |
두장의 카드 중에서 하나의 카드를 선택 하는 장면을 그려주는 함수인 drawSelectTwoCards() 함수에서
처리를 해 주는 것이 가장 좋겠죠. 그림을 그려준 후에 만약 현재 턴이 컴퓨터 턴이라면,
Agent 클래스의 selectTwoCards() 함수를 호출해서 A.I.가 카드를 선택할 수 있도록 해 줍니다.
Agent 클래스의 selectTwoCards() 함수를 한번 보도록 하죠.
/**
* A.I.가 두 장의 카드 중 하나를 선택한다.
* @return
*/
public byte selectTwoCards(byte index)
{
byte i;
/* 광 체크. */
for(i=0; i<2; i++)
{
// 두개의 패중에 광이 있다면,
if(gCard.getType(
room.getMonth(room.slot[index])[i]) == CARDTYPE_KWANG
|| gCard.getType(
room.getMonth(room.slot[index])[i]) == CARDTYPE_BIKWANG)
{
System.out.println(" # selectTwoCards MSG : 광 발견!");
return (byte)(i+1);
}
}
/* 고도리, 청단, 홍단, 초단 체크 */
for(i=0; i<2; i++)
{
// 고도리 검색 허용 상태이거나 상대방이 고도리를 두장 이상 가지고 있다면,
if(bNoFindGodori == false
|| (room.getCntGodori(Room.PLAYER) >= 2
&& room.getCntGodori(Room.COMPUTER) == 0))
{
if(room.getCntGodori(Room.PLAYER) >= 1)
{
bNoFindGodori = true;
}
else if(gCard.getType(
room.getMonth(room.slot[index])[i]) == CARDTYPE_GODORI)
{
// 두개의 패중에 고도리가 있다면,
System.out.println(" # selectTwoCards MSG : 고도리 발견!");
return (byte)(i+1);
}
}
// 청단 검색 허용 상태이거나 상대방이 청단을 두장 이상 가지고 있다면,
if(bNoFindChungDan == false
|| (room.getCntChungDan(Room.PLAYER) >= 2
&& room.getCntChungDan(Room.COMPUTER) == 0))
{
if(room.getCntChungDan(Room.PLAYER) >= 1)
{
bNoFindChungDan = true;
}
else if(gCard.getType(
room.getMonth(room.slot[index])[i]) == CARDTYPE_CHUNGDAN)
{
// 두개의 패중에 청단이 있다면,
System.out.println(" # selectTwoCards MSG : 청단 발견!");
return (byte)(i+1);
}
}
// 홍단 검색 허용 상태이거나 상대방이 홍단을 두장 이상 가지고 있다면,
if(bNoFindHongDan == false
|| (room.getCntHongDan(Room.PLAYER) >= 2
&& room.getCntHongDan(Room.COMPUTER) == 0))
{
if(room.getCntHongDan(Room.PLAYER) >= 1)
{
bNoFindHongDan = true;
}
else if(gCard.getType(
room.getMonth(room.slot[index])[i]) == CARDTYPE_HONGDAN)
{
// 두개의 패중에 홍단이 있다면,
System.out.println(" # selectTwoCards MSG : 홍단 발견!");
return (byte)(i+1);
}
}
// 초단 검색 허용 상태이거나 상대방이 초단을 두장 이상 가지고 있다면,
if(bNoFindChoDan == false
|| (room.getCntChoDan(Room.PLAYER) >= 2
&& room.getCntChoDan(Room.COMPUTER) == 0))
{
if(room.getCntChoDan(Room.PLAYER) >= 1)
{
bNoFindChoDan = true;
}
else if(gCard.getType(
room.getMonth(room.slot[index])[i]) == CARDTYPE_CHODAN)
{
// 두개의 패중에 초단이 있다면,
System.out.println(" # selectTwoCards MSG : 초단 발견!");
return (byte)(i+1);
}
}
}
/* 쌍피 체크 */
for(i=0; i<2; i++)
{
// 두개의 패중에 쌍피가 있다면,
if(gCard.getType(
room.getMonth(room.slot[index])[i]) == CARDTYPE_SSANG_PEE)
{
System.out.println(" # selectTwoCards MSG : 쌍피 발견!");
return (byte)(i+1);
}
else if(room.getMonth(room.slot[index])[i] == gCard.CARD_GOOKJIN)
{
// 국진이 있다면, 국진을 먹는다.
System.out.println(" # selectTwoCards MSG : 국진 발견!");
return (byte)(i+1);
}
}
/* 열끗 체크 */
if(room.getNumOfYeul(Room.COMPUTER) >= 5
&& room.getNumOfYeul(Room.PLAYER) <= 2)
{
/* 멍따 가능성이 있다면, 피보다는 열끗을 먹어준다. */
for(i=0; i<2; i++)
{
// 두개의 패중에 열끗이 있다면,
if(gCard.getType(
room.getMonth(room.slot[index])[i]) == CARDTYPE_YEUL)
{
System.out.println(" # selectTwoCards MSG : 열끗 발견!");
return (byte)(i+1);
}
}
}
/* 피 체크 */
for(i=0; i<2; i++)
{
// 두개의 패중에 피가 있다면,
if(gCard.getType(room.getMonth(room.slot[index])[i]) == CARDTYPE_PEE)
{
System.out.println(" # selectTwoCards MSG : 피 발견!");
return (byte)(i+1);
}
}
/* 그 외에는 둘중 아무거나 선택. */
System.out.println(" # selectTwoCards MSG : 암거나 냈음!");
return (byte)(Etc.getRandomInt(2)+1);
}
|
역시 마찬가지로 주욱 여러가지의 상황을 나열해 놓고 비교 하고 있습니다.
순서는 Agent.run() 함수와 동일합니다.
두개의 카드 중 광이나 비광이 있다면 그것을 선택해 주고,
청단, 홍단, 초단이 있다면, 먹을 필요가 있는지 먼저 체크 후, 그것을 선택해 줍니다.
그리고 그 다음으로는 쌍피가 있는지 체크하고, 열끗이 있다면, 멍따 가능성이 있을대에는 열끗을 먹고,
멍따 가능성이 별로 없다면, 그냥 일반 피를 선택해 줍니다.
이전과 마찬가지로 위의 모든 상황에 해당되는 것이 없다면, 아무거나 렌덤으로선택 해 주는 것이고요.
흔듬/폭탄의 여부를 선택해야 할때...
☆ ☆ ☆ ☆ ☆ ☆ |
/**
* 흔듬/폭탄 여부를 선택 중일때...
* @param g
*/
private void drawSelectShake(Graphics g)
{
drawRunningGame(g);
if(shakeType == SHAKE) // 흔듬 선택 중일때,
dialog.drawBorder(g, CyDialog.SELECTSHAKE);
else /* if(shakeType == BOMB) */ // 폭탄 선택 중일때,
dialog.drawBorder(g, CyDialog.SELECTBOMB);
// 컴퓨터 턴일땐 AI가 선택.
if(curTurn == User.COMPUTER)
{
Sleep.sleep(500);
selectShake(User.COMPUTER, agent.selectShake());
}
} |
역시 앞의 drawSelectTwoCards()와 마찬가지로,
현재 턴이 컴퓨터의 턴일때에는, agent.selectShake() 함수를 수행해서 A.I.가 선택할 수 있도록 합니다.
그러나... 흔듬/폭탄의 선택은 다음과 같습니다.
/** * A.I.가 흔듬 여부를 선택한다. * @return */ public byte selectShake() { // 무조건 흔듬(or 폭탄). return 1; } |
너무 간단하죠?
대부분, 흔듬을 선택하고, 흔들지 않아야 할 이유가 별로 없는 것 같아서, 무조건 흔듬으로 지정했습니다.
국진의 사용 여부를 선택해야 할때...
☆ ☆ ☆ ☆ ☆ ☆ |
/**
* 국진의 사용을 선택 중일때,
* @param g
*/
private void drawSelectGookjin(Graphics g)
{
drawRunningGame(g);
dialog.drawBorder(g, CyDialog.SELECTGOOKJIN);
// 컴퓨터 턴일땐 AI가 선택.
if(curTurn == User.COMPUTER)
{
Sleep.sleep(500);
selectGookjin(User.COMPUTER, agent.selectGookjin());
}
} |
역시 이전과 마찬가지로 현재 턴이 컴퓨터의 턴일때에는 agent.selectGookjin()을 호출합니다.
|
국진을 선택하는 것도 그렇게 복잡하지는 않습니다.
열이 다섯장 이상이고, 플레이어가 열을 2장 이하로 가지고 있다면, 멍따의 가능성이 있겠죠?
왜냐하면, 열이 아홉장인데, 컴퓨터가 다섯장을 가지고 있다고 해도, 플레이어가 이미 4장을 가지고 있다면,
멍따의 가능성이 전혀 없으므로, 굳이 열끗을 먹을 필요가 없겠지요.
멍따의 가능성이 없을때에는 국진패는 무조건 쌍피로 처리를 합니다.
고/스톱을 선택해야 할때...
/**
* A.I.가 고/스톱을 선택한다.
* @return
*/
public byte selectGoStop()
{
room.getNumOfCard();
// 플레이어의 광이 4장 이상일때,
if(room.getNumOfKwang(Room.PLAYER) >= 4)
{
// 컴퓨터가 광이 한장도 없다면, (오광 위험!)
if(room.getNumOfKwang(Room.COMPUTER) == 0)
return 2; // 스톱.
}
// 고도리 위험이 있다면,
if(room.getCntGodori(Room.PLAYER) >= 2
&& room.getCntGodori(Room.COMPUTER) == 0)
{
// 점수가 0점이 아니라면 스톱.
if(room.getRealPoint(Room.PLAYER) != 0)
return 2; // 스톱.
}
// 청단 위험이 있다면,
if(room.getCntChungDan(Room.PLAYER) >= 2
&& room.getCntChungDan(Room.COMPUTER) == 0)
{
// 점수가 2점 이상이라면 스톱.
if(room.getRealPoint(Room.PLAYER) >= 2)
return 2; // 스톱.
}
// 홍단 위험이 있다면,
if(room.getCntHongDan(Room.PLAYER) >= 2
&& room.getCntHongDan(Room.COMPUTER) == 0)
{
// 점수가 2점 이상이라면 스톱.
if(room.getRealPoint(Room.PLAYER) >= 2)
return 2; // 스톱.
}
// 초단 위험이 있다면,
if(room.getCntChoDan(Room.PLAYER) >= 2
&& room.getCntChoDan(Room.COMPUTER) == 0)
{
// 점수가 2점 이상이라면 스톱.
if(room.getRealPoint(Room.PLAYER) >= 2)
return 2; // 스톱.
}
// 피가 10장 이상이고,
if(room.getNumOfPee(Room.PLAYER) >= 10)
{
// 열이 7개 이상이라면,
if(room.getNumOfYeul(Room.PLAYER) >= 7)
return 2; // 스톱.
// 플레이어의 열과 띠가 5장 이상이라면,
if(room.getNumOfYeul(Room.PLAYER) >= 5
&& room.getNumOfDdee(Room.PLAYER) >= 5)
return 2;
// 플레이어의 열이 5장 이상이고, 점수가 3점 이상이라면,
if(room.getNumOfYeul(Room.PLAYER) >= 5
&& room.getNumOfYeul(Room.COMPUTER) <= 2
&& room.getRealPoint(Room.PLAYER) >= 3)
return 2; // 스톱.
// 플레이어의 띠가 5장 이상이고, 점수가 3점 이상이라면,
if(room.getNumOfDdee(Room.PLAYER) >= 5
&& room.getNumOfDdee(Room.COMPUTER) <= 2
&& room.getRealPoint(Room.PLAYER) >= 3)
return 2; // 스톱.
}
// 피가 13장 이상이라면,
if(room.getNumOfPee(Room.PLAYER) >= 13)
return 2; // 스톱.
// 플레이어의 점수가 4점 이상이라면,
if(room.getRealPoint(Room.PLAYER) >= 4)
return 2; // 스톱.
// 남은 카드가 한장이거나 혹은 없다면,
if(room.numOfComputerCard <= 1)
return 2; // 스톱.
// 그 외엔 무조건 고.
return 1;
} |
가능하다면, 컴퓨터가 먼저 고를 했다가, 고박을 당하고 지게 된다면, 안되겠죠.
그렇다고 너무 고를 안하고, 원고에서 바로 스톱을 하거나 하면 게임이 재미가 없어질 것이구요.
따라서 최대한 고를 하면서도 안전하게 게임을 진행 할 수 있도록 구현을 해야합니다.
위의 상황 체크는 지극히 제 개인적인 주관에 의해서 체크하는 상황이므로, 따로 설명은 하지 않겠습니다.
게다가 어차피 주석으로 다 설명이 되어 있으니...
마음에 들지 않는 상황이나, 추가 하고 싶은 상황이 있다면, 삭제하거나 상황을 더 추가할 수도 있겠죠.
Alpha-Beta Search에서 한가지 중요한 점은 중요한 상황일 수록 앞에 위치 해야 한다는 것입니다.
별로 중요하지도 않은 상황이 앞에 있다면, 뒤에 더 중요한 상황은 체크도 못해본 채로 함수가 끝나겠죠?
강좌 지면상이라는 이유로 A.I. 프로그래밍을 완벽하게 구현하지는 않았습니다.
딱 보시면 아시곘지만, 모든 상황을 다 체크하고 있는 것은 아니니까요.
그러나, 이렇게 별로 많이 추가하지 않았는데도 컴퓨터가 꽤 합니다. 가끔가다가 한판은 집니다^^;
상황을 더 많이 추가해준다면 컴퓨터가 그 만큼 똑똑해 지겠죠.
이게 바로 Alpha-Beta Search의 장점인 동시에 치명적인 단점이죠...
이번 시간의 화면 인터페이스 자체는 지난시간과 동일하므로, 스크린 샷은 생략하도록 하겠습니다.
'DevSource > 맞고' 카테고리의 다른 글
모바일 맞고 게임10(RMS를 이용한 게임 데이터 세이브/로드) (0) | 2012.12.27 |
---|---|
모바일 맞고 게임9(사운드 출력과 진동) (0) | 2012.12.27 |
모바일 맞고 게임8-1(간단한 A.I. 첨가 #1 - 기본 게임 진행 기능 구현) (0) | 2012.12.27 |
모바일 맞고 게임7-2(디스플레이 요소 추가 #2) (0) | 2012.12.27 |
모바일 맞고 게임7-1(디스플레이 요소 추가 #1) (0) | 2012.12.27 |