10 장세균전프로그래밍 10.1 게임룰 (1) 사람과컴퓨터가싸우는 2인용보드게임이다. (2) 사람이먼저움직이고, 컴퓨터가움직인다. (3) 세균을가로및세로방향으로 2칸까지빈칸으로이동시킬수있다. (4) 1칸을이동할경우에는복제가된다. (5) 이동한후주변세균은내편으로바뀐다. (6) 둘다이동할수없으면, 경기가종료된다. (7) 가장많은세균을가진사람이이긴다. 10.2 기초지식 - 이벤트발생횟수를카운트한다. (0= 사람이이동준비, 1= 이동, 2= 컴퓨터가이동준비, 3= 이동 ) - 세균은그림판으로그린다. (50x50픽셀) - 컴퓨터는타이머함수를사용한다. - 컴퓨터는 brute-force방식으로이동위치를정한다. 10.3 새프로젝트를생성한다. - 프로젝트명 =Vacteria, 응용프로그램 =SDI, - 유니코드라이브러리사용 ( 체크해제 ) 또는프로젝트속성 -> 일반 -> 문자집합 = 멀티바이트 10.4 세균비트맵을그림판으로만들어리소스에등록한다. - Resource -> Import Vacteria.bmp -> IDB_BITMAP1 ( 빈칸, 세균 1, 세균 2, 세균 1, 세균 2 순서 ) 10.5 말판변수를선언하고, 초기값을넣어준다. public: int m_nvac[6][6]; enum VAC EMPTY, VAC_1, VAC_2, VAC_3, VAC_4 ; CVacteriaView::CVacteriaView() memset(m_nvac, EMPTY, sizeof(m_nvac)); m_nvac[0][0] = m_nvac[0][1] = m_nvac[1][0] = m_nvac[1][1] = VAC_1; m_nvac[4][4] = m_nvac[4][5] = m_nvac[5][4] = m_nvac[5][5] = VAC_2; 10.5 말판을그린다.
void CVacteriaView::OnDraw(CDC* pdc) CBitmap bitmap; bitmap.loadbitmap(idb_bitmap1); CDC memdc; memdc.createcompatibledc(pdc); memdc.selectobject(&bitmap); for (int i=0; i<6; i++) for (int j=0; j<6; j++) int x=50+50*j, y=50+50*i; pdc->bitblt(x, y, 50, 50, &memdc, 50*m_nVac[i][j], 0, SRCCOPY); for (int i=0; i<=6; i++) pdc->moveto(50, 50+50*i); pdc->lineto(50+50*6, 50+50*i); for (int j=0; j<=6; j++) pdc->moveto(50+50*j, 50); pdc->lineto(50+50*j, 50+50*6); memdc.deletedc(); bitmap.deleteobject(); 10.6 마우스의왼쪽버튼으로옮길세균과목표빈칸을지정한다. public: int m_ncount; CPoint m_ptmove1, m_ptmove2; enum TURN HUMAN_PREPARE, HUMAN_MOVE, COMPUTER_PREPARE, COMPUTER_MOVE ; CVacteriaView::CVacteriaView() m_ncount = 0; void CVacteriaView::OnLButtonDown(UINT nflags, CPoint point) int i = point.y / 50-1; int j = point.x / 50-1; if (i<0 i>=6 j<0 j>=6) return; // 말판바깥을찍으면 skip switch (m_ncount%4) case HUMAN_PREPARE: // 옮길세균을선택한다 if (m_nvac[i][j] == EMPTY) // 빈칸을찍으면안된다 m_ptmove1 = CPoint(j, I); m_nvac[i][j] += 2; // 모양을바꾼다 // 화면을갱신한다 case HUMAN_MOVE: // 옮길위치를선택한다 if (m_nvac[i][j]!= EMPTY) // 빈칸으로만옮겨갈수있다 if ((abs(i - m_ptmove1.y)>2) (abs(j - m_ptmove1.x)>2)) // 2칸이상은불허한다 m_nvac[m_ptmove1.y][m_ptmove1.x] -= 2; // 원래모양으로되돌린다 m_ptmove2 = CPoint(j, I); MoveVacteria(); // 세균을이동한후 // 화면을갱신한다 SetTimer(0, 500, NULL); // 컴퓨터는타이머함수를사용한다
10.7 세균을이동하는함수를만든다. public: void MoveVacteria(void); void CVacteriaView::MoveVacteria() int i1 = m_ptmove1.y; int j1 = m_ptmove1.x; int i2 = m_ptmove2.y; int j2 = m_ptmove2.x; int nvac = m_nvac[i2][j2] = m_nvac[i1][j1]; if (abs(i1-i2)>1 abs(j1-j2)>1) m_nvac[i1][j1] = EMPTY; for (int i=i2-1; i<=i2+1; i++) for (int j=j2-1; j<=j2+1; j++) if (i<0 i>=6 j<0 j>=6) continue; if (m_nvac[i][j]!= EMPTY) m_nvac[i][j] = nvac; 10.8 컴퓨터는최적위치를찾는다.
public: int FindBestMoving(int nvac); int CVacteriaView::FindBestMoving(int nvac) // brute-force search method 를사용하여최선의위치를찾는다. int nmaxcount = 0; for (int i1=0; i1<6; i1++) for (int j1=0; j1<6; j1++) if (m_nvac[i1][j1]!= nvac) continue; for (int di=-2; di<=2; di++) for (int dj=-2; dj<=2; dj++) int i2=i1+di, j2=j1+dj; if (i2<0 i2>=6 j2<0 j2>=6) continue; if (m_nvac[i2][j2]!= EMPTY) continue; int ncount = 0; for (int zi=-1; zi<=1; zi++) for (int zj=-1; zj<=1; zj++) int i3=i2+zi, j3=j2+zj; if (i3<0 i3>=6 j3<0 j3>=6) continue; if (m_nvac[i3][j3] == EMPTY) continue; if (m_nvac[i3][j3]!= nvac) ncount += 2; if ((abs(di)<2) && (abs(dj)<2)) ncount++; if (ncount > nmaxcount) nmaxcount = ncount; m_ptmove1 = CPoint(j1, i1); m_ptmove2 = CPoint(j2, i2); return nmaxcount; 10.9 해당세균을찾았으면그세균을움직이고, 사람차례로바꾼다.
void CVacteriaView::OnTimer(UINT_PTR nidevent) switch (m_ncount%4) case COMPUTER_PREPARE: if (FindBestMoving(VAC_2) == 0) m_ncount += 2; m_nvac[m_ptmove1.y][m_ptmove1.x] += 2; SetTimer(0, 1000, NULL); case COMPUTER_MOVE: m_nvac[m_ptmove1.y][m_ptmove1.x] -= 2; MoveVacteria(); KillTimer(0); 10.10 각세균수를점수판에표시한다. void CVacteriaView::OnDraw(CDC* pdc) int nsum[5]; memset(nsum, 0, sizeof(nsum)); for (int i=0; i<6; i++) for (int j=0; j<6; j++) nsum[m_nvac[i][j]]++; nsum[vac_1] += nsum[vac_3]; nsum[vac_2] += nsum[vac_4]; // 옮길세균도포함한다 CString str; str.format(_t("%d : %d"), nsum[vac_1], nsum[vac_2]); pdc->drawtext(str, CRect(50, 0, 50+50*6, 50), DT_CENTER DT_VCENTER DT_SINGLELINE); 10.11 움직일세균이없으면종료한다.
public: bool IsMovable(int nvac); void CVacteriaView::OnDraw(CDC* pdc) if (nsum[empty]==0 nsum[vac_1]==0 nsum[vac_2]==0 (!IsMovable(VAC_1) &&!IsMovable(VAC_2))) if (nsum[vac_1] > nsum[vac_2]) AfxMessageBox(_T("You win!")); else if (nsum[vac_1] < nsum[vac_2]) AfxMessageBox(_T("I win!")); else AfxMessageBox(_T("Try Again?")); bool CVacteriaView::IsMovable(int nvac) for (int i1=0; i1<6; i1++) for (int j1=0; j1<6; j1++) if (m_nvac[i1][j1]!= nvac) continue; for (int di=-2; di<=2; di++) for (int dj=-2; dj<=2; dj++) int i2=i1+di, j2=j1+dj; if (i2<0 i2>=6 j2<0 j2>=6) continue; if (m_nvac[i2][j2] == EMPTY) return TRUE; return FALSE; 10.12 효과음을넣는다. (1) 멀티미디어라이브러리를등록한다. 프로젝트속성 - > 구성요소 -> 링크 -> 종속성 =winmm.lib (2) 소리파일을 res 폴더에복사한다. C:\Windows\Media\ding.wav (tada.wav, chimes.wav, ringout.wav,...) #include <MMsystem.h> void CVacteriaView::OnLButtonDown(UINT nflags, CPoint point) sndplaysound( res/ding.wav ), SND_SYNC); // 소리가종료될때까지프로그램은중지 10.13 저작권정보를기록한다. (1) 도움말대화상자의문자열을수정한다. Resource.rc -> Dialog -> IDD_ABOUTBOX -> Vacteria, 버전 1.0, 안재형교수제작 (2) 리소스창에서버전정보를수정한다. (Resource.rc -> Version) CompanyName TODO: < 안재형교수 > LegalCopyright TODO: < 충북대학교 >. 모든권리보유. ProductName TODO: < 세균전 >