3뻑/쪽/따닥.

우리가 그냥 넘어가긴 했지만, 지난시간에 구현했던 내용에 이미 쪽과 따닥이 구현되어 있습니다.

그 곳에 쪽과 따닥이 발생 시, 상대방의 피를 한장 가져오는 것만 추가를 해주면 되겠죠.


한판에 3번 뻑이 나면, 게임이 끝나며, 7점에 해당되는 금액을 상대방에게획득하고, 승리합니다.

또, 쪽이란 먹을 수 있는 패가 없어서 아무 패나 냈는데 뒤집은 패가 같은 무늬인 경우이죠.

쪽이 발생했을 경우에는, 패를 먹음과 동시에 상대방의 피 한 장을 가져옵니다.

따닥은 바닥에 같은 무늬 패 두 장이 있고 그걸 먹기 위해 패를 냈는데, 뒤집은 패도 같은 무늬일 경우,

해당 카드를 네 장 모두 먹음과 동시에 상대방의 피 한 장을 가져옵니다.


지난 시간에 뻑을 구현 했는데, 그 안에 쪽과 따닥에 관한 내용도 이미 포함이 되어 있습니다.

// 손에서 냈던 카드와 뒤집어서 낸 카드가 같은 슬롯에 있다면,
if(handCardIdx == reverseCardIdx)
{
    int i, j;

    // 뻑 체크.
    for(i=0; i<4; i++)
    {
        if(room.getMonth(handCardIdx)[i] == room.CARD_NULL)
        {
            switch(i)
            {
            case 2: // 손에서 낸 카드와, 뒤집어 낸 카드 둘뿐이라면,
              /* 쪽! */
                // 쪽일때에는, 우선 두장 다 먹고,
                room.addPae(curTurn, room.getMonth(handCardIdx)[0]);
                room.addPae(curTurn, room.getMonth(handCardIdx)[1]);
                room.getMonth(handCardIdx)[0] = room.CARD_NULL;
                room.getMonth(handCardIdx)[1] = room.CARD_NULL;

              // 상대방의 피를 하나 가져온다.
              room.robPee(curTurn);

                repaints();
                break;
            case 3: // 손에서 낸 카드와, 뒤집어 낸 카드와 원래 카드 한장이라면,
                // System.out.println(" # Msg : 뻑!!");
                room.incCntPuck(curTurn); // 뻑 횟수 +1.

              // 3번 뻑이라면,
              if(room.getCntPuck(curTurn) >= 3)
              {
                  // 7점에 해당하는 금액을 상대방에게 받고,
                  room.setPoint(curTurn, 7); // 플레이어 점수를 7점으로 세팅.
                  // 게임 종료.
                  curViewState = State.GAME_RESULT;

                  repaints();

                  // 게임 끝.
                  curViewState = State.GAME_RESULT;
                  repaints();

                  return;
              }

                break;
            }
        } // end if(room.getMonth(handCardIdx)[i] == room.CARD_NULL).
        else
        {
          /* 카드가 슬롯에 가득 차 있다면, 따닥! */
            if(i == 3)
            {
                // 모두 먹고,
                for(j=0; j<4; j++)
                {
                    room.addPae(curTurn, room.getMonth(handCardIdx)[j]);
                    room.getMonth(handCardIdx)[j] = room.CARD_NULL;
                }

              // 상대방의 피를 하나 가져온다.
              room.robPee(curTurn);
            }
        }
    }
}

앞에
와 같은 별이 달려있는 Line이 새로 추가된 줄입니다.

뻑이 3번 발생하면, 뻑이 3번 발생한 플레이어의 점수를 무조건 7점으로 만들고 게임을 종료 합니다.

왜냐하면, 게임이 끝났을때, 플레이어의 점수에 따라 상대방에게서 금액을 획득하므로,

점수를 7점으로 세팅해 주면, 7점에 해당하는 금액을 받아오게 되겠지요.


그리고 쪽과 따닥은 지난 시간까지의 내용에 상대방의 피를 하나 가져오는 부분만을 추가한 것이죠.

상대방에게서 피를 한장 뺏어오는 robPee() 함수는 twogo1p.Room 클래스에 정의 되어 있습니다.

/**
 * 다른 사용자로부터, 피를 한장 가져온다.
 * @param userType
 * @return
 */
public byte robPee(byte userType)
{
    byte userLastIdx = -1;
    byte enemyLastIdx = -1;
    byte enemyType;
    byte userPeeIdx = CARD_NULL, enemyPeeIdx = CARD_NULL;
    byte userSsangPeeIdx = CARD_NULL, enemySsangPeeIdx = CARD_NULL;

    // 상대방의 타입 설정.
    enemyType = (userType==COMPUTER)? PLAYER : COMPUTER;

    // 상대방의 피에 1점자리 피가 있는지 찾는다. 
    for(i=0; i<MAX_PEE; i++)
    {
        // 플레이어의 카드가 없다면,
        if(cardsPee[userType][i] == CARD_NULL)
            userLastIdx = (byte)(i-1);
// 상대방의 카드가 없다면, if(cardsPee[enemyType][i] == CARD_NULL) enemyLastIdx = (byte)(i-1);
// 둘다 검사가 끝났다면, if(userLastIdx != -1 && enemyLastIdx != -1) break;
// 상대방으로부터 가져올 1점 자리 패를 찾았다면, if(enemyPeeIdx == CARD_NULL && gCard.getType(cardsPee[enemyType][i]) == CARDTYPE_PEE)
{ enemyPeeIdx = (byte)i; } // 상대방으로부터 가져올 쌍피를 찾았다면, if(enemyPeeIdx == CARD_NULL && gCard.getType(cardsPee[enemyType][i]) == CARDTYPE_SSANG_PEE) { enemySsangPeeIdx = (byte)i; } // 사용자의 패에서, 1점 자리 패를 찾았다면, if(userPeeIdx == CARD_NULL && gCard.getType(cardsPee[enemyType][i]) == CARDTYPE_PEE) { userPeeIdx = (byte)i; } // 사용자의 패에서, 쌍피를 찾았다면, if(userPeeIdx == CARD_NULL && gCard.getType(cardsPee[enemyType][i]) == CARDTYPE_SSANG_PEE) { userSsangPeeIdx = (byte)i; } } // for(i=0; i<MAX_PEE; i++). // 상대방의 패에서 1점 자리 패를 찾았다면, if(enemyPeeIdx != CARD_NULL) { // 사용자의 피를 한장 추가 하고, addPae(userType, cardsPee[enemyType][enemyPeeIdx]); // 빈 슬롯을 매꿔 준다. cardsPee[enemyType][enemyPeeIdx] = cardsPee[enemyType][enemyLastIdx]; cardsPee[enemyType][enemyLastIdx] = CARD_NULL; } else if(userPeeIdx != CARD_NULL) // 사용자의 패에서 1점 자리 패를 찾았다면, { byte Temp; // 상대방의 마지막 패가 쌍피라면, if(gCard.getType(cardsPee[enemyType][enemyLastIdx]) == CARDTYPE_SSANG_PEE) { // 상대방으로부터 쌍피를 받고, Temp = cardsPee[enemyType][enemyLastIdx]; // 1점 짜리 피를 넘겨줌. cardsPee[enemyType][enemyLastIdx] = cardsPee[userType][userPeeIdx]; cardsPee[userType][userPeeIdx] = Temp; } else if(userSsangPeeIdx != CARD_NULL) // 사용자가 쌍피가 있다면, { // 상대방의 마지막 패가 쓰리피라면, if(gCard.getType(cardsPee[enemyType][enemyLastIdx])==CARDTYPE_THREE_PEE) { // 상대방으로부터 쓰리피를 받고, Temp = cardsPee[enemyType][enemyLastIdx]; // 쌍피를 넘겨줌. cardsPee[enemyType][enemyLastIdx] = cardsPee[userType][userSsangPeeIdx]; cardsPee[userType][userSsangPeeIdx] = Temp; } } } return 1; } // end public byte robPee(byte userType).

함수가 조금 길긴 하지만, 별건 아닙니다.

먼저, 0부터 MAX_PEE(36) 까지 루프를 돌면서, 각각의 플레이어의 피에서 쌍피와 1점자리 일반피를 찾아냅니다.

만약에 상대방의 피에서 1점 자리 일반 피를 찾았다면, 그 피만 받고 끝나면 되겠죠.


그러나, 상대방의 피에 1점자리 피가 없다면, 쌍피가 있는지 보고,

플레이어가 1점자리 피가 있다면, 쌍피를 받고 1점자리 피를 상대에게 넘겨줍니다.


상대방이 쓰리피가 있다면, 플레이어에게 쌍피가 있는지 보고,

플레이어가 쌍피가 있다면, 쓰리피를 받고 쌍피를 넘겨 줍니다.

이렇게 하면 피 한장을 받을 수 있겠죠?


고/스톱 관리.

맞고 역시 1:1 고스톱이나 마찬 가지이므로, 고와 스톱이 존재하죠.

투고 까지는 1고는 1점, 2고는 2점. 과 같이 더해지지만,

3고부터는 점수의 2배, 4고는 점수의 4배, 5고는 점수의 8배, .... 이런식으로 늘어납니다.


고/스톱 을 관리 해 주는 checkGoStop() 함수는 다음과 같이 작성해 줍니다.

private void checkGoStop()
{ if(room.getCntGo(curTurn) == 0 && room.getRealPoint(curTurn) >= LIMITPOINT_GOSTOP) { // 고를 한번도 하지 않았을때, 일정 점수를 초과하면, prePoint[curTurn] = room.getPoint(curTurn); // 현재 점수 저장. // 고스톱 선택 다이얼로그를 보여준다. curViewState = State.GAME_SELECTGOSTOP; room.incCntGo(curTurn); // 고 횟수 +1. repaints(); return; } else if(room.getCntGo(curTurn) >= 1 && room.getRealPoint(curTurn)-prePoint[curTurn] >= 1) { // 고를 한번이상 한 뒤, 1점이라도 내면, prePoint[curTurn] = room.getPoint(curTurn); // 현재 점수 저장. // 고스톱 선택 다이얼로그를 보여준다. curViewState = State.GAME_SELECTGOSTOP; room.incCntGo(curTurn); // 고 횟수 +1. repaints(); return; }
}

위에서 별로 헷갈릴만한 내용은 없을 거라고 봅니다.

고를 한번도 하지 않은 상태에서는 LIMITPOINT_GOSTOP(GameView에 7로 정의되어있음)을 초과하면,

고/스톱을 물어보지만, 일단 한번 고를 한 상태에서는 추가로 점수가 발생할 때마다 고/스톱을 물어보죠.


이 checkGoStop() 함수는추가로 점수가 발생 하는 곳에다가 모두 삽입을 해주어야 하겠지요.

카드를 냈을때에만, 점수가 발생하는 것이 아니니까요.

흔들었을 때에도, 점수가 2배가 되므로, 고/스톱을 물어 보아야 하고,

국진을 쌍피로 선택 했을때에도, 추가 점수가 발생하므로, 고/스톱을 물어보아야 하고,

일반 적인 경우에도, 점수가 발생했다면, 고/스톱을 체크해야하겠죠.


그런데 이 checkGoStop() 함수에 보면, getRealPoint() 라는 함수를 이용해서 점수를 얻어 오고 있는데,

지난 시간에 우리가 작성했던 점수를 얻어오는 함수는 getPoint() 함수였었죠?


getRealPoint() 함수는, 각종 룰의 공식에 의해 실제적으로 얻어지는 실제 점수를 리턴해 줍니다.

getRealPoint() 함수는 gostop.GameRoom 클래스에 정의되어 있습니다.

/**
 * 실제 점수를 리턴한다.
 * @param userType
 * @return
 */
public int getRealPoint(byte userType)
{
    int realPoint=getPoint(userType);

    // 원고나 투고 라면,
    if(cntGo[userType] == 1
        ||  cntGo[userType] == 2)
    {
        // 원고면 1점, 투고면 2점을 더해줌.
        realPoint += cntGo[userType];
    }
    else if(cntGo[userType] >= 3) // 쓰리고 이상이라면,
    {
        // 3고 부터는, 2의 (고횟수-2)승으로 계산.
        realPoint *= Etc.pow(2, cntGo[userType]-2);
    }

    // 흔들기도, 2의 흔든 횟수승으로 계산.
    realPoint *= Etc.pow(2, cntShake[userType]);

    return realPoint;
}

고나 흔들기가 발생하면, 점수에 두배를 곱해야 하는데,

현재 점수의 두배가 아니라 최종 점수의 두배이므로, 현재 점수를 함부로 건드려선 안되겠죠.


그래서, 고와 흔들기에 의한 변경된 점수는 getRealPoint() 함수를 통해서 얻어 옵니다.

여기서 3고 부터는 2의 (고횟수-2)승 이라는 것까지는 이해가 가시겠죠?

그런데 여기서 제곱승을 구하는 함수가 Etc.pow() 함수가 쓰였습니다.

제곱승을 구해오는 pow() 함수는 표기 된 데로 common.Etc 클래스에 정의되어 있습니다.

/**
 * a의 n제곱승을 리턴.
 * @param a
 * @param n
 * @return a의 n승.
 */
public static int pow(int a, int n)
{
    byte i;
    short nPowOfa=1;

    for(i=0; i<n; i++)
    {
        nPowOfa *= a;
    }

    return nPowOfa;
}

그냥 단순히 인자로 받은 a의 n승을 리턴해 주는 함수입니다.

for 루프를 n번 돌아서 1에 a를 곱해 주면 a의 n승을 구해 올 수 있겠죠.

나가리.

나가리란, 두명의 사용자가 모든 카드를 다 냈는데도, 게임이 끝나지 않았을때,

즉, 그 판에서 아무도 7점을 내지 못하거나 상대방이 고를 했는데 추가 점수를 내지 못했을 경우를 말합니다.

나가리 시에는 게임을 끝내고 결과를 보여 주어야 하겠죠.

/**
 * 나가리를 체크.
 * @return 나가리 여부.
 */
private boolean checkNagari()
{
    // 플레이어의 카드와 컴퓨터의 카드가 없다면,
    if(room.numOfPlayerCard == 0 && room.numOfComputerCard == 0 )
    {
        repaints();

        // 나가리.
        room.setNagari(true);
        // 게임 끝.
        curViewState = State.GAME_RESULT;
        repaints();

        return false;
    }

    return true;
}

플레이어의 카드와 컴퓨터의 카드가 한장도 없는데, 게임이 끝나질 않았다면, 나가리로 처리하고,

결과 화면을 보여줍니다.

결과 출력.

이제 그럼 결과 화면을 출력해 보도록 하겠습니다.

/**
 * 결과 화면을 그려준다.
 * @param g
 */
private void drawResult(Graphics g)
{
    drawRunningGame(g);

    // 나가리라면...
    if(room.getNagari() == true)
    {
        if(gameEnd == false)
            gameEnd = true;

        g.drawString("나가리!", 0, 0, Graphics.LEFT|Graphics.TOP);
    }
    else
    {
        /* 벌칙. */
        byte goBak; // 고박.
        byte peeBak; // 피박.
        byte kwangBak; // 광박.
        byte mungDda; // 멍따.

        switch(curTurn)
        {
        case User.PLAYER: // 플레이어의 턴일때,
            g.drawString("플레이어 승!", 0, 0, Graphics.LEFT|Graphics.TOP);

            // 상대방이 고를 한번 이상 했었다면, 고박. (고박일경우 돈 두배 획득)
            goBak = (room.getCntGo(User.COMPUTER) >= 1)? 
                    (byte)2 : (byte)1;
// 상대방의 피가 7장 이하라면, 피박. (피박일경우 돈 두배 획득) peeBak = (room.getNumOfPee(User.COMPUTER) < 7)? (byte)2 : (byte)1; // 상대방의 광이 없다면, 광박. (광박일경우, 돈 두배 획득) kwangBak = (room.getNumOfKwang(User.COMPUTER) == 0)? (byte)2 : (byte)1;
// 상대방의 열이 7장 이상이라면, 멍따. (멍따일 경우, 돈 두배 획득) mungDda = (room.getNumOfYeul(User.COMPUTER) >= 7)? (byte)2 : (byte)1; if(gameEnd == false) { gameEnd = true; // 플레이어의 돈 증가. room.addMoney(curTurn, room.MONEY_PER_POINT *room.getRealPoint(User.PLAYER) *goBak*kwangBak*peeBak*mungDda); // 컴퓨터의 돈 감소. room.addMoney(curTurn, -room.MONEY_PER_POINT *room.getRealPoint(User.COMPUTER) *goBak*kwangBak*peeBak*mungDda); } break; case User.COMPUTER: // 컴퓨터의 턴일대, g.drawString("컴퓨터 승!", 0, 0, Graphics.LEFT|Graphics.TOP); // 상대방이 고를 한번 이상 했었다면, 고박. (고박일경우 돈 두배 획득) goBak = (room.getCntGo(User.COMPUTER) >= 1)? (byte)2 : (byte)1;
// 상대방의 피가 7장 이하라면, 피박. (피박일경우 돈 두배 획득) peeBak = (room.getNumOfPee(User.COMPUTER) < 7)? (byte)2 : (byte)1; // 상대방의 광이 없다면, 광박. (광박일경우, 돈 두배 획득) kwangBak = (room.getNumOfKwang(User.COMPUTER) == 0)? (byte)2 : (byte)1;
// 상대방의 열이 7장 이상이라면, 멍따. (멍따일 경우, 돈 두배 획득) mungDda = (room.getNumOfYeul(User.COMPUTER) >= 7)? (byte)2 : (byte)1; if(gameEnd == false) { gameEnd = true; // 플레이어의 돈 감소. room.addMoney(curTurn, -room.MONEY_PER_POINT *room.getRealPoint(User.PLAYER) *goBak*kwangBak*peeBak*mungDda); // 컴퓨터의 돈 증가. room.addMoney(curTurn, room.MONEY_PER_POINT *room.getRealPoint(User.COMPUTER) *goBak*kwangBak*peeBak*mungDda); } } } }

만약에 나가리로 게임이 끝났다면, 화면에 그냥 나가리라고 출력해 줍니다.

만약에 나가리가 아니라면, 현재 턴의 플레이어가 스톱을 했을테니 현재 턴의 플레이어가 이겼겠죠.


이곳에서 고박, 피박, 광박, 멍따 등의 벌칙 룰을 적용해 줍니다.

// 상대방이 고를 한번 이상 했었다면, 고박. (고박일경우 돈 두배 획득)
goBak = (room.getCntGo(User.COMPUTER) >= 1)? (byte)2 : (byte)1;

// 상대방의 피가 7장 이하라면, 피박. (피박일경우 돈 두배 획득)

peeBak = (room.getNumOfPee(User.COMPUTER) < 7)? (byte)2 : (byte)1;

// 상대방의 광이 없다면, 광박. (광박일경우, 돈 두배 획득)

kwangBak = (room.getNumOfKwang(User.COMPUTER) == 0)? (byte)2 : (byte)1;

// 상대방의 열이 7장 이상이라면, 멍따. (멍따일 경우, 돈 두배 획득)

mungDda = (room.getNumOfYeul(User.COMPUTER) >= 7)? (byte)2 : (byte)1;

상대방이 고를 한번이라도 했다면, 고박으로 돈을 두배로 획득하게 해주죠.

이때 goBak에 대입해주는 정수를 곱하므로, 고박이라면 획득 금액에 2를 곱해주고, 아니면 1을 곱하겠죠.

나머지도 모두 마찬가겠죠?


따로 gameEnd라는 boolean 변수를 두어서 gameEnd가 false일때에만, 플레이어의 돈이 증감하게 합니다.

그래야 drawResult() 함수가 중복 호출 되었을때, 돈이 이중으로 빠져나가지 않게 할 수 있겠죠.


이번 시간까지의 내용으로 전반적인 맞고 게임의 틀은 모두 갖춘것 같네요.

이번 시간에 작성한 게임의 스크린 샷은 지난 시간과 별다를게 없으므로 생략하도록 하겠습니다.

 

Posted by maysent
: