E D R , A S I H C RSS

Mine Finder

1. 소개

  • 이름 : Mine Finder
  • 참여 : 강석천 (99, 1002)
  • 기간 : 2002. 2. 10 ~ 2.23 (문서화 작업과정 포함해서.. 2주 걸릴듯.)
  • 목표 : 윈도우의 지뢰찾기 프로그램과 직접 대화, 지뢰를 스스로 찾아내는 프로그램을 만든다.
  • 개발툴 : Visual C++ 6.0, cppunit 1.62, SPY++, 지뢰찾기 2000, 98버전
  • 동기 : 심심해서. 손목저려서. -_-;
  • 개발방법 : XP 의 일부분 소폭적용.
  • 시스템 : 듀론 1G 256RAM WIN 2000

2. 계획

2.1. schedule

특별히 잡은바 없음. --;
2월 16일까지 프로그래밍 완료. 2월 23일까지 문서화 완료.
  • 목표수정 - 뜻지 않은 문제로. -_-; 2월 28일. 말일까지는 어떻게든! --;

2.2. 기능 spec

  • 윈98, 윈2000 지뢰찾기 프로그램 지원 (현재 2000 은 제대로 지원. 98 쪽 호환성 높이기중)
  • 추후 프로그램이 커질 경우 '눈' 부분과 '지능' 부분을 따로 빼낼 수 있도록 궁리.
    • '눈' 해당 부분 - 지뢰찾기 프로그램으로부터 비트맵을 얻어 데이터로 변환는 루틴 관련부. 현재 bitmap 1:1 matching 부분이 가장 부가 많이 걸리는 부분으로 확인됨에 따라, 가장 개선해야 할 부분.
    • '지능' 해당 부분 - 변환된 데이터를 근거로 해야 할 행동을 결정는 부분. 기본적인 형태는 유한상태머신을 띈다.
      • 추후 DP 로 확장된다면 StrategyPatternStatePattern 등이 이용될 것 같지만. 이는 추후 Refactoring 해 나가면서 생각해볼 사항. 프로그램이 좀 더 커지고 Refactoring 이 이루어진다면 DLL 부분으로 빠져나올 수 있을듯. ('빠져나와야 할 상황이 생길듯' 이 더 정확지만. -_-a)

3. 진행 상황

  • 2월 10일
    • 지뢰찾기 프로그램의 윈도우 핸들을 얻고 해당 메세지를 보내어서 지뢰찾기 프로그램을 구동는 루틴 관련 SpikeSolution. (아.. UnitTest 코드 넣기가 애매해서 안넣었다. 궁리해봐야 할 부분같다.)
    • 지뢰찾기 프로그램의 윈도우 핸들을 얻은뒤 DC를 얻은후 화면 캡쳐. 그리고 캡쳐한 비트맵을 근거로 여 데이터로 변환는 루틴 관련 SpikeSolution
  • 2월 11일
    • Main Design, Algorithm 1차 완성. 어느정도 Refactoring의 추구.
  • 2월 13일
    • 알고리즘 최적화 궁리. 3가지정도 대안 모색.
    • 98 버전의 지뢰찾기와 2000 버전의 지뢰찾기가 비트맵데이터가 달라서 생기는 문제 어느정도 해결.
    • 현재 속도
      • Begineer mode 최고기록 1초, 평균 4-7초.
      • Middle mode 최고기록 21초, 평균 30~50초 안쪽.
      • Expert mode 최고기록 151초. 단, 깰 수 있는 확률 낮음. -_-; 아. 확률높은 찍기 알고리즘이 필요다는. --;
  • 2월 26일
    • 도큐먼트 추가중. (일주일을 푸욱 놀았다는. -_-;)
  • 2월 27일
    • Expert mode 51초, Middle mode 11초 기록. 알고리즘 최적화에 대한 다른 관점 잡음. (지만, 여전히 깰 수 있는 확률 낮음)
  • 2월 28일, 3월 1일
    • Expert mode 깰 수 있는 확률을 높임. 최적화내에서 해결할 방법은 더 힘들듯. 98과의 호환성 향상문제 해결이후 종료 예정.
  • 3월 1일 종료.

4. 종료후 감상기(?)

  • 디자인 부분에서의 TFP의 중요성을 놓친것이 화근이 되었다는. -_-; 추후 알고리즘 부분으로 들어가면서 TFP를 게을리 한 점과 프로그램 돌아간다는 점에서의 즐거움에 시간낭비가 좀 심했다는..~
  • 미션 크리티컬한 문제였다면 그냥 넘어가면 안될 일이지만. -_-; 장난감 가지고 노는 기분으로 한 일이였던지라.~ 그리 무게감을 가지고 한 일이 아닌 관계로 특별히 나쁘진 않았다.
  • 현실에서 가상으로 다시 현실로. 암튼 '1002 보기에 좋았더라'. 여전히 멍청한 넘이고 주사위 던지는 넘이지만 (오호.. Random Open 때 주사위 돌리는 애니메이션을 넣을까. ^^;)
  • 지금쯤 다시 짜라고 한다면 TFP를 좀 더 제대로 추구할 수 있을 것도 같다. (이 점에서 TFP를 할때 SpikeSolution 에 대한 어느정도의 충분한 시간을 두는 점이 좋을 것 같다는 생각이 들었다. 초기 SpikeSolution 으로 해당 부분을 간단게 대강 해보고, Test를 할 수 있는 부분에 대한 구체화기.)
  • CppUnit 를 쓸때에는 MFC 라이브러리들이 static 으로 링킹이 안되는 것 같다. 좀 더 살펴봐야겠다.

5. 참조 문서

  • MSDN
----
형 신기해~ ㅎㅎㅎ bitmap data로 숫자들과 거시기들을 구분한건가..ㅡㅡa.. spy 좋구만..

오오.. Artificial Intelligence.. -_- 근데 저 스펠링이 맞나..

1002 뭐. 어차피 노가다를 해도 컴터가 는 것을. -_-v 이로서 즐기게 되는 게임이 나 줄어버리는건가. --;; A.I. 라고 붙이기엔 너무 단순해서 좀 쪽 팔리는군. --;


6. Opening

습관성으로 여는 프로그램 Best: 1. Explorer 2. 프리셀 3. 지뢰찾기. -_-;

NSISIde 소스를 만지작 거리던중 피곤해서 지뢰찾기를 게 되었다. 조옴 무리를 했는지(?) 손목이 저려오기 시작했다. 그러다가 갑자기 '퍽' 고 동시 다발적으로 여러가지 생각을 게 되었는데, 나는 예전에 학교에서 열렸던 '선배님들과의 만남' 에서 소프트캠프에 있는 환국선배가 했던 말이였다.

프로그래밍이 뭐라고 생각세요? 여러가지 답이 나올수 있겠지만, 저는 현실세계에 있는 것들을 가상세계로 모델링는 것이라고 생각했어요.

자.. 노가다를 고 있는 나의 저려오는 손목을 모델링해보자. -_-..

7. User Story

  • 지뢰찾기 프로그램은 윈도우에 기본적으로 내장된 프로그램을 이용한다.
  • 기본적으로는 Begineer Mode 만을 지원한다.
  • 컴퓨터는 현재의 지뢰찾기 프로그램 상황을 알아서 판단고, 해당 행동을 결정한다.
  • 컴퓨터가 실패했을 경우 자동으로 다시 시작여, 사용자가 중지시키거나 지뢰를 다 찾을때까지 프로그램을 계속 진행시킨다.

8. 간단 작업 준비

  1. Spy++
  2. Visual C++ (2개 열어두고 쓴다. 나는 해당 부분부분 연습용 코드 만들 곳. 나는 메인소스 만들 곳)
  3. 인덱스 카드 & 필기구 - CRC 를 쓰건 UserStory를 쓰건..~ 단,
  4. 지뢰찾기
  5. CppUnit - 이번 플밍때 윈도우 메세지 관련 처리에 대해서는 코드를 작성못했다. (이 부분에 대해서는 전통적인 Manual Test방법을 쓸 수 밖에. GUI Testing 관련 글을 더 읽어봐야 겠다. 아직 더 지식이 필요다.) 단, 나중에 비트맵 분석부분 & Refactoring 시에 TFP 를 할 수 있게 되었다.

9. Start. 어디서 부터 잡아갈까.. 데이터 모으기

글쌔. 무엇부터 해 나가야 할 것인가. 일단은 지뢰찾기 프로그램을 제어할 수 있는 프로그램이여야 고, 지뢰찾기 알고리즘도 필요할테고.. 우어. 정신없다. 일단은 생각나는 것들에 대해 나 잡아봐야겠다.

일단, 소위 말는 '꺼리' 들을 찾기 위해 Spy++ 을 실행시켰다.



지뢰찾기 프로그램의 윈도우클래스 이름이 '지뢰 찾기' 였다. 윈도우 OS 의 특징상 해당 윈도우 핸들간 메세지의 발생에 따라 해당 윈도우프로시저에서 처리가 된다. 해당 윈도우 핸들은 윈도우 클래스 이름을 아는 이상 FindWindow 함수를 이용해서 찾으면 될 것이다.

각각 메뉴들에 대해 처리를 기 위해 Message 를 캡쳐해봤다.



beginner 에 해당는 메뉴클릭시 발생는 메세지는 WM_COMMAND 이고, ID는 wParam 으로 521이 날라간다. 즉, 해당 메뉴의 ID가 521 인 것이다. (우리는 컨트롤 아이디를 쓰지만 이는 resource.h 에서 알 수 있듯 전부 #define 매크로 정의이다.) 각각 찾아본 결과, 521,522,523 이였다.

지뢰 버튼을 열고 깃발체크를 위한 마우스 클릭시엔 WM_LBUTTONDOWN, WM_RBUTTONDOWN 이고, 단 ? 체크관련 옵션이 문제이니 이는 적절게 처리해주면 될 것이다. 마우스클릭은 해당 Client 부분 좌표를 잘 재어서 이를 lParam 에 넘겨주면 될 것이다.

손에 대한 모델링이여서 그런지 손에만 집착게 되었군. -_-; 이 일을 위해서는 손, 눈, 머리가 있어야 겠는데. 마우스 노가다를 위한 손, 해당 지뢰찾기 상태를 봐야 할 눈, 그리고 해당 상황에 따른 판단, 지시를 해야 할 머리의 모델링. (단, 여기에 각각에 대해 조건을 붙인다면 '지뢰찾기프로그램을 위한' 이라는 말이 붙겠지만.)

눈에 해당는 부분은 어떻게 할까.. 나는 신이 되는 방법이 있고 나는 사람이 되는 방법이 있다. -_-; 즉, 나는 직접 지뢰찾기 프로그램의 메모리부분을 얻어낸 뒤, 그중에 배열에 해당되는 부분 (어떤 데이터구조일지는 모르겠지만, 배열일 것 같다. -_-;)을 얻어내서 보던지, 아니면 사람처럼 화면을 봐야 할 것이다. 애석게도 나는 지뢰찾기의 창조자도 아니고 윈도우의 창조자는 더더욱 아니므로. -_-; 후자를 선택게 된다.

원리는 간단다. 윈도우 핸들을 얻을 수 있다면, 해당 윈도우에 대한 DC를 얻을 수 있을 것이다. DC를 얻을 수 있다면, BitBlt 을 이용, 비트맵을 메모리DC 쪽으로 복사할 수 있을테니까. (간단한 캡쳐 프로그램시 이용할 수 있다.) 단, 화면을 복사려는 프로그램이 다른 프로그램에 가려지면 안되겠다.

머리는? 지뢰찾기 알고리즘에 해당되는 부분은. 으흐~ 나중에. -_-; 대강 이쯤 해서 각 부분부분에 대해 맞는지를 알아보기 위한 프로그램을 간단게 짰다.

  • 1차일부분코드 - 손과 눈에 해당는 부분 코드를 위한 간단한 예제코드들 모음. 그리고 지뢰찾기 프로그램을 제어는 부분들에 대해 Delegation 시도. (CMinerControler 클래스는 처음 '막 짠' 코드로부터 지뢰찾기 제어부분 함수들을 클래스화한것임)

10. User Story & Engineering Task

여기서는 Task Estmiate 를 생략했다. (그냥 막 나간지라. ^^;)
  • 지뢰찾기 프로그램은 윈도우에 기본적으로 내장된 프로그램을 이용한다.
    • 현재 열려있는 프로그램 중에서 지뢰찾기 프로그램을 윈도우 클래스 이름으로 찾아낸다.
    • 지뢰찾기 프로그램을 조작한다. (Mode 변환, 재시작, 지뢰체크, 빈칸 열기 등)
      • 지뢰찾기 프로그램에게 해당 행동 Message를 보낸다.
    • 지뢰찾기 프로그램의 화면을 Capture, 분석한뒤 데이터화한다.

  • 기본적으로는 Begineer Mode 만을 지원한다.
  • 컴퓨터는 현재의 지뢰찾기 프로그램 상황을 알아서 판단고, 해당 행동을 결정한다.
    • 지뢰찾기 프로그램 비트맵 데이터를 근거로 수치데이터화한다.
      • 게임 시작 & 게임중인 상태 확인
      • 게임 실패인 상태 확인
      • 게임 클리어인 상태 확인
      • 빈칸, 깃발체크, 숫자들 등 Cell들에 대한 상태 확인
    • 지뢰찾기 판단 알고리즘을 만든다.
      • 깃발 체크를 해야 할 때
      • 빈칸을 열어야 할 때
      • 빈칸 찍기를 해야 할 때

  • 컴퓨터가 실패했을 경우 자동으로 다시 시작여, 사용자가 중지시키거나 지뢰를 다 찾을때까지 프로그램을 계속 진행시킨다.
    • 게임 실패인 상태에 대한 확인기.
    • 프로그램을 다시 시작도록 조작기.

11. First

  • 1차제작소스
    • 유의 : 이때는 Windows ME, Windows 2000 이상에서만 실행가능다. (지뢰찾기 비트맵이 98과 다르다)
    • 기록 : 초급 8-9초, 중급 90-100초, 고급쪽은 너무 느려서 테스트 포기. -_-;

처음에는 전체에 대한 환경설정을 게 된다. 기본적인 클래스는 다음과 같다.
CMinerBitmapAnalyzer 비트맵을 분석, 데이터화한다.
CMinerController 지뢰찾기 프로그램에 대한 화면 캡쳐, 모드변환, 버튼 클릭 등의 제어를 한다
CMineSweeper 실질적인 두뇌에 해당되는 부분. CMinerControllerCMinerBitampAnalyzer 를 멤버로 가지며, 이를 이용여 게임상황분석, 지뢰찾기관련 판단 등을 한다

일종의 애니메이션을 는 캐릭터와 같다. 타이머가 Key Frame 에 대한 이벤트를 주기적으로 걸어주고, 해당 Key Frame 에는 현재 상태에 대한 판단을 한뒤 동작을 한다. 여기서는 1초마다 MineSweeper 의 동작을 수행게 된다.
~cpp 
void CMinerFinderDlg::OnButtonStart() 
{
	// TODO: Add your control notification handler code here
	m_mineSweeper.Start ();
	SetTimer (1000, 100, NULL);
}

void CMinerFinderDlg::OnTimer(UINT nIDEvent) 
{
	// TODO: Add your message handler code here and/or call default
	int nRet = Excute ();
	if (nRet == MINERMODE_CLEAR) 
		KillTimer (1000);
	
	CDialog::OnTimer(nIDEvent);
}

void CMinerFinderDlg::OnButtonStop() 
{
	// TODO: Add your control notification handler code here
	KillTimer (1000);
}

int CMinerFinderDlg::Excute ()
{
	return m_mineSweeper.Excute ();
}

MineSweeper 의 Excute 의 방법은 일종의 유한 상태 머신의 형태이다. 단지 Switch ~ Case 만 쓰지 않았을뿐이지만. 그리 큰 장점이 보이지는 않은 관계로 일단은 그냥 이렇게.


~cpp 
int CMineSweeper::Excute ()
{
	CBitmap* pBitmap = CaptureClient ();

	ConvertBitmapToGameMode (pBitmap);
	ConvertBitmapToData (pBitmap);

	if (m_nCurrentGameMode == MINERMODE_GAMEOVER) {
		Start ();
		return m_nCurrentGameMode;
	}

	// Todo : Matrix 를 근거로 여 할 일의 설정.
	// 할 일 : 
	int nRet = 0;

	CMinerFinderDlg* pDlg = (CMinerFinderDlg*)m_pDlg;
	
	//		Flag 의 체크. 횟수세기.
	nRet = CheckFlag ();
	if (nRet) {
		pDlg->PrintStatus ("Action : CheckFlag - %d rn", nRet);
		return m_nCurrentGameMode;
	}

	//		Open. 횟수세기.
	nRet = OpenBlocks ();
	if (nRet) {
		pDlg->PrintStatus ("Action : OpenBlocks - %d rn", nRet);
		return m_nCurrentGameMode;
	}

	//		찍기. --;
	RandomOpen ();
	pDlg->PrintStatus ("Action : Random Open rn");

	return m_nCurrentGameMode;
}

11.1. Profiling

~cpp 
Profile: Function timing, sorted by time
Date:    Tue Feb 26 19:16:26 2002


Program Statistics
------------------
    Command line at 2002 Feb 26 19:00: "F:WorkingTempMinerFinderDebugMinerFinder" 
    Total time: 223521.679 millisecond
    Time outside of functions: 28.613 millisecond
    Call depth: 23
    Total functions: 660
    Total hits: 37532338
    Function coverage: 52.1%
    Overhead Calculated 5
    Overhead Average 5

Module Statistics for minerfinder.exe
-------------------------------------
    Time in module: 223493.066 millisecond
    Percent of time in module: 100.0%
    Functions in module: 660
    Hits in module: 37532338
    Module function coverage: 52.1%

        Func          Func+Child           Hit
        Time   %         Time      %      Count  Function
---------------------------------------------------------
   86270.513  38.6    86270.513  38.6 18252086 CDC::GetPixel(int,int) (mfc42d.dll)
   53966.631  24.1   223296.921  99.9      855 CWinThread::PumpMessage(void) (mfc42d.dll)
   28819.313  12.9   150892.058  67.5    93243 CMinerBitmapAnalyzer::CompareBitmapBlock(class CDC *,class CDC *,int,int,int,int,class CDC *,int,int) (minerbitmapanalyzer.obj)
   18225.064   8.2    61776.601  27.6  9126043 CMinerBitmapAnalyzer::CompareBitmapPixel(class CDC *,class CDC *,int,int,unsigned long) (minerbitmapanalyzer.obj)
   17577.168   7.9    17577.168   7.9  9126043 CDC::SetPixel(int,int,unsigned long) (mfc42d.dll)
    5146.101   2.3    53721.330  24.0      104 CMineSweeper::CheckFlag(void) (minesweeper.obj)
    4748.690   2.1     4974.806   2.2       74 CMineSweeper::OpenBlocks(void) (minesweeper.obj)
    2496.582   1.1     2506.333   1.1       27 CMineSweeper::RandomOpen(void) (minesweeper.obj)
    1504.775   0.7     1504.775   0.7    85245 CDC::DeleteDC(void) (mfc42d.dll)
     944.596   0.4      944.596   0.4    85245 CDC::CreateCompatibleDC(class CDC *) (mfc42d.dll)
     492.930   0.2   111257.841  49.8    32492 CMinerBitmapAnalyzer::ConvertBitmapToBlock(int,int) (minerbitmapanalyzer.obj)
     383.392   0.2      383.392   0.2    85245 CDC::SelectObject(class CBitmap *) (mfc42d.dll)
     348.243   0.2      348.243   0.2    42702 CWnd::ReleaseDC(class CDC *) (mfc42d.dll)
     326.552   0.1      326.552   0.1      221 CWnd::SetWindowTextA(char const *) (mfc42d.dll)
     294.378   0.1      295.578   0.1     2619 CWnd::DefWindowProcA(unsigned int,unsigned int,long) (mfc42d.dll)
     251.949   0.1      251.949   0.1    42702 CWnd::GetDC(void) (mfc42d.dll)
     201.741   0.1      225.112   0.1      221 CEdit::LineScroll(int,int) (mfc42d.dll)
     184.900   0.1      184.900   0.1   208404 CMineSweeper::GetData(int,int) (minesweeper.obj)
     155.178   0.1    42649.225  19.1     9944 CMinerBitmapAnalyzer::ConvertBitmapToNumber(int,int) (minerbitmapanalyzer.obj)
     134.201   0.1   154139.803  69.0      157 CMineSweeper::ConvertBitmapToData(class CBitmap *) (minesweeper.obj)

위의 결과를 보면, 가장 많이 호출되어 시간을 점유는 것은 GetPixelPumpMessage이다. mfc의 함수와 윈도우 메세지드리븐 방식에 대해서는 수정할 수 없다 더라도, 해당 함수에 대해서 호출 횟수를 줄이는 방법은 강구해야 할 것이다.

GetPixel 은 어디서 호출될까? Edit->Find in Files 를 면 해당 프로젝트내에서 GetPixel이 쓰인 부분들에 대해 알 수 있다.
~cpp 
Searching for 'GetPixel'...
F:WorkingTempMinerFinderMinerBitmapAnalyzer.cpp(65):	if (bmpdc->GetPixel (bmpStartX,bmpStartY) != rgbColor)
F:WorkingTempMinerFinderMinerBitmapAnalyzer.cpp(135):			rgb = screendc->GetPixel (nX+nBi,nY+nBj);
2 occurrence(s) have been found.

GetPixel은 다음과 같은 화면 캡쳐로 얻은 비트맵에 대해 비교여 같은 데이터임을 판독는 부분에 쓰였다.
~cpp 
BOOL CMinerBitmapAnalyzer::CompareBitmapPixel (CDC* pDC, CDC* bmpdc, int bmpStartX, int bmpStartY, COLORREF rgbColor) 
{
	if (bmpdc->GetPixel (bmpStartX,bmpStartY) != rgbColor)
		return FALSE;
	else
		return TRUE;
}

BOOL CMinerBitmapAnalyzer::CompareBitmapBlock (CDC* pDC, CDC* screendc, int nX, int nY, int nWidth, int nHeight, CDC* bmpdc, int nSrcX, int nSrcY)
{
	int nBi, nBj;
	COLORREF rgb;

	for (nBi=0;nBi<nWidth;nBi++) {
		for (nBj=0;nBj<nHeight;nBj++) {
			rgb = screendc->GetPixel (nX+nBi,nY+nBj);
			pDC->SetPixel (nX+nBi,nY+nBj,rgb);
			
			if (!CompareBitmapPixel (pDC, bmpdc, nSrcX + nBi, nSrcY + nBj, rgb)) return FALSE;
		}
	}

	return TRUE;
}
즉, 저 함수들을 최적화 시키던지, 아니면 저 함수들이 호출되는 횟수를 줄여야 는 것이다.

12. Second - 최적화의 시도

  • 2차제작소스
    • 유의 : 여전히 Windows ME, Windows 2000 이상에서만 실행가능다. (지뢰찾기 비트맵이 98과 다르다)
    • 기록 : 초급 2-3초, 중급 21~40초, 고급쪽은 너무 느려서 테스트 포기. -_-;
    • 반성 : 차라리 순수 TFP 로 해 나갈걸 그랬다는 생각이 든다. 테스트 코드에 대한 아이디어를 제대로 못내었다. (아.. 타성에 젖으면 안되건만. --; TFP중 막힐때 예전방식으로 플밍려고 는게 문제이다. -_-;)

12.1. 최적화 방안의 궁리

목표는 Func time 이 많이 걸리는 함수들에 대해서 그 횟수를 줄이는 방법.
  • 비트맵의 종류가 한정적이라는 점에서 착안, Block 전체에 대한 비교대신 한줄에 대한 비교로 바꿨다. (블럭마다 숫자들의 색이 다르므로, 이를 이용면 속도 향상을 이끌어 낼 수 있겠다.)

~cpp 
BOOL CMinerBitmapAnalyzer::CompareBitmapCenterLine (CDC* screendc, int nX, int nY, int nWidth, int nHeight, CDC* bmpdc, int nSrcX, int nSrcY)
{
	int nBi, nBj;
	COLORREF rgb;
	nBj = int(nHeight / 2);

	// for optimizing...
	for (nBi=int(nWidth / 4);nBi<nWidth-int(nWidth / 4);nBi++) {
		rgb = screendc->GetPixel (nX+nBi,nY+nBj);
	
		if (!CompareBitmapPixel (bmpdc, nSrcX + nBi, nSrcY + nBj, rgb)) return FALSE;
	}

	return TRUE;
}
  • Flag 체크부분 효율화 - 기존의 방식은 Flag 을 나씩 체크고 난 뒤, 다시 전체 비트맵을 읽어오는 방식이였다. 이에 대해 Flag 를 체크할 수 있는 부분에 대해 한꺼번에 체크도록 알고리즘을 수정했다. 이는 전체 비트맵을 읽어오는 횟수를 줄여주므로, 효율성이 높아진다.

~cpp 
int CMineSweeper::CheckFlag ()
{
	int nCount = 0;
	int nCheckCount = 0;

	CMinerFinderDlg* pDlg = (CMinerFinderDlg*)m_pDlg;

	for (int i=0;i<m_nArrMaxY;i++) {
		for (int j=0;j<m_nArrMaxX;j++) {
			if (IsNumber (GetData (j,i))) {
				// 숫자인 경우. 
				nCount = GetAroundClosedCount (j,i) + GetAroundFlagCount (j,i);

				if (nCount == GetData(j,i) && GetAroundClosedCount (j,i)) {
					SetFlagsAround (j,i);
					
					// CBitmap* pBitmap = CaptureClient ();
					// ConvertBitmapToData (pBitmap);

					nCheckCount ++;
				}
			}
		}
	}

	return nCheckCount;
}

13. Third - 최적화 시도 2

  • 3차제작소스
  • 지역 우선 검색법 - 이벤트를 빨리 발생기 위한 방법. 이전에 지뢰체크를 거나 빈칸을 연 곳을 기준으로 검색범위를 점점 넓혀가는 방법
  • 문제점의 발생 - windows 98 이버전의 지뢰찾기 비트맵부분
  • 4차제작소스
  • windows 98 이 버전 지뢰찾기 지원. (완벽진 않음)

14. Fourth - 최적화 시도 3

  • 5차제작소스
  • 초반 무차별 찍기 기능 추가
  • Status 표시창 주기적으로 지워주기 - 진행상황이 길어지면 길어질수록 부가 많이 걸린다.
  • Open Block 에 대해서 한꺼번에 일괄처리 - 의외로 효과가 높은 방법 (궁극적으로는 Excute 수행 횟수를 줄여주므로)
  • 추후 알고리즘 최적화에 대한 궁리
    • 지역 우선 비트맵 분석 & 데이터화 - 무조건 전체 비트맵을 데이터화 는대신, 탐색려는 기점을 잡고 그 기점을 중심으로 데이터화려는 범위를 넓혀가는 방법
    • Random Open 에 대한 확률 높이기 방법 궁리
      • 해당 빈칸들에 대해서 주위의 숫자들의 합이 가장 낮은 빈칸을 여는 것.
    • Smart Random Open - 현재 열 수 있는 Cell 수를 기준으로 Random Open 횟수를 조절는 방식

15. Fifth - 최적화 시도 4

  • 6차제작소스
  • Random Open 에 대한 확률 높이기 시도 - 해당 빈칸들에 대해서 주위의 숫자들의 합이 가장 낮은 빈칸을 50%의 확률로 열기 시도. 비교적 빈칸을 여는 확률이 높아지긴 했다. (단, 의미없는 곳이 열리는 경우가 많다는점에서 개선의 여지필요)
  • Smart Random Open - 현재 열 수 있는 Cell 수를 기준으로 Random Open 횟수를 조절함. 전체 Cell 수의 1/8 비율.
  • 98호환버그수정소스

16. Thread

안녕세요, 강석천님. 여기 이렇게 글 쓰는게 맞는지 모르겠네요.
검색을 다가 우연히 MineFinder 페이지를 발견했습니다. 상당히 재미있더군요, 소스를 무척 깔끔게 이해기 쉽도록 만드셔서 무척 인상깊게 봤습니다. 다름이 아니오라 질문이 있어서 이렇게 글을 남깁니다. 지뢰찾기 알고리즘에서 각 블럭의 상태를 비트맵으로 비교를 고 있는데 지뢰찾기 윈도우의 비트맵 데이타를 어떻게 추출셨는지 궁금합니다. 제가 윈도우 초보라서 그런지 몰라도 무척 궁금네요. 그 방법을 다른 응용 어플리케이션에서 얼마든 응용할 수 있을 것 같아서요. 답변 부탁드립니다. --동우

관심있게 봐 주셔서 감사합니다. ^^ 제가 한 비트맵 데이터 추출 방법은 일반적인 윈도우 캡처 프로그램의 원리와 비슷합니다. FindWindow 를 이용, 지뢰찾기 프로그램의 윈도우 핸들을 얻은뒤, 이를 가지고 해당 윈도우의 비트맵을 얻어내는 것이지요. 기타 제작 과정과 아이디어는 MineFinder 페이지에 서술해놓았습니다. --1002

답변 감사드립니다. 제가 질문드리고자 했던 포인트는 GetClientRect API를 통해 윈도우의 클라이언트 영역을 가져와서 실제 비교는 IDB_BITMAP_MINES 비트탭 리소스를 말씀드린 것이였습니다. IDB_BITMAP_MINES 비트맵 리소스도 GetClientRect 를 통해 추출신건가요? 만약 그 API로 추출셨다고 해도 클라이언트 영역 전체가 캡쳐가 되었을 텐데 숫자와 버튼등을 픽셀 단위로 어떻게 추출해서 IDB_BITMAP_MINES 리소스로 만드셨는지 궁금합니다. MineFinder 페이지에는 IDB_BITMAP_MINES 리소스를 만드는 이야기는 없어서요. --동우

리소스 화일은 그냥 화면캡쳐한 뒤 포토샵에서 잘랐습니다. ;) (좀 노가다 틱지만 가장 간단한 해결책이 아닐까 는 생각. 2년전 일이여서 정확히 기억 안나지만 95용 지뢰찾기와 2000용 지뢰찾기 2번 작업했었을겁니다. 약간 그림이 다르고 이미지좌표도 조금은 달라서. ^^)--1002

강석천님. MineFinder를 win32 API로 컨버전 해서 제 홈페이지에 코드 분석 강좌 페이지를 만들까 합니다. 물론 원저자와 원작 페이지의 링크를 분명히 표시할 것입니다. 그렇게 win32 API 버전으로 수정해서 제 홈페이지에 게시를 할 수 있을지요. 석천님의 허가를 부탁드립니다. --동우

출처(링크)만 표기시면 자유롭게 이용셔도 됩니다. 홈페이지 링크주세요. ^^ (혹시 전에 지뢰찾기 어셈레벨에서 분석MineFinder 만드신 분이신가요? 저는 인간까지만 모델링 했지만, 신을 모델링한. ^^;) --1002

저의 홈페이지는 http://ssrnet.snu.ac.kr/~leedw 입니다. MineFInder는 잘 모르겠구요, 지뢰찾기를 디스어셈블링해서 프로세스 메모리 맵 안의 0x1005700 번지가 지뢰찾기의 전체 상태 맵 배열이란건 알고 있습니다. 이곳 맵 상태 배열에서 7번째 비트가 1로 셋팅되어 있으면 그것에 해당되는 x, y좌표가 지뢰가 있는 곳이지요. 그래서 지뢰찾기 맵핵을 만들었더랬습니다. 저는 '해부학자'를 모델링 했다고면 되겠네요. 어쨌든 허락해 주셔서 감사합니다. :) --동우

조동영이가 중급으로 19초도 해냈습니다;; 어디서 05학번중에 김소현이라는 친구가 손으로 초급 10초 안팍이 나온다고 들었습니다. - 톱아보다
초급은 나도 9초까진 해봤음. 그래도 내가 본 최고는 97의 희진누님.; (고급 50-60초대) --1002

----
프로젝트분류
Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2021-02-07 05:23:45
Processing time 0.0800 sec