이 블로그는 방문자 통계를 위해 티스토리 기본 기능과 Woopra를 사용합니다. 원하지 않으신다면 사용하시는 웹 브라우저에 내장된 DNT 헤더를 켜고, JavaScript를 끄셔도 무방합니다.
이 블로그 방문자의 약 60%는 네이버 검색을 사용하십니다. 을 이용하시면 더 유용한 정보를 쉽게 얻게 되실 수도 있습니다. [mediatoday]
« 2018/02 »
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28      
블로그 이미지
제가 주제인 블로그... 그냥 주제 없는 블로그입니다. 전공 분야나 예전 관심 분야 등등에 관한 글이 우선입니다만, 두어 문단을 넘길 만한 글이라면 대강 정리해 기록합니다. 학부생입니다. 트위터에서 볼 수 있습니다. http://aurynj.net/ 어­리


 
 
Notepad2 패치에 발을 담그다 - (1) Mark Occurrences, Case insensitively
http://un-i.tistory.com/entry/Notepad2-Custom-Patch-1-Mark-Occurrences-Case-insensitively
위 링크에 이어지는 글. 위의 패치가 적용되었다고 가정한다.

주의: 이 방법은 ANSI나 UTF-8 인코딩에서만 정상적으로 작동하는 것으로 확인되었다.
UCS-16에서도 한글이나 가나 문자에는 잘 적용되지만, 특정 한자에는 적용되지 않을 수 있다.



Mark Occurrences 기능은 원래 대소문자가 일치하는 전체 단어에 대해서만 강조하게 되어 있다.
이 중 대소문자 일치 여부를 메뉴에서 선택하게 한 것이 패치 (1)이다.


메뉴를 이용해 bOccurrencesCaseSensitive를 결정,

파라미터를 함수로 전달해서 SCI_FINDTEXT에 SCFIND_MATCHCASE를 사용할지 정한 것이다.

한편 SCFIND_WHOLEWORD라는 것이 있는데, 단어 전체의 일치에 대해서만 강조가 작동한다.
위 사진에서 보다시피 boy를 선택하면 boys가 강조되지 않는다.
유럽어권 문장의 특징으로 비추어 보건대 단어 전체 일치만 따지지 않을 필요가 별로 없기 때문이다.

그러나 이게 한국어나 일본어 사용자들에게는 좀 곤란한 게, 조사나 어미가 붙기 때문이다.
심지어 한자 원문의 경우에는 띄어쓰기가 없다. -_-
이걸 위해 소스를 좀 더 건드렸다. 사실 커스텀 패치를 시작한 이유가 이건데 여태 끌어 온 거다. ㄲㄲ
역시 시험 기간이 되면 머리가 잘 돌아가지. 특히 시험 외의 영역에서...

고칠 곳은 한 줄뿐이다.
1. Edit.c

void EditMarkAll(HWND,int,BOOL)에서 다음 내용을 찾아서,

  while ((iPos = (int)SendMessage(hwnd, SCI_FINDTEXT, (bMarkOccurrencesCaseSensitive ? (SCFIND_MATCHCASE | SCFIND_WHOLEWORD) : SCFIND_WHOLEWORD), (LPARAM)&ttf)) != -1
      && ++iMatchesCount < 1000)
다음과 같이 바꾼다.
  while ((iPos = (int)SendMessage(hwnd, SCI_FINDTEXT, (bMarkOccurrencesCaseSensitive ? SCFIND_MATCHCASE | NULL) | (!(pszText[0] >> 7) ? SCFIND_WHOLEWORD : NULL), (LPARAM)&ttf)) != -1
      && ++iMatchesCount < 1000)

다음 글에서는 사용성을 높이기 위한 스마트한(...-_-) 스트링 처리를 해 보도록 한다.



원래 새 글로 올리려 했지만 양심에 찔려서 그냥 추가한다.
드래그 영역 앞뒤의 공백을 선택적으로 지우고,
ASCII 영역에서도 앞의 공백을 포함하며 3글자가 넘으면 단어 첫머리의 일치를 강조한다.
EditMarkAll()의 나름 개궁극판이다.ㅋㅋ....ㅋ..ㅋ.....ㅠㅠㅠㅠㅠ으헝
//=============================================================================
//
//    EditMarkAll()
//    Mark all occurrences of the text currently selected (by Aleksandar Lekov / slightly modified by Un-i-que)
//
void EditMarkAll(HWND hwnd, int iMarkOccurrences, BOOL bMarkOccurrencesCaseSensitive)
{
    struct TextToFind ttf;
    int iPos;
    char  *pszText;
    int iTextLen;
    int iSelStart;
    int iSelEnd;
    int iSelCount;
    int iMatchesCount;

    // feature is off
    if (!iMarkOccurrences)
    {
        return;
    }

    iTextLen = (int)SendMessage(hwnd,SCI_GETLENGTH,0,0);

    // get current selection
    iSelStart = (int)SendMessage(hwnd,SCI_GETSELECTIONSTART,0,0);
    iSelEnd = (int)SendMessage(hwnd,SCI_GETSELECTIONEND,0,0);
    iSelCount = iSelEnd - iSelStart;

    // clear existing indicator
    SendMessage(hwnd, SCI_SETINDICATORCURRENT, 1, 0);
    SendMessage(hwnd, SCI_INDICATORCLEARRANGE, 0, iTextLen);

    // if nothing selected or multiple lines are selected exit
    if (iSelCount == 0 ||
        (int)SendMessage(hwnd, SCI_LINEFROMPOSITION, iSelStart, 0) !=
        (int)SendMessage(hwnd, SCI_LINEFROMPOSITION, iSelEnd, 0))
    {
        return;
    }

    pszText = LocalAlloc(LPTR,iSelCount + 1);
    (int)SendMessage(hwnd,SCI_GETSELTEXT,0,(LPARAM)pszText);

    ZeroMemory(&ttf,sizeof(ttf));

    ttf.chrg.cpMin = 0;
    ttf.chrg.cpMax = iTextLen;
    ttf.lpstrText = pszText;

    if (ttf.lpstrText[1] != '\0' && ttf.lpstrText[0] == ' ' && ttf.lpstrText[1] != ' ')
    {
    iSelCount--;
        ttf.chrg.cpMax--;
        ttf.lpstrText++;
    }
    if (strlen(ttf.lpstrText) > 1 && ttf.lpstrText[strlen(ttf.lpstrText) - 1] == ' ' && ttf.lpstrText[strlen(ttf.lpstrText) - 2] != ' ')
    {
        iSelCount--;
        ttf.chrg.cpMax--;
    }
    ttf.lpstrText[iSelCount] = '\0';

    // set style, green should be greener not to confuse with bookmarks' style
    SendMessage(hwnd, SCI_INDICSETALPHA, 1, iMarkOccurrences == 2 ? 100 : 30);
    SendMessage(hwnd, SCI_INDICSETFORE, 1, 0xff << ((iMarkOccurrences - 1) << 3));
    SendMessage(hwnd, SCI_INDICSETSTYLE, 1, INDIC_ROUNDBOX);

    iMatchesCount = 0;
    while ((iPos = (int)SendMessage(hwnd, SCI_FINDTEXT, (bMarkOccurrencesCaseSensitive ? SCFIND_MATCHCASE : NULL) | (!(ttf.lpstrText[0] >> 7) ? (strlen(ttf.lpstrText) > 2 && !(pszText[0] - ' ') ? SCFIND_WORDSTART : (strlen(ttf.lpstrText) == 1 || ttf.lpstrText[0] - ' ' && ttf.lpstrText[strlen(ttf.lpstrText) - 1] - ' ' ? SCFIND_WHOLEWORD : NULL)) : NULL), (LPARAM)&ttf)) != -1
        && ++iMatchesCount < 1000)
    {
        // mark this match
        SendMessage(hwnd, SCI_INDICATORFILLRANGE, iPos, iSelCount);
        ttf.chrg.cpMin = ttf.chrgText.cpMin + iSelCount;
        if (ttf.chrg.cpMin == ttf.chrg.cpMax)
        break;
    }

    localFree(pszText);
    return;
}
블루앤라이브 님의 Notepad2 4.2.25 패치 중 Notepad2-mod에서 차용된 것이 있으니,
Notepad2 4.2.25 패치 준비#4: Mark Occurrences 기능 추가
링크된 블로그 글에도 있다시피, 다음과 같이 선택된 부분과 일치하는 단어를 강조해 주는 기능이다.
(Notepad2 4,2,25 original w/ Scintilla 2.25 + 'Mark Occurrences' original)
이 기능을 조금 바꾸어, 아래와 같이 대소문자에 무관하게 동작할 수 있도록 했다.
어셈블리어 소스 코드라든지, 같은 단어가 대소문자를 달리해 나타나는 보통의 글 등에서 쓸 수 있다.
물론 메뉴에서 적용 여부를 선택 가능하며, 기본값은 대소문자 구분이다.
참고로, 대부분의 언어에서 식별자는 대소문자를 구분하기 때문에, 이를 빼 버리는 건 미친 짓이다.



블루앤라이브 님과 같은 순서로 설명하며, 기존 패치와 달라진 줄을 강조했다.

1. resource.h

적당한 위치에 아래와 같은 내용을 추가한다. 40445~40449가 사용 중이면 적당히 수정한다.

#define IDM_VIEW_MARKOCCURRENCES_CASE   40445
#define IDM_VIEW_MARKOCCURRENCES_RED    40446
#define IDM_VIEW_MARKOCCURRENCES_GREEN  40447
#define IDM_VIEW_MARKOCCURRENCES_BLUE   40448
#define IDM_VIEW_MARKOCCURRENCES_OFF    40449
* 적당한 위치: 번호 순으로 정렬되어 있으므로 번호 순서에 맞게 넣어야 위험하지 않다.
* 적당히 수정: 사용되지 않는 번호.


2. Notepad2.rc

다음과 같은 내용을 찾아

MENUITEM "Selection &Margin\tCtrl+Shift+M", IDM_VIEW_MARGIN
바로 아래에 다음 내용을 추가한다.
  POPUP "Mar&k Occurrences"
  BEGIN
      MENUITEM "Matc&h case",                 IDM_VIEW_MARKOCCURRENCES_CASE
      MENUITEM SEPARATOR
      MENUITEM "&Red",                        IDM_VIEW_MARKOCCURRENCES_RED
      MENUITEM "&Green",                      IDM_VIEW_MARKOCCURRENCES_GREEN
      MENUITEM "&Blue",                       IDM_VIEW_MARKOCCURRENCES_BLUE
      MENUITEM "&Off",                        IDM_VIEW_MARKOCCURRENCES_OFF
  END


3. Edit.h

다음과 같은 내용을 적당한 위치에 추가한다.

void  EditMarkAll(HWND,int,BOOL);


4. Edit.c

다음과 같은 내용을 적당한 위치에 추가한다.

//=============================================================================
//
//  EditMarkAll()
//  Mark all occurrences of the text currently selected (by Aleksandar Lekov / slightly modified by Un-i-que)
//
void EditMarkAll(HWND hwnd, int iMarkOccurrences, BOOL bMarkOccurrencesCaseSensitive)
{
  struct TextToFind ttf;
  int iPos;
  char  *pszText;
  int iTextLen;
  int iSelStart;
  int iSelEnd;
  int iSelCount;
  int iMatchesCount;
 
  // feature is off
  if (!iMarkOccurrences)
  {
      return;
  }
 
  iTextLen = (int)SendMessage(hwnd,SCI_GETLENGTH,0,0);
 
  // get current selection
  iSelStart = (int)SendMessage(hwnd,SCI_GETSELECTIONSTART,0,0);
  iSelEnd = (int)SendMessage(hwnd,SCI_GETSELECTIONEND,0,0);
  iSelCount = iSelEnd - iSelStart;
 
  // clear existing indicator
  SendMessage(hwnd, SCI_SETINDICATORCURRENT, 1, 0);
  SendMessage(hwnd, SCI_INDICATORCLEARRANGE, 0, iTextLen);
 
  // if nothing selected or multiple lines are selected exit
  if (iSelCount == 0 ||
      (int)SendMessage(hwnd, SCI_LINEFROMPOSITION, iSelStart, 0) !=
      (int)SendMessage(hwnd, SCI_LINEFROMPOSITION, iSelEnd, 0))
  {
      return;
  }
 
  pszText = LocalAlloc(LPTR,iSelCount + 1);
  (int)SendMessage(hwnd,SCI_GETSELTEXT,0,(LPARAM)pszText);
 
  ZeroMemory(&ttf,sizeof(ttf));
 
  ttf.chrg.cpMin = 0;
  ttf.chrg.cpMax = iTextLen;
  ttf.lpstrText = pszText;
 
  // set style, green should be greener not to confuse with bookmarks' style
  SendMessage(hwnd, SCI_INDICSETALPHA, 1, iMarkOccurrences == 2 ? 100 : 30);
  SendMessage(hwnd, SCI_INDICSETFORE, 1, 0xff << ((iMarkOccurrences - 1) << 3));
  SendMessage(hwnd, SCI_INDICSETSTYLE, 1, INDIC_ROUNDBOX);
 
  iMatchesCount = 0;
  while ((iPos = (int)SendMessage(hwnd, SCI_FINDTEXT, (bMarkOccurrencesCaseSensitive ? (SCFIND_MATCHCASE | SCFIND_WHOLEWORD) : SCFIND_WHOLEWORD), (LPARAM)&ttf)) != -1
      && ++iMatchesCount < 1000)
  {
    // mark this match
    SendMessage(hwnd, SCI_INDICATORFILLRANGE, iPos, iSelCount);
    ttf.chrg.cpMin = ttf.chrgText.cpMin + iSelCount;
    if (ttf.chrg.cpMin == ttf.chrg.cpMax)
      break;
  }
 
  LocalFree(pszText);
  return;
}
* Scintilla에 넘기는 인자가 bMarkOccurrencesCaseSensitive에 따라 바뀐다.


5. Notepad2.c

소스의 앞부분, 다른 전역변수들과 함께 다음 두 변수 선언을 추가한다.

int       iMarkOccurrences;
BOOL      bMarkOccurrencesCaseSensitive;

void MsgInitMenu(HWND hwnd,WPARAM wParam,LPARAM lParam) 내의 다음 두 줄 사이에,
  CheckCmd(hmenu,IDM_VIEW_MARGIN,bShowSelectionMargin);
  CheckCmd(hmenu,IDM_VIEW_SHOWWHITESPACE,bViewWhiteSpace);
다음과 같은 내용을 추가한다.
  CheckCmd(hmenu,IDM_VIEW_MARKOCCURRENCES_CASE,bMarkOccurrencesCaseSensitive);
  switch (iMarkOccurrences)
  {
    case 0: i = IDM_VIEW_MARKOCCURRENCES_OFF;break;
    case 1: i = IDM_VIEW_MARKOCCURRENCES_RED;break;
    case 2: i = IDM_VIEW_MARKOCCURRENCES_GREEN;break;
    case 3: i = IDM_VIEW_MARKOCCURRENCES_BLUE;break;
  }
  CheckMenuRadioItem(hmenu,IDM_VIEW_MARKOCCURRENCES_RED,IDM_VIEW_MARKOCCURRENCES_OFF,i,MF_BYCOMMAND);

LRESULT MsgCommand(HWND hwnd,WPARAM wParam,LPARAM lParam) 내의 case IDM_VIEW_MARGIN: ~ break;과 case IDM_VIEW_SHOWWHITESPACE: ~ break; 사이에 다음과 같은 내용을 추가한다.
    case IDM_VIEW_MARKOCCURRENCES_CASE:
      bMarkOccurrencesCaseSensitive = (bMarkOccurrencesCaseSensitive) ? FALSE : TRUE;
      // clear all marks and remark with changed case sensitivity
      SendMessage(hwndEdit, SCI_SETINDICATORCURRENT, 1, 0);
      SendMessage(hwndEdit, SCI_INDICATORCLEARRANGE, 0, (int)SendMessage(hwndEdit,SCI_GETLENGTH,0,0));
      EditMarkAll(hwndEdit, iMarkOccurrences, bMarkOccurrencesCaseSensitive);
      break;
    
    case IDM_VIEW_MARKOCCURRENCES_OFF:
      iMarkOccurrences = 0;
      // clear all marks
      SendMessage(hwndEdit, SCI_SETINDICATORCURRENT, 1, 0);
      SendMessage(hwndEdit, SCI_INDICATORCLEARRANGE, 0, (int)SendMessage(hwndEdit,SCI_GETLENGTH,0,0));
      break;
 
    case IDM_VIEW_MARKOCCURRENCES_RED:
      iMarkOccurrences = 1;
      EditMarkAll(hwndEdit, iMarkOccurrences, bMarkOccurrencesCaseSensitive);
      break;
 
    case IDM_VIEW_MARKOCCURRENCES_GREEN:
      iMarkOccurrences = 2;
      EditMarkAll(hwndEdit, iMarkOccurrences, bMarkOccurrencesCaseSensitive);
      break;
 
    case IDM_VIEW_MARKOCCURRENCES_BLUE:
      iMarkOccurrences = 3;
      EditMarkAll(hwndEdit, iMarkOccurrences, bMarkOccurrencesCaseSensitive);
      break;

LRESULT MsgNotify(HWND hwnd,WPARAM wParam,LPARAM lParam) 내에서 다음 내용을 찾아서,
// Brace Match
if (bMatchBraces)
이 바로 위에 다음과 같은 내용을 추가한다.
// mark occurrences of text currently selected
EditMarkAll(hwndEdit, iMarkOccurrences, bMarkOccurrencesCaseSensitive);

void LoadSettings() 내에서 다음 내용을 찾아서,
bViewWhiteSpace = IniSectionGetInt(pIniSection,L"ViewWhiteSpace",0);
이 바로 위에 다음과 같은 내용을 추가한다.
  bMarkOccurrencesCaseSensitive = IniSectionGetInt(pIniSection,L"MarkOccurrencesCaseSensitively",1);
  if (bMarkOccurrencesCaseSensitive) bMarkOccurrencesCaseSensitive = 1;

  iMarkOccurrences = IniSectionGetInt(pIniSection,L"MarkOccurrences",3);

마지막으로 void SaveSettings() 내의 다음 두 줄 사이에,
  IniSectionSetInt(pIniSection,L"ShowLineNumbers",bShowLineNumbers);
  IniSectionSetInt(pIniSection,L"ViewWhiteSpace",bViewWhiteSpace);
다음 두 줄을 추가한다.
  IniSectionSetInt(pIniSection,L"MarkOccurrencesCaseSensitively",bMarkOccurrencesCaseSensitive);
  IniSectionSetInt(pIniSection,L"MarkOccurrences",iMarkOccurrences);


시험적으로 구상한 패치 소스 패치(!)였지만 놀랍게도 성공적으로 작동했다.(?)
나도 이제 소스 패치 변태야 엉엉 ㅠㅠ 하라는 과제는 안 하고

앞으로 몇 가지 패치를 더 만들어 낼지도 모르겠습니다. :)


다음 글. 한글은 전영역에서 찾는다든지 하는 옵션이 추가되어 있는데 정신 건강에 좋지는 않습니다.
지환태 님의 오목 게임 소스를 처음 접한 것은 올해 중순이었을 겁니다.

오목을 만들어 보겠다는 생각 자체를 하지 않았기 때문에 당연히 소스에 관심이 갔고,
소스 자체가 그다지 깔끔하지 못해서 리팩토링(refactoring)을 해 보고 싶었습니다.
(환타[지환태] 님께서도 C언어를 배운지 얼마 안 되었을 때 만들었다고 고백하셨습니다.)

처음엔 허락도 받지 않고 시작한 재구성인데 환타 님께서 관심을 가져 주셨으며,
다섯 번에 걸친 재구성(R1R2R3R4R5)과 추가 수정으로, 나름 깔끔한 코드를 만들어 봤습니다.

아래가 드디어 최종본입니다.
제가 만든 코드는 하나의 메인 C 파일과 두 헤더 H 파일로 이루어져 있습니다.
지난번 재구성에 비해 소스와 파일명이 아주 약간 달라졌습니다.

game.c 펼치기...


five_in_a_row.h 펼치기...


five_in-tool.h 펼치기...


A infinite number of monkeys typing into GNU emacs would never make a good program.
— Linus Benedict Torvalds(1969-12-28 ~), Linux 1.3.53 CodingStyle documentation

그러나 다행히도 포스팅할 것은 아직 많이 남아 있다죠.

환타 오목 게임 재구성. (5)

Sablog Models/자작자작 | 2009.11.12 10:59 | Posted by 어­리
드디어 check()를 줄일 아이디어를 구체화시켰습니다.-_-


현재 방식 그대로라면 모듈로 분리한다 해도 거의 아무런 차이가 없으므로,

아예 검사 방식 자체를 바꾸려고 했습니다만 그게 잘 안 되고 있었습니다.


뻘소리를 좀 하자면 오늘은 2010 수능 D-day이면서 2011 수능 D-364 즉 52주...

어차피 블로깅이 많이 줄었지만 앞으로 1년간 거의 잠수 탈 것 같습니다.


새로운 검사 방식을 소개합니다.

1. 검사 방식에 있어서 생긴 차이는 아닙니다만,
일단 check()는 x와 y 인자만 받습니다.
turn 인자가 필요한데, (x, y) 인자를 정상적으로 받았다고 가정하면,
board[x][y]가 turn 인자와 같습니다.

즉,

int check(int x, int y, int turn)
{
    int px, py;
    int count;

    // 가로로 오목이 있는지 검사
    ...
}

이것을,
int check(int x, int y)
{
    int px, py;
    int count;
    int turn = board[x][y];

    // 가로로 오목이 있는지 검사
    ....
}

이렇게 바꿔도 된다는 것입니다. 물론, move()에서
int move()
{
    ...
    else if(board[x][y] == 0)
    {
        board[x][y] = turn;
        gotoxy(x * 2, y);
        puts(stone[turn]);
        if(check(x, y, turn))
            return 0;
        turn ^= 3;
    }
    return 1;
}

이 부분을,
        if(check(x, y))

이렇게 바꾸는 것도 잊지 않아야 되겠습니다.


2. 진짜 변화입니다. 크크크.
환타[지환태] 님은 검사할 때 아래의 방법을 썼습니다.
  • 횡오목: 왼쪽으로 최대한 거슬러 간 후 오른쪽으로 하나씩 세기
  • 종오목: 위쪽으로 최대한 거슬러 간 후 아래로 하나씩 세기
  • 좌상우하오목: 왼쪽 위로 최대한 거슬러 간 후 오른쪽 아래로 하나씩 세기
  • 우상좌하오목: 오른쪽 위로 최대한 거슬러 간 후 왼쪽 아래로 하나씩 세기
전체가 하나의 루틴에 순차적으로 있어서 상당히 비효율적이라고 생각하고 있었습니다.
반면에 새로 생각해 낸 방법은 이것입니다.
  • 방향을 입력받아 세는 함수(인자: 8방향)
  • 좌표를 입력받아 점검하는 함수(인자: 좌표)
    1. 8방향별로 좌표를 입력받아 세는 함수를 호출한다.
    2. 반환된 값을 각각 상+하, 좌+우, 좌상+우하, 우상+좌하 합해서 오목을 검사한다.
물론 누군가 이미 쓰고 있는 방법이라면(=...) 하는 수 없지만 말입니다.


이 작업을 위해 count()라는 함수를 만들었습니다.
int count(int x, int y, int turn, int direction)
{
    int n = 0;

    switch(direction)
    {
    case 0: // 상
        while(y > 0 && board[x][--y] == turn)
            n++;
        break;
    case 1: // 우상
        while(x < BOARD_SIZE && y > 0 && board[++x][--y] == turn)
            n++;
        break;
    case 2: // 우
        while(x < BOARD_SIZE && board[++x][y] == turn)
            n++;
        break;
    case 3: // 우하
        while(x < BOARD_SIZE && y < BOARD_SIZE && board[++x][++y] == turn)
            n++;
        break;
    case 4: // 하
        while(y < BOARD_SIZE && board[x][++y] == turn)
            n++;
        break;
    case 5: // 좌하
        while(x > 0 && y < BOARD_SIZE && board[--x][++y] == turn)
            n++;
        break;
    case 6: // 좌
        while(x > 0 && board[--x][y] == turn)
            n++;
        break;
    case 7: // 좌상
        while(x > 0 && y > 0 && board[--x][--y] == turn)
            n++;
        break;
    default:
        n = 0;
        break;
    }

    return n;
}
상당히 뻘짓하는 것으로 보일 수도 있으나,
이렇게 변화를 주면 check()의 전체는 아래처럼 획기적으로 바뀝니다!
int check(int x, int y)
{
    int turn = board[x][y];
	if (count(x, y, turn, 0) + count(x, y, turn, 4) > 3 ||
		count(x, y, turn, 1) + count(x, y, turn, 5) > 3 ||
		count(x, y, turn, 2) + count(x, y, turn, 6) > 3 ||
		count(x, y, turn, 3) + count(x, y, turn, 7) > 3 )
		return 1;
	else
		return 0;
}

이제 저는 여기서 리팩토링을 마무리하려고 합니다.

블로그에 글을 채우기 위해-_- 시작한,
거의 공중분해에 가까운 재구성을 보고도 양해해 주신 환타 님 감사합니다.

다음 글에서 세 부분으로 이루어진 파일 전체를 글에 싣고 끝내겠습니다.
제가 스텔로라는 카페에서 활동 중인데, 쉽게 말해서(?) 언어를 창작하는 곳입니다.

제가 사실상 (매니저 빼면) 최초 멤버인데다 가장 꾸준해서-_-

활동 회원들을 주도하고 있습니다.


그곳에 최근에 전파되어 온 프로그램 중에서 ConlangDictionary(0.2)라는 게 있는데,

처음에 Zompist.com의 한 사용자가 만들어 올렸다고 하며 소스도 공개되어 있습니다.

굳이 인공 언어(Conlang, Constructed Language)를 위한 사전은 아니지만...

언어의 바닥이라고 할 수 있는 음성 어휘를 파일에 관리해 주는 어플리케이션입니다.


인코딩 포맷을 자동으로(윈도는 US-ASCII 소위 ANSI로..-_-) 결정해 버려서,

덕분에 몇 가지 발음 기호들(가장 기본적인 ɸ나 ŋ마저도!!)이 저장이 안 되는 문제가 있지만,

일단 얼마 정도 괜찮은 프로그램입니다.


그런데 아시다시피 ㅋㅋ 제가 바이너리 어플리케이션을 피하는 성격인데다,

최근에 카페의 어떤 분께서 프로그램 실행 중 오류가 생겼다고 어려움을 호소하시더군요.

그래서 일단 .cdic 파일을 notepad로 열어 보았고 충분히 구분이 되었지만,

아무래도 어플에서 보는 것보다는 불편해서 xml 포맷으로 바꿔 볼 궁리를 하게 되었습니다!


다만 공개된 소스 코드가 맥용인 것 같아서 잘 분석이 되지 않습니다.-_-;;

원래 남의 소스 코드 공중분해는 자신있었는데 생판 모르는 라이브러리를 만나니 병신=ㅁ=이 되네요.



그래서 일단 파일 포맷을 충분히 연구하고 나서 이를 그냥 알아서 토큰으로 분리할 계획입니다.

소스 분석은 나중에 하고요. (훗.)

환타 오목 게임 재구성. (4)

Sablog Models/자작자작 | 2009.09.17 21:13 | Posted by 어­리
이 코드를 리팩토링하기 시작한 후로 상당히 많은 시간이 흘렀는데....

제대로 된 개선은 한 적이 없다.

미쳤구나.

시간이 많던 방학 동안에는 아예 안 건드렸다.



원래 check()를 바꿔 보려고 했습니다만 더 최적화시키기는 어려워 보입니다.-_-;;


대신에 move()의 길이를 좀 더 줄여 보도록 하겠습니다.


/* five_in_a_row.h
 * by ZFanta & UNique
 */
#ifndef FIVE_IN_A_ROW
#define FIVE_IN_A_ROW


#include "tool.h"

#define UP 72
#define DOWN 80
#define LEFT 75
#define RIGHT 77
#define ENTER 13
#define ESC 27
#define BOARD_SIZE 19

int x = 0, y = 0;
int oldx = 0, oldy = 0;
int board[BOARD_SIZE][BOARD_SIZE];
int turn = 1;
char who[3][3] = {"", "흑", "백"};
char stone[3][3] = {"", "○", "●"};

int check(int x, int y, int turn)
{
    int px, py;
    int count;

    // 가로로 오목이 있는지 검사
    px = x;
    py = y;
    count = 0;

    while(px > 1 && board[px-1][y] == turn)
        px--;
    while(px < BOARD_SIZE && board[px][y] == turn)
    {
        px++;
        count++;
    }

    if(count == 5)
        return 1;


    // 세로로 오목이 있는지 검사
    px = x;
    py = y;
    count = 0;

    while(py > 0 && board[x][py-1] == turn)
        py--;
    while(py < BOARD_SIZE && board[x][py++] == turn)
        count++;

    if(count == 5)
        return 1;


    // 좌상우하로 오목이 있는지 검사
    px = x;
    py = y;
    count = 0;

    while(px > 0 && py > 0 && board[px-1][py-1] == turn)
        px--, py--;
    while(px < BOARD_SIZE && py < BOARD_SIZE && board[px][py++] == turn)
    {
        px++;
        count++;
    }

    if(count == 5)
        return 1;


    // 우상좌하로 오목이 있는지 검사
    px = x;
    py = y;
    count = 0;

    while(px < BOARD_SIZE && py > 0 && board[px+1][py-1] == turn)
        px++, py--;
    while(px >= 0 && py < BOARD_SIZE && board[px][py++] == turn)
    {
        px--;
        count++;
    }

    if(count == 5)
        return 1;

    return 0;
}

int knock(int x, int y)
{
    if(board[x][y] != 0)
    {
        gotoxy(x * 2, y);
        puts(stone[board[x][y]]);
        return 0;
    }
    else
        return 1;
}

void load(int x, int y)
{
    gotoxy(x * 2, y);
    puts("⊙");
}

void board_write(int x, int y)
{
    x *= 2;
    gotoxy(x, y);
    if (x == 0)
    {
        if (y == 0)
            puts("┌");
        else if (y == (BOARD_SIZE - 1))
            puts("└");
        else
            puts("├");
    }
    else if (x == (BOARD_SIZE - 1) * 2)
    {
        if (y == 0)
            puts("┐");
        else if (y == (BOARD_SIZE - 1))
            puts("┘");
        else
            puts("┤");
    }
    else
    {
        if (y == 0)
            puts("┬");
        else if (y == (BOARD_SIZE - 1))
            puts("┴");
        else
            puts("┼");
    }
}

void draw()
{        
    int i, j;
    for (i = 0; i < BOARD_SIZE; i++)
        for (j = 0; j < BOARD_SIZE; j++)
            board_write(i, j);
}

int move()
{
    char input = 0;
    input = getch();

    if(input != ENTER)
    {
        oldx = x;
        oldy = y;
        if(input == UP && y > 0)
            y--;
        else if(input == DOWN && y < BOARD_SIZE - 1)
            y++;
        else if(input == LEFT && x > 0)
            x--;
        else if(input == RIGHT && x < BOARD_SIZE - 1)
            x++;
        if(knock(oldx, oldy))
            board_write(oldx, oldy);
        load(x, y);
    }
    else if(board[x][y] == 0)
    { 
        board[x][y] = turn;
        gotoxy(x * 2, y);
        puts(stone[turn]);
        if(check(x, y, turn))
            return 0;
        turn ^= 3;
    }

    return 1;
}

int five_in_a_row()
{
    /* If user wants to re-match, returns 0; or not, returns 1.
     * (only if one game successfully ended)
     */
    int i, j;
    system("CLS");
    draw();
    for (i = 0; i < BOARD_SIZE; i++)
        for (j = 0; j < BOARD_SIZE; j++)
            board[i][j] = 0;
    load(x, y);
    while (move());
    system("CLS");
    printf("%s 승리.\n계속하려면 아무 키나 누르십시오(종료 Esc):", who[turn]);
    if (getch() == ESC)
        return 1;
    else
        return 0;
}


#endif

-_-;;

여전히 코드 수준이 그다지 나아지지 않고 있는 것 같군요.


날로 먹는 이 느낌은 뭘까요.

+ 아, 그래도 251 Line에서 212 Line으로 40줄 가량 줄였습니다.
거의 goto에 버금가는 C언어의 switch-case도 없애 버리고요.

환타 오목 게임 재구성. (3)

Sablog Models/자작자작 | 2009.06.13 23:20 | Posted by 어­리
일단 지난 번에 제가 바꿔 놓은 코드에 좀 문제가 있습니다.
누가 이겼는지 안 나오더군요.. 잘 되더니 왜 그러지;;

뭐 좀 바꿨더니 다시 됩니다만.
/* five_in_a_row.h
 * by ZFanta & UNique
 */
#ifndef FIVE_IN_A_ROW
#define FIVE_IN_A_ROW


#include "tool.h"

#define UP 72
#define DOWN 80
#define LEFT 75
#define RIGHT 77
#define ENTER 13
#define ESC 27

#define BOARD_SIZE 19

int x = 0, y = 0;
int oldx = 0, oldy = 0;
int board[BOARD_SIZE][BOARD_SIZE];
int turn = 1;
char who[3][3] = {"", "흑", "백"};
char stone[3][3] = {"", "○", "●"};

int check(int x, int y, int turn)
{
    int px, py;
    int count;

    // 가로로 오목이 있는지 검사
    px = x;
    py = y;
    count = 0;

    while(px > 1 && board[px-1][y] == turn)
        px--;
    while(px < BOARD_SIZE && board[px][y] == turn)
    {
        px++;
        count++;
    }

    if(count == 5)
        return 1;


    // 세로로 오목이 있는지 검사
    px = x;
    py = y;
    count = 0;

    while(py > 0 && board[x][py-1] == turn)
        py--;
    while(py < BOARD_SIZE && board[x][py++] == turn)
        count++;

    if(count == 5)
        return 1;


    // 좌상우하로 오목이 있는지 검사
    px = x;
    py = y;
    count = 0;

    while(px > 0 && py > 0 && board[px-1][py-1] == turn)
        px--, py--;
    while(px < BOARD_SIZE && py < BOARD_SIZE && board[px][py++] == turn)
    {
        px++;
        count++;
    }

    if(count == 5)
        return 1;


    // 우상좌하로 오목이 있는지 검사
    px = x;
    py = y;
    count = 0;

    while(px < BOARD_SIZE && py > 0 && board[px+1][py-1] == turn)
        px++, py--;
    while(px >= 0 && py < BOARD_SIZE && board[px][py++] == turn)
    {
        px--;
        count++;
    }

    if(count == 5)
        return 1;

    return 0;
}

int knock(int x, int y)
{
    if(board[x][y] != 0)
    {
        gotoxy(x * 2, y);
        puts(stone[board[x][y]]);
        return 0;
    }
    else
        return 1;
}

void board_write(int x, int y)
{
    x *= 2;
    gotoxy(x, y);
    if (x == 0)
    {
        if (y == 0)
            puts("┌");
        else if (y == (BOARD_SIZE - 1))
            puts("└");
        else
            puts("├");
    }
    else if (x == (BOARD_SIZE - 1) * 2)
    {
        if (y == 0)
            puts("┐");
        else if (y == (BOARD_SIZE - 1))
            puts("┘");
        else
            puts("┤");
    }
    else
    {
        if (y == 0)
            puts("┬");
        else if (y == (BOARD_SIZE - 1))
            puts("┴");
        else
            puts("┼");
    }
}

void draw()
{        
    int i, j;
    for (i = 0; i < BOARD_SIZE; i++)
        for (j = 0; j < BOARD_SIZE; j++)
            board_write(i, j);
}

int move()
{
    char input = 0;
    input = getch();

    switch(input)
    {
    case UP :
        if(y > 0)
        {
            oldy = y--;
            gotoxy(x * 2, y);
            puts("⊙");
            if(knock(x, oldy))
                board_write(x, oldy);
        }
        return 1;
        break;

    case DOWN :
        if(y < BOARD_SIZE - 1)
        {
            oldy = y++;
            gotoxy(x * 2, y);
            puts("⊙");
            if(knock(x, oldy))
                board_write(x, oldy);
        }
        return 1;
        break;

    case LEFT :
        if(x > 0)
        {
            oldx = x--;
            gotoxy(x * 2, y);
            puts("⊙");
            if(knock(oldx, y))
                board_write(oldx, y);
        }
        return 1;
        break;

    case RIGHT :
        if(x < BOARD_SIZE - 1)
        {
            oldx = x++;
            gotoxy(x * 2, y);
            puts("⊙");
            if(knock(oldx, y))
                board_write(oldx, y);

        }
        return 1;
        break;

    case ENTER :
        if(board[x][y] == 0)
        { 
            board[x][y] = turn;
            gotoxy(x * 2, y);
            puts(stone[turn]);
            if(check(x, y, turn))
            {
                return 0;
            }
            turn++;
            if(turn == 3)
                turn = 1;
        }
        return 1;
        break;
    }
    return 1;
}

int five_in_a_row()
{
    /* If user wants to re-match, returns 0; or not, returns 1.
     * (only if one game successfully ended)
     */
    int i, j;
    system("CLS");
    draw();
    for (i = 0; i < BOARD_SIZE; i++)
        for (j = 0; j < BOARD_SIZE; j++)
            board[i][j] = 0;
    while (move());
    system("CLS");
    printf("%s 승리.\n계속하려면 아무 키나 누르십시오(종료 Esc):", who[turn]);
    if (getch() == ESC)
        return 1;
    else
        return 0;
}


#endif
그나저나 제 VC++에서 갑자기 system(CLS)가 안 먹습니다. 하하하하하....

멋지군요-_-

이번에는 계획을 멋대로 벗어나서 함수를 하나 더 만들어 보겠습니다.
void load(int x, int y)
{
	gotoxy(x * 2, y);
	puts("⊙");
}

그러면 소스를 이따구로 바꿀 수 있겠죠.
/* five_in_a_row.h
 * by ZFanta & UNique
 */
#ifndef FIVE_IN_A_ROW
#define FIVE_IN_A_ROW


#include "tool.h"

#define UP 72
#define DOWN 80
#define LEFT 75
#define RIGHT 77
#define ENTER 13
#define ESC 27

#define BOARD_SIZE 19

int x = 0, y = 0;
int oldx = 0, oldy = 0;
int board[BOARD_SIZE][BOARD_SIZE];
int turn = 1;
char who[3][3] = {"", "흑", "백"};
char stone[3][3] = {"", "○", "●"};

int check(int x, int y, int turn)
{
    int px, py;
    int count;

    // 가로로 오목이 있는지 검사
    px = x;
    py = y;
    count = 0;

    while(px > 1 && board[px-1][y] == turn)
        px--;
    while(px < BOARD_SIZE && board[px][y] == turn)
    {
        px++;
        count++;
    }

    if(count == 5)
        return 1;


    // 세로로 오목이 있는지 검사
    px = x;
    py = y;
    count = 0;

    while(py > 0 && board[x][py-1] == turn)
        py--;
    while(py < BOARD_SIZE && board[x][py++] == turn)
        count++;

    if(count == 5)
        return 1;


    // 좌상우하로 오목이 있는지 검사
    px = x;
    py = y;
    count = 0;

    while(px > 0 && py > 0 && board[px-1][py-1] == turn)
        px--, py--;
    while(px < BOARD_SIZE && py < BOARD_SIZE && board[px][py++] == turn)
    {
        px++;
        count++;
    }

    if(count == 5)
        return 1;


    // 우상좌하로 오목이 있는지 검사
    px = x;
    py = y;
    count = 0;

    while(px < BOARD_SIZE && py > 0 && board[px+1][py-1] == turn)
        px++, py--;
    while(px >= 0 && py < BOARD_SIZE && board[px][py++] == turn)
    {
        px--;
        count++;
    }

    if(count == 5)
        return 1;

    return 0;
}

int knock(int x, int y)
{
    if(board[x][y] != 0)
    {
        gotoxy(x * 2, y);
        puts(stone[board[x][y]]);
        return 0;
    }
    else
        return 1;
}

void load(int x, int y)
{
    gotoxy(x * 2, y);
    puts("⊙");
}

void board_write(int x, int y)
{
    x *= 2;
    gotoxy(x, y);
    if (x == 0)
    {
        if (y == 0)
            puts("┌");
        else if (y == (BOARD_SIZE - 1))
            puts("└");
        else
            puts("├");
    }
    else if (x == (BOARD_SIZE - 1) * 2)
    {
        if (y == 0)
            puts("┐");
        else if (y == (BOARD_SIZE - 1))
            puts("┘");
        else
            puts("┤");
    }
    else
    {
        if (y == 0)
            puts("┬");
        else if (y == (BOARD_SIZE - 1))
            puts("┴");
        else
            puts("┼");
    }
}

void draw()
{        
    int i, j;
    for (i = 0; i < BOARD_SIZE; i++)
        for (j = 0; j < BOARD_SIZE; j++)
            board_write(i, j);
}

int move()
{
    char input = 0;
    input = getch();

    switch(input)
    {
    case UP :
        if(y > 0)
        {
            oldy = y--;
            load(x, y);
            if(knock(x, oldy))
                board_write(x, oldy);
        }
        return 1;
        break;

    case DOWN :
        if(y < BOARD_SIZE - 1)
        {
            oldy = y++;
            load(x, y);
            if(knock(x, oldy))
                board_write(x, oldy);
        }
        return 1;
        break;

    case LEFT :
        if(x > 0)
        {
            oldx = x--;
            load(x, y);
            if(knock(oldx, y))
                board_write(oldx, y);
        }
        return 1;
        break;

    case RIGHT :
        if(x < BOARD_SIZE - 1)
        {
            oldx = x++;
            load(x, y);
            if(knock(oldx, y))
                board_write(oldx, y);

        }
        return 1;
        break;

    case ENTER :
        if(board[x][y] == 0)
        { 
            board[x][y] = turn;
            gotoxy(x * 2, y);
            puts(stone[turn]);
            if(check(x, y, turn))
            {
                return 0;
            }
            turn++;
            if(turn == 3)
                turn = 1;
        }
        return 1;
        break;
    }
    return 1;
}

int five_in_a_row()
{
    /* If user wants to re-match, returns 0; or not, returns 1.
     * (only if one game successfully ended)
     */
    int i, j;
    system("CLS");
    draw();
    for (i = 0; i < BOARD_SIZE; i++)
        for (j = 0; j < BOARD_SIZE; j++)
            board[i][j] = 0;
    load(x, y);
    while (move());
    system("CLS");
    printf("%s 승리.\n계속하려면 아무 키나 누르십시오(종료 Esc):", who[turn]);
    if (getch() == ESC)
        return 1;
    else
        return 0;
}


#endif

커서가 처음부터 뜹니다.

환타 오목 게임 재구성. (2)

Sablog Models/자작자작 | 2009.06.07 21:10 | Posted by 어­리

지난번 글에서는 move() 함수는 'move'답게 만들고

다른 함수가 move()를 반복하여 한 판을 조정하여,

main()에서는 판을 반복하게 했습니다.


물론 제가 five_in_a_row()에 단 주석과 약간 다릅니다.

제 주석에 따르면 플레이어가 재경기를 원할 때 재경기를 시켜 줘야 합니다.

게다가 원래 게임과도 약간 달라져 버렸지요.

그.러.나.


그 점은 미뤄 두고, 일단 다른 부분을 시작하도록 하죠. (-_-ㅗ;;)


이번에는 판을 그리는 부분을 바꾸어 보겠습니다.

switch-case 부분이 너무 길어서 저 내부를 한 함수로 빼낼 수 있으면 좋겠군요.

#define BOARD_SIZE 19

void board_write(int x, int y)
{
    x *= 2;
    gotoxy(x, y);
    if (x == 0)
    {
        if (y == 0)
            puts("┌");
        else if (y == (BOARD_SIZE - 1))
            puts("└");
        else
            puts("├");
    }
    else if (x == (BOARD_SIZE - 1) * 2)
    {
        if (y == 0)
            puts("┐");
        else if (y == (BOARD_SIZE - 1))
            puts("┘");
        else
            puts("┤");
    }
    else
    {
        if (y == 0)
            puts("┬");
        else if (y == (BOARD_SIZE - 1))
            puts("┴");
        else
            puts("┼");
    }
}


판 크기는 맨 위에 따로 정의한 후,

전각 문자의 크기로 인한 문제는 board_write()에서 해결하도록 합니다.

move()에서는 x도 한 칸씩만 움직이게 해야겠습니다.

그러면 check()에서도 돌이 일렬로 있는지 검사하기 위해 x를 1씩만 움직여도 되고,

코드를 오히려 줄일 수 있습니다.


또한 move()의 switch-case의 각 방향키 case 안에서,

필요 없는 연산을 수행하고 있습니니다.

예를 들자면,

y += 1;
oldy = y - 1;

이 있겠습니다. 이런 건,

oldy = y;
y++;
(더 줄여서 oldy = y++;)

로 만들 수 있습니다.

이런 식으로 코드를 2차 개선하면 five_in_a_row.h는 아래와 같이 바뀝니다.

/* five_in_a_row.h
 * by ZFanta & UNique
 */
#ifndef FIVE_IN_A_ROW
#define FIVE_IN_A_ROW


#include "tool.h"

#define UP 72
#define DOWN 80
#define LEFT 75
#define RIGHT 77
#define ENTER 13

#define BOARD_SIZE 19

int x = 0, y = 0;
int oldx = 0, oldy = 0;
int board[BOARD_SIZE][BOARD_SIZE];
int turn = 1;
char who[3][3] = {"", "흑", "백"};
char stone[3][3] = {"", "○", "●"};

int check(int x, int y, int turn)
{
    int px, py;
    int count;

    // 가로로 오목이 있는지 검사
    px = x;
    py = y;
    count = 0;

    while(px > 1 && board[px-1][y] == turn)
        px--;
    while(px < BOARD_SIZE && board[px][y] == turn)
    {
        px++;
        count++;
    }

    if(count == 5)
        return 1;


    // 세로로 오목이 있는지 검사
    px = x;
    py = y;
    count = 0;

    while(py > 0 && board[x][py-1] == turn)
        py--;
    while(py < BOARD_SIZE && board[x][py++] == turn)
        count++;

    if(count == 5)
        return 1;


    // 좌상우하로 오목이 있는지 검사
    px = x;
    py = y;
    count = 0;

    while(px > 0 && py > 0 && board[px-1][py-1] == turn)
        px--, py--;
    while(px < BOARD_SIZE && py < BOARD_SIZE && board[px][py++] == turn)
    {
        px++;
        count++;
    }

    if(count == 5)
        return 1;


    // 우상좌하로 오목이 있는지 검사
    px = x;
    py = y;
    count = 0;

    while(px < BOARD_SIZE && py > 0 && board[px+1][py-1] == turn)
        px++, py--;
    while(px >= 0 && py < BOARD_SIZE && board[px][py++] == turn)
    {
        px--;
        count++;
    }

    if(count == 5)
        return 1;

    return 0;
}

int knock(int x, int y)
{
    if(board[x][y] != 0)
    {
        gotoxy(x * 2, y);
        puts(stone[board[x][y]]);
        return 0;
    }
    else
        return 1;
}

void board_write(int x, int y)
{
    x *= 2;
    gotoxy(x, y);
    if (x == 0)
    {
        if (y == 0)
            puts("┌");
        else if (y == (BOARD_SIZE - 1))
            puts("└");
        else
            puts("├");
    }
    else if (x == (BOARD_SIZE - 1) * 2)
    {
        if (y == 0)
            puts("┐");
        else if (y == (BOARD_SIZE - 1))
            puts("┘");
        else
            puts("┤");
    }
    else
    {
        if (y == 0)
            puts("┬");
        else if (y == (BOARD_SIZE - 1))
            puts("┴");
        else
            puts("┼");
    }
}

void draw()
{        
    int i, j;
    for (i = 0; i < BOARD_SIZE; i++)
        for (j = 0; j < BOARD_SIZE; j++)
            board_write(i, j);
}

int move()
{
    char input = 0;
    input = getch();

    switch(input)
    {
    case UP :
        if(y > 0)
        {
            oldy = y--;
            gotoxy(x * 2, y);
            puts("⊙");
            if(knock(x, oldy))
                board_write(x, oldy);
        }
        return 1;
        break;

    case DOWN :
        if(y < BOARD_SIZE - 1)
        {
            oldy = y++;
            gotoxy(x * 2, y);
            puts("⊙");
            if(knock(x, oldy))
                board_write(x, oldy);
        }
        return 1;
        break;

    case LEFT :
        if(x > 0)
        {
            oldx = x--;
            gotoxy(x * 2, y);
            puts("⊙");
            if(knock(oldx, y))
                board_write(oldx, y);
        }
        return 1;
        break;

    case RIGHT :
        if(x < BOARD_SIZE - 1)
        {
            oldx = x++;
            gotoxy(x * 2, y);
            puts("⊙");
            if(knock(oldx, y))
                board_write(oldx, y);

        }
        return 1;
        break;

    case ENTER :
        if(board[x][y] == 0)
        { 
            board[x][y] = turn;
            gotoxy(x * 2, y);
            puts(stone[turn]);
            if(check(x, y, turn))
            {
                return 0;
            }
            turn++;
            if(turn == 3)
                turn = 1;
        }
        return 1;
        break;
    }
    return 1;
}

int five_in_a_row()
{
    /* If user wants to re-match, returns 0; or not, returns 1.
     * (only if one game successfully ended)
     */
    int i, j;
    system("CLS");
    draw();
    for (i = 0; i < BOARD_SIZE; i++)
        for (j = 0; j < BOARD_SIZE; j++)
            board[i][j] = 0;
    while (move());
    system("CLS");
    printf("%s 승리\n", who[turn]);
    system("PAUSE");

    return 0;
}


#endif

뭐 내친 김에 draw()도 write_board()를 이용하도록 바꾸어 버렸습니다.


이번에 move() 바꾸는 게 가장 중요한 작업인데 어느 정도 성공입니다.

지난번 20줄 정도 늘렸던 게 이번에 거기서 110줄 정도 줄었군요.

뭐 C 파일에서 분리하고, 헤더 하나 더 만든 것까지 포함하면 아직 멀었지만...



다음에는 check()을 좀 바꿔 봐야겠습니다.

환타 오목 게임 재구성. (1)

Sablog Models/자작자작 | 2009.06.06 19:29 | Posted by 어­리

지환태 님의 미니홈피에서 가져온 오목 게임입니다.
상당히 끌려서 말입니다-_-; 도와드리지 않고는 못 배기겠더군요.(?)

일단 아래는 원본.


접었습니다. 짜증을 감수하고 보시려면 클릭.


상태가 약간 이뭐병입니다. 전역변수는 물론이고 덜떨어진 가독성이 돋보이는군요.

제가 만든다고 얼마나 나을지는 모르겠습니다만,

이 소스는 무개념으로 충만하다 못해 아주 넘치는 것 같아요.


.....-_- 장난이었구요, 죄송합니다.



코드를 살펴보면 구조가 상당히 난해합니다.

일단 main()에서 move()를 무한 반복해서 모든 프로세싱을 넘겨 줍니다.

이 때 move()는 한 번에 커서 한 칸 움직이거나 돌 한 번 놓기밖에 수행할 줄 모릅니다.

전역변수 x, y, oldx, oldy, who[3][3]가 move()에서만 쓰임에도 불구하고,

전역변수로 선언되어 있는 것은 move 내에 선언하면 변수가 계속 바뀌기 때문입니다.



아무래도 판을 수행하는 부분은 main()에 남겨 놓고,

커서를 움직이거나 돌을 움직이는 부분은 move()에 남겨 놓되,

판의 승패를 결정하고 move()를 반복하는 부분은 또 다른 함수로 바꾸어야 되겠습니다.

새로운 함수의 이름을 five_in_a_row()로 합시다.


근데 상당히 짜증나네요.

나머지 함수들은 모조리 헤더 파일 하나에 몰아 넣어 봐야 되겠습니다.

그럼 C 파일의 내용은 일단....

#include "five_in_a_row.h"

int main()
{
    while (!(five_in_a_row()));
    return 0;
}

애석하게도 대충 이따구로 변형할 수 있겠습니다.

그리고 아래는 five_in_a_row.h입니다.
/* five_in_a_row.h
 * by ZFanta & UNique
 */
#ifndef FIVE_IN_A_ROW
#define FIVE_IN_A_ROW


#include "tool.h"

#define UP 72
#define DOWN 80
#define LEFT 75
#define RIGHT 77
#define ENTER 13

int x = 0, y = 0;
int oldx = 0, oldy = 0;
int board[38][19];
int turn = 1;
char who[3][3] = {"", "흑", "백"};
char stone[3][3] = {"", "○", "●"}; 


void draw()
{        
    char a[39][39]=
    {
        "┌┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┐",
        "├┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┼┤",
        "└┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┘"
    };
    int count;
    printf("%s\n", a[0]); 
    for(count = 0; count < 17; count++)
        printf("%s\n", a[1]);
    printf("%s", a[2]);
}

int check(int x, int y, int turn)
{
    int px, py;
    int count;

    //가로
    px = x;
    py = y;
    count = 0;    

    while(px > 1 && board[px-2][y] == turn)
        px -= 2;
    while(px <= 36 && board[px][y] == turn)
    {
        count++;
        px += 2;
    }

    if(count == 5)
    {
        return 1;
    }

    //세로
    px = x;
    py = y;
    count = 0;

    while(py > 0 && board[x][py-1] == turn)
        py--;
    while(py <= 18 && board[x][py++] == turn)
        count++;

    if(count==5)
    {
        return 1;
    }

    //대각선 ↘
    px = x;
    py = y;
    count = 0;

    while(px > 0 && py > 0 && board[px-2][py-1] == turn)
    {
        px -= 2;
        py--;
    }

    while(px <= 36 && py <= 18 && board[px][py++] == turn)
    {
        px += 2;
        count++;
    }
    if(count == 5)
    {
        return 1;
    }

    //대각선 ↙
    px = x;
    py = y;
    count = 0;

    while(px < 36 && py > 0 && board[px+2][py-1] == turn)
    {
        px += 2;
        py--;
    }

    while(px >= 0 && py <= 18 && board[px][py++] == turn)
    {
        px -= 2;
        count++;
    }
    if(count == 5)
    {
        return 1;
    }
    return 0;
}

int knock(int x, int y)
{
    gotoxy(x,y);
    puts(stone[board[x][y]]);
    if(board[x][y] != 0)
        return 0;

    else
        return 1;
}

int move()
{
    char input = 0;
    input = getch();

    switch(input)
    {
    case UP :
        if(y>0)
        {
            y -= 1;
            oldy = y + 1;
            gotoxy(x, y);
            puts("⊙");
            if(knock(x, oldy))
            {
                gotoxy(x, oldy);
                if(oldy == 18)
                {
                    if(x == 0)
                    {
                        puts("└");
                    }
                    if(x == 36)
                    {
                        puts("┘");
                    }
                    else if(x != 0 && x != 36)
                    {
                        puts("┴");
                    }
                }
                else if(x == 0)
                {
                    puts("├");
                }
                else if(x == 36)
                {
                    puts("┤");
                }
                else
                {
                    puts("┼");
                }
            }

        }
        return 1;
        break;

    case DOWN :
        if(y<18)
        {
            y+=1;
            oldy=y-1;
            gotoxy(x,y);
            puts("⊙");
            if(knock(x, oldy))
            {
                gotoxy(x, oldy);
                if(oldy == 0)
                {
                    if(x == 0)
                    {
                        puts("┌");
                    }
                    if(x == 36)
                    {
                        puts("┐");
                    }
                    else if(x != 0 && x != 36)
                    {
                        puts("┬");
                    }
                }
                else if(x == 0)
                {
                    puts("├");
                }
                else if(x == 36)
                {
                    puts("┤");
                }
                else
                {
                    puts("┼");
                }
            }

        }
        return 1;
        break;

    case LEFT :
        if(x>0)
        {
            x -= 2;
            oldx = x + 2;
            gotoxy(x, y);
            puts("⊙");
            if(knock(oldx, y))
            {
                gotoxy(oldx, y);
                if(oldx == 36)
                {
                    if(y == 0)
                    {
                        puts("┐");
                    }
                    if(y == 18)
                    {
                        puts("┘");
                    }
                    else if(y != 0 && y != 36)
                    {
                        puts("┤");
                    }
                }
                else if(y == 18)
                {
                    puts("┴");
                }
                else if(y == 0)
                {
                    puts("┬");
                }
                else
                {
                    puts("┼");
                }
            }

        }
        return 1;
        break;

    case RIGHT :
        if(x<36)
        {
            x += 2;
            oldx = x-2;
            gotoxy(x, y);
            puts("⊙");
            if(knock(oldx, y))
            {
                gotoxy(oldx, y);
                if(oldx == 0)
                {
                    if(y == 0)
                    {
                        puts("┌");
                    }
                    if(y == 18)
                    {
                        puts("└");
                    }
                    else if(y != 0 && y != 36)
                    {
                        puts("├");
                    }
                }
                else if(y == 18)
                {
                    puts("┴");
                }
                else if(y == 0)
                {
                    puts("┬");
                }
                else
                {
                    puts("┼");
                }
            }

        }
        return 1;
        break;

    case ENTER :
        if(board[x][y] == 0)
        { 
            board[x][y] = turn;
            gotoxy(x, y);
            puts(stone[turn]);
            if(check(x, y, turn))
            {
                return 0;
            }
            turn++;
            if(turn == 3)
                turn = 1;
        }
        return 1;
        break;
    }
    return 1;
}

int five_in_a_row()
{
    /* If user wants to re-match, returns 0; or not, returns 1.
     * (only if one game successfully ended)
     */
    int i, j;
    system("CLS");
    draw();
    for (i = 0; i < 38; i++)
        for (j = 0; j < 19; j++)
            board[i][j] = 0;
    while (move());
    system("CLS");
    printf("%s 승리", who[turn]);
    system("PAUSE");

    return 0;
}


#endif

위에 보면 다른 헤더 파일이 없고 tool.h만 있습니다.

getch 관련해서 꼬박 반나절을 삽질한 탓에 열받아서 분리시켜 버렸죠.
/* tool.h (for five_in_a_row.h)
 * supports gotoxy(x, y) and getch()
 */
#include <stdio.h>
#include <windows.h>

void gotoxy(int x, int y)
{
    COORD XY = {x, y};
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), XY);
}

#if defined(_MSC_VER) && (_MSC_VER > 1000)
_CRTIMP _getch();
#define getch() _getch()
#elif defined(__GNUC__) || defined(__GNUG__)
#include <termios.h>
int getch()
{
    char ch;
	struct termios buffer, status;

    tcgetattr( STDIN_FILENO, &buffer );
    status = buffer;
    status.c_lflag &= ~(ECHO | ICANON);
    tcsetattr( STDIN_FILENO, TCSANOW, &status );
    read( 0, &ch, 1 );
    tcsetattr( STDIN_FILENO, TCSANOW, &buffer );

    return ch;
}
#else
#include <conio.h>
#endif


일단 제 코딩 스타일로 가독성을 약간 높였습니다.

여전히 저 switch-case 제어문은 쩔어주게 기네요. 아니 더 길어진 듯?

저걸 처리할 방법을 다음 글에서 다루도록 하죠.