되는대로 살자

뿌요뿌요 소스코드 본문

2009~2014/C/C++

뿌요뿌요 소스코드

malu 2010. 11. 4. 23:22

와 오늘 진짜 대박 뿌요뿌요 소스코드를 찾았다 !!!
분석은 내일 하도록 하겠음  (주석 너무 잘달아 놔서 할 게 없을지도? ㅋ )

0123456789101112131415

#include <Turboc.h>
 
#define LEFT_KEY     75
#define RIGHT_KEY     77
#define UP_KEY      72
#define DOWN_KEY     80
#define STOP_KEY     27 // ESC
#define START_KEY     115 // S
#define QUIT_KEY     113 // Q

#define MAP_START_X    5
#define MAP_START_Y    1
#define BOARD_WIDTH     10
#define BOARD_HEIGHT    20
#define COUNT_OF_BRICK    2
#define BRICK_COUNT_OF_CLEAR  4
#define NUM_OF_DIRECTION_TO_CHECK 4
#define HIT_COUNT_FOR_LEVEL_UP  20  // 게임 등급 상승을 위한 Brick clear회수: 4개 1회 4개 초과
#define MAX_LEVEL_COUNT    20  // 최대 게임 등급
#define AUTO_SCROLL_INTERVAL  2  // Brick를 자동으로 아래로 움직이기 위한 시간
#define BRICK_CLEAR_INTERVAL  50  // Brick한개를 지울때 지연시간

#define MAX_HIT_COUNT    99000
#define INIT_GAME     0
#define WAITING_GAME_MODE_SELECTION 1
#define START_GAME     2
#define INITIAL_TO_START_GAME  3
#define RUNNING_GAME    4
#define STOP_GAME     5
#define QUIT_PROGRAM    6

#define NEW_BRICK_MODE    1
#define BRICK_OPERATING_MODE  2

void DrawScreen();
void DisplayLevelAndBrickClearCount();

void ProcessKeyAtMenu();
BOOL ProcessKeyAtGame();
void PrintCurrentBrick(BOOL Show);
int  GetAround(int x,int y,int r);
BOOL MoveDown();
void DownOneBrick(int x, int y,int block);
void ClearOneBrick(int x, int y);
void ClearConnectedBrick();
int  BrickConnectionStatus(int x, int y, int CheckBoard);
int  BlockedBrickProgress(int x, int y);
void DisplayGameOverMessage(int mode);

struct Point {
     int x,y;
};

// 해당 정점을 중심으로하 4 방향
Point Shape[NUM_OF_DIRECTION_TO_CHECK] = {{0,1},{-1,0},{0,-1},{1,0}};
enum { EMPTY, WALL, BRICK};
char *arTile[]={". ","□","★","●","◈","♠"};
int  Board[BOARD_WIDTH+2][BOARD_HEIGHT+2];
int  Pos_X,Pos_Y;
int  CurrentBrick[COUNT_OF_BRICK],AliveBrick[COUNT_OF_BRICK];
int  RotationId;
int  Column_Height[BOARD_WIDTH];
int  BrickConnectedTable[BOARD_WIDTH+2][BOARD_HEIGHT+2];
int  CurrentLevel,BrickClearCountChanged;
long BrickClearCount;
int  RunningMode,OldRunningMode,BrickMode;

void main() 
{
 int  i,x,y;
 int  OldLevel,OldBrickClearCount;
 int  AutoScrollTimer,AutoScrollCount;

 srand((time(NULL)));
 setcursortype(NOCURSOR);
 randomize();
 clrscr();
 
 ///////////////////////////////////////////////////////////
 // 변수 초기화
 ///////////////////////////////////////////////////////////
 RunningMode = OldRunningMode = INIT_GAME;
 WAITING_GAME_MODE_SELECTION;

 while (true)
 {
  if (RunningMode == QUIT_PROGRAM)
   break;
  
  // S키를 이용해서 게임을 시작했을 때 초기화 루틴
  if ((RunningMode == INITIAL_TO_START_GAME) || (RunningMode == INIT_GAME))
  {
   // 초기화(. ) & 벽만들기(□)
   for (x=0;x<BOARD_WIDTH+2;x++)
   {
    // 열별 block의 수 초기화
    Column_Height[x] = 0;
    // 2는 벽
    for (y=0;y<BOARD_HEIGHT+2;y++) // 2는 벽
     Board[x][y] = (y==0 || y==BOARD_HEIGHT+1 || x==0 || x==BOARD_WIDTH+1) ? WALL:EMPTY;
   }
   // Level and 히트 수 초기화
   CurrentLevel = 1;
   BrickClearCount = 0;

   // Level이 올라가면 delay가 짧아져서 빨라짐
   AutoScrollCount = (MAX_LEVEL_COUNT - CurrentLevel) * AUTO_SCROLL_INTERVAL;
   AutoScrollTimer = AutoScrollCount;

   // 새 게임시작할때는 새 brick만들기로시작한다.
   BrickMode = NEW_BRICK_MODE;
   DrawScreen();
   DisplayGameOverMessage(false);

   if (RunningMode == INIT_GAME)
    RunningMode = WAITING_GAME_MODE_SELECTION;
   else if (RunningMode == INITIAL_TO_START_GAME)
    RunningMode = RUNNING_GAME;
  }

  // BRICK을 움직일수 없는 조건이면 새 BRICK생성모드가 됨
  // S나 Q,ESC키를 누르면 게임시작, 게임종료, 게임중지 등이 됨
  // Key를 조작하여 회전,아래로 떨어뜨리기 등이 됨
  if ((RunningMode == RUNNING_GAME) && (BrickMode == BRICK_OPERATING_MODE))
   ProcessKeyAtGame();
  else
   ProcessKeyAtMenu();

  if (RunningMode == RUNNING_GAME)
  {
   // BRICK생성모드(생성된 Brick이 바닥에 닿았을 때
   if (BrickMode == NEW_BRICK_MODE)
   {
    for (i=0;i<COUNT_OF_BRICK;i++)
    {
     // 생성될 벽돌 지정
     CurrentBrick[i] = rand() % 4 + 2; //2: EMPTY & WALL 제외
     
     // 다른 brick과 닿으면 false로 변함
     // false가 되면 더이상 움직일 수 없음
     AliveBrick[i] = true; 
    }

    // 벽돌의 현재 위치 초기화(생성 위치)
    Pos_X = BOARD_WIDTH/2;
    Pos_Y = 1;

    // 수평, 수직 구분
    // 추가된 Brick의 중심 brick에 대한 상대 좌표
    RotationId = rand() % 2;
    PrintCurrentBrick(TRUE);

    //BRICK생성하고 조작모드로 변경시킴
    BrickMode = BRICK_OPERATING_MODE;
   }

   // BRICK 조작 모드
   if (BrickMode == BRICK_OPERATING_MODE)
   {
    OldLevel = CurrentLevel;
    OldBrickClearCount = BrickClearCount;

    // 1씩 감소시켜 0이되면 자동 Scroll down
    if (--AutoScrollTimer == 0)
    {
     AutoScrollTimer = AutoScrollCount;
     if (MoveDown())
      BrickMode = NEW_BRICK_MODE;
    }
    CurrentLevel = BrickClearCount/HIT_COUNT_FOR_LEVEL_UP + 1;
    if (CurrentLevel >= MAX_LEVEL_COUNT)
     CurrentLevel = MAX_LEVEL_COUNT-1;

    CurrentLevel = 2;
    AutoScrollCount = (MAX_LEVEL_COUNT - CurrentLevel) * AUTO_SCROLL_INTERVAL;

    if ((CurrentLevel != OldLevel) || BrickClearCountChanged)
    {
     DisplayLevelAndBrickClearCount();
     BrickClearCountChanged = false;
     // Level이 올라가면 delay가 짧아져서 빨라짐
     AutoScrollCount = (MAX_LEVEL_COUNT - CurrentLevel) * AUTO_SCROLL_INTERVAL;
    }
   }
  }
  delay(10);
     }
     setcursortype(NORMALCURSOR);
}
 
void DrawScreen()
{
 int x,y;
 char String[256];
 
 for (x=0;x<BOARD_WIDTH+2;x++)
 {
  for (y=0;y<BOARD_HEIGHT+2;y++)
  {
   gotoxy(MAP_START_X+x*2,MAP_START_Y+y);
   puts(arTile[Board[x][y]]);
  }
 }
 gotoxy(50,3);puts("뿌요뿌요 Ver 1.0");
 gotoxy(50,5);puts("S:시작 Esc:중지 Q: 종료");

 gotoxy(50,7);puts("좌우:이동, 위:회전, 아래:내림");
 gotoxy(50,8);puts("공백:전부 내림");
 
 sprintf(String,"Level:%02d/%02d",CurrentLevel,MAX_LEVEL_COUNT);
 gotoxy(50,12);puts(String);

 sprintf(String,"Hit Count:%05ld",BrickClearCount);
 gotoxy(50,13);puts(String);
}

void DisplayLevelAndBrickClearCount()
{
 char String[256];
 sprintf(String,"%02d/%02d",CurrentLevel,MAX_LEVEL_COUNT);
 gotoxy(56,12);puts(String);

 sprintf(String,"%05ld",BrickClearCount);
 gotoxy(60,13);puts(String);
}

void DisplayGameOverMessage(int mode)
{
 gotoxy(50,15);
 if (mode)
  puts("G A M E  O V E R");
 else
  puts("                ");
}

BOOL ProcessKeyAtGame()
{
 int InputKey,tRotationId;
 
 if (kbhit())
 {
  InputKey = getch();
  if ((InputKey == 0xE0 || InputKey == 0))
  {
   InputKey = getch();
   switch (InputKey)
   {
    case LEFT_KEY:
                   if (GetAround(Pos_X-1,Pos_Y,RotationId) == EMPTY)
       {
                        PrintCurrentBrick(FALSE);
                        Pos_X--;
                        PrintCurrentBrick(TRUE);
                   }
                   break;
    case RIGHT_KEY:
                   if (GetAround(Pos_X+1,Pos_Y,RotationId) == EMPTY)
       {
                        PrintCurrentBrick(FALSE);
                        Pos_X++;
                        PrintCurrentBrick(TRUE);
                   }
                   break;
    // Rotation
    case UP_KEY:
                   tRotationId=(RotationId == 3 ? 0:RotationId+1);
                   if (GetAround(Pos_X,Pos_Y,tRotationId) == EMPTY)
       {
                        PrintCurrentBrick(FALSE);
                        RotationId=tRotationId;
                        PrintCurrentBrick(TRUE);
                   }
                   break;
    case DOWN_KEY:
                   if (MoveDown())
       BrickMode = NEW_BRICK_MODE;
                   break;
   }
  }
  else
  {
   switch (InputKey)
   {
    case STOP_KEY:
     DisplayGameOverMessage(true);
     RunningMode = WAITING_GAME_MODE_SELECTION;
     break;
    case ' ':
     while(MoveDown()==FALSE) {;}
     BrickMode = NEW_BRICK_MODE;
     break;  
   }
  }
 }
     return FALSE;
}

void ProcessKeyAtMenu()
{
 int InputKey;
 if (kbhit())
 {
  InputKey = getch();
  switch (InputKey)
  {
   case START_KEY:
    if (RunningMode == WAITING_GAME_MODE_SELECTION)
     RunningMode = INITIAL_TO_START_GAME;
    break;
   case QUIT_KEY:
    RunningMode = QUIT_PROGRAM;
    break;
  }
     }
}

// True: Brick을 현재 위치에 표시
// False: Brick이 표시되었던 이전 위치를 EMPTY로 지움
void PrintCurrentBrick(BOOL Show)
{
 // 현재 출력 위치로 이동
 gotoxy(MAP_START_X+Pos_X*2,MAP_START_Y+Pos_Y);
 puts(arTile[Show ? CurrentBrick[0]:EMPTY]);

 gotoxy(MAP_START_X+(Pos_X+Shape[RotationId].x)*2,
  MAP_START_Y+Pos_Y+Shape[RotationId].y);
 puts(arTile[Show ? CurrentBrick[1]:EMPTY]);
}
 
int GetAround(int x,int y,int r)
{
 int  i,MaxBlockValue;
 Point BrickPosition[COUNT_OF_BRICK];
 for (i=0;i<COUNT_OF_BRICK;i++)
 {
  // 중심점에 있는 Brick
  if (i==0)
  {
   BrickPosition[i].x = x;
   BrickPosition[i].y = y;
  }
  // 상대 좌표에 있는 Brick
  else
  {
   BrickPosition[i].x = x+Shape[r].x;
   BrickPosition[i].y = y+Shape[r].y;
  }
 }
 MaxBlockValue = EMPTY;
 for (i=0;i<COUNT_OF_BRICK;i++)
 {
  // 이미 처리된 brick은 제외함
  if (AliveBrick[i] == false)
   continue;
  if (Board[BrickPosition[i].x][BrickPosition[i].y] > MaxBlockValue)
   MaxBlockValue = Board[BrickPosition[i].x][BrickPosition[i].y];
 }
 return(MaxBlockValue);
}
 
BOOL MoveDown()
{
  if(GetAround(Pos_X,Pos_Y+1,RotationId) == EMPTY)
  {
   PrintCurrentBrick(FALSE);
   Pos_Y++;
   PrintCurrentBrick(TRUE);
   return FALSE;
  }
  else
  {
  // BRICK이 천장에 닿으면 게임 Over
  if (Pos_Y == 1)
  {
   DisplayGameOverMessage(true);
   RunningMode = WAITING_GAME_MODE_SELECTION;
  }
  else
  {
   // 중심점 이외의 brick이 아래에 위치한 경우
   if (Shape[RotationId].y <= 0)
   {
    DownOneBrick(Pos_X,Pos_Y,CurrentBrick[0]);
    DownOneBrick(Pos_X+Shape[RotationId].x,Pos_Y+Shape[RotationId].y,CurrentBrick[1]);
   }
   else
   {
    DownOneBrick(Pos_X+Shape[RotationId].x,Pos_Y+Shape[RotationId].y,CurrentBrick[1]);
    DownOneBrick(Pos_X,Pos_Y,CurrentBrick[0]);
   }
   ClearConnectedBrick();
  }
  }
  return TRUE;
}

void DownOneBrick(int x, int y,int block)
{
 int Scan_Y;
 for (Scan_Y=y;Scan_Y < BOARD_HEIGHT+1;Scan_Y++)
 {
  if (Board[x][Scan_Y+1] >= WALL)
  {
   Board[x][Scan_Y] = block;
   break;
  }
  else
  {
   gotoxy(MAP_START_X+x*2,MAP_START_Y+Scan_Y);
   puts(arTile[EMPTY]);

   gotoxy(MAP_START_X+x*2,MAP_START_Y+Scan_Y+1);
   puts(arTile[block]);
  }
 }
}

void ClearOneBrick(int x, int y)
{
 int scan_y;
 for (scan_y = y;scan_y > 0;scan_y--) // Wall은 제외
 {
  if ((Board[x][scan_y-1] == WALL) || (Board[x][scan_y] == EMPTY))
   break;
  gotoxy(MAP_START_X+x*2,MAP_START_Y+scan_y);
  puts(arTile[Board[x][scan_y-1]]);
  Board[x][scan_y] = Board[x][scan_y-1];
  delay(BRICK_CLEAR_INTERVAL);
 }
}

int BrickConnectionStatus(int x, int y, int CheckBoard)
{
 int Dir,BrickConnectedCount;
 BrickConnectedCount = 0;
 if ((Board[x][y] == CheckBoard) && BrickConnectedTable[x][y])
 {
  // 현 점에서 4방향으로 검색
  for (Dir=0;Dir < NUM_OF_DIRECTION_TO_CHECK;Dir++)
  {
   if ((Board[x+Shape[Dir].x][y+Shape[Dir].y] == CheckBoard) &&
    (BrickConnectedTable[x+Shape[Dir].x][y+Shape[Dir].y] == false))
   {
    // 신규로 연결 검사된 pixel만 계산
    BrickConnectedCount++;
    BrickConnectedTable[x+Shape[Dir].x][y+Shape[Dir].y] = true;    
   }
  }
 }
 return(BrickConnectedCount);
}

void ClearConnectedBrick()
{
 int  i,j,scan_x,scan_y,MaxY,LineHeightCheckNeeded;
 int  Brick_Count_At_Column;
 int  BlockClearProgressed;
 
 LineHeightCheckNeeded = true;
 while (true)
 {
  if (LineHeightCheckNeeded)
  { 
   for (i = 0;i < BOARD_WIDTH+2;i++)
   { 
    Brick_Count_At_Column = 0;
    for (j = BOARD_HEIGHT;j >= 0;j--)
    {
     BrickConnectedTable[i][j] = false;
     if (Board[i][j] >= BRICK)
      Brick_Count_At_Column++;
    }
    Column_Height[i] = Brick_Count_At_Column;
   }
   LineHeightCheckNeeded = false;
   scan_x = 1;
   scan_y = 1;
  }
  BlockClearProgressed = false;
  if (Board[scan_x][scan_y] >= BRICK)
   BlockClearProgressed = BlockedBrickProgress(scan_x,scan_y);

  if (BlockClearProgressed)
   LineHeightCheckNeeded = true;
  else
  {
   scan_x++;
   if (scan_x > BOARD_WIDTH+1)
   {
    scan_x = 1;
    scan_y++;
    if (scan_y > BOARD_HEIGHT+2)
     break;
   }
  }
 }
 
}

int BlockedBrickProgress(int scan_x, int scan_y)
{
 int  i,j,x,y,AllCount=0;
 int  CheckBoard,BrickConnectedCountInLine;
 int  temp_x,temp_y;
 int  BrickConnectedCount,BlockClearedStatus,BrickCountInBlock;

 for (x = 0;x < BOARD_WIDTH+2;x++)
  for (y = 0;y < BOARD_HEIGHT + 2;y++)
   BrickConnectedTable[x][y] = false;

 BrickConnectedTable[scan_x][scan_y] = true;
 BlockClearedStatus = false;

    CheckBoard = Board[scan_x][scan_y];
 temp_y = scan_y;
 // 라인별 Scan수행
 while(true)
 {
  BrickConnectedCountInLine = 0;
  // 우측 방향 SCAN
  for (temp_x = scan_x;temp_x < BOARD_WIDTH+2;temp_x++)
  {
   BrickConnectedCount = BrickConnectionStatus(temp_x,temp_y,CheckBoard);
   BrickConnectedCountInLine += BrickConnectedCount;
  }

  // 좌측 방향 SCAN
  for (temp_x = scan_x-1;temp_x >= 0;temp_x--)
  {
   BrickConnectedCount = BrickConnectionStatus(temp_x,temp_y,CheckBoard);
   BrickConnectedCountInLine += BrickConnectedCount;
  }
  
  if (BrickConnectedCountInLine == 0)
   break;

  // 다음행 검색 실행을 위한 라인 증가
  temp_y++;
  if (temp_y >= BOARD_HEIGHT+2)
   break;
 }

 // 화면 전체에서 연결된 보드의 수 계산
 BrickCountInBlock = 0;
 for (y = 0; y < BOARD_HEIGHT+1;y++)
  for (x = 0;x < BOARD_WIDTH+2;x++)
   if (BrickConnectedTable[x][y])
    BrickCountInBlock++;

 if (BrickCountInBlock >= BRICK_COUNT_OF_CLEAR)
 {
  // 기본 4개 삭제되면 hit 1증가
  BrickClearCount = BrickClearCount + 1;
  
  // 4개보다 많이 삭제하면 combo수만큼 hit수 증가
  if (BrickCountInBlock > BRICK_COUNT_OF_CLEAR)
   BrickClearCount += (BrickCountInBlock - BRICK_COUNT_OF_CLEAR);

  if (MAX_HIT_COUNT <= BrickClearCount)
   BrickClearCount = MAX_HIT_COUNT;
  BrickClearCountChanged = true;
 }

 // BRICK 제거 수행
 if (BrickCountInBlock >= BRICK_COUNT_OF_CLEAR)
 {
  BlockClearedStatus = true;
  for (j = 0; j < BOARD_HEIGHT+2;j++)
   for (i = 0;i < BOARD_WIDTH+2;i++)
   {
    // BRICK Delete
    if (BrickConnectedTable[i][j])
     ClearOneBrick(i,j);
   }
 }
 return(BlockClearedStatus);
}