[따라하기] 인공지능 오목게임 [7] 오목게임 승자(5) 결정 지능프로그램

 

 

안녕하세요 델파이 프로그래머님들!

이번 인공지능 오목게임 7번째 세션에서는 다음의 과정을 해보도록 하겠습니다.

 

 

1. 마우스클릭에서 호출되는 IsThereFiveToStopGame (5개연속있는가!) 함수(function)의 의미는?

2. 좌표상 서->동 수평축(West->East) 5돌 연속 있는가를 확인하기 위한 기본 이해

3. 좌표상 서->동 수평축(West->East) 과 동->서 수평축 등 연속 5돌을 확인하기 위한

    모든 경우의 수를 생각해보고 나열식(비효율적)으로 1차적 프로그램 하기

4. 연속 5돌을 확인하기 위한 좌표상 8개 방향을 이용하는 효율적 방식 프로그램 하기

5. 게임 종료후 GameOver를 사용하기

6. 다음 세션들 중에 하게될 컴퓨터와 사람과의 프로그램 논리흐름도 이해하기

     

 

1. 마우스클릭에서 호출되는 IsThereFiveToStopGame (5개연속있는가?) 함수(function)의 의미는?

지난 세션에서 마우스클릭에서 IsThereFiveToStopGame 함수(function) 호출한 것 기억하시죠?

이 프로시져를 호출시 3개의 값 (흑백여부, Column, Row) 을 보냅니다.

 

 

procedure TMainForm.TTTBoardMouseUp( ,   ,   , );

begin

    ,  ,  ,

   if IsThereFiveToStopGame(StonePiece[Column, Row].WhiteOrBlack, Column, Row) then

        MessageDlg('백색의 5개 연속입니다!', mtInformation, [mbOk], 0);

end;

  

function TMainForm.IsThereFiveToStopGame(ForWho : char; X, Y: integer): boolean;

begin

   // 5개 연속있는가를 결정함

end;

 

이렇게 함수(function)에서 ForWho, X, Y 변수이름으로 받아들입니다.

여기서 ForWho의 값은 W, B, ‘ ’ 3개중 하나입니다. 그리고 매번 놓여진 돌의 X,Y좌표 값을 승자를

확인하기 위해 전합니다

    

 

2. 좌표상 동-서 수평축(West-East) 5돌 연속있는가를 확인하기 위한 기본 이해

   

    4casesfor5.png

  

 위 그림처럼 각 돌이 화살표 방향으로 순서대로 놓여진다고 가정하면 좌표 점의 변화는 다음과 같습니다

1: (1,1), (2,1), (3,1), (4,1), (5,1)                    : X 축만 1씩 증가

2: (11,1), (11,2), (11,3), (11,4), (11,5)            : Y 축만 1씩 증가

3: (1,10), (2,11), (3,12), (4,13), (5,14)            : X, Y 1씩 증가

4: (19,10), (18,11), (17,12), (16,13), (15,14)    : X1씩 감소, Y1씩 증가

 

 

물론 화살표 반대방향으로 놓여지게 될 때 좌표 점의 변화도 알수 있겠죠.

  

 

 

3. 좌표상 동-서 수평축(West-East) 5돌 연속 확인하기 위한 프로그램 하기

        연속 5돌을 확인하기 위해 프로그램을 하는데는 2가지 방법이 있습니다.

   1) 코딩을 길게 나열해서 재사용을 못하는 비효율적이 방법이 있고

   2) 코딩의 재사용이 가능한 효율적이 방법이 있습니다.

물론 프로그래머가 2번째 방법으로 처음부터 시작하면 좋겠지만 그게 잘 안될때도 있기에

1번째 방법으로 문제를 일단 해결하고 2번째 방법으로 개선하는 것도 좋을 것 같습니다.

 

그래서 이번 따라하기에선 먼저 1번째 방법으로 해보고 2번째 방법으로 변환하는 것을 함께 해보도록 하겠습니다.  

 

 

 

function TMainForm.IsThereFiveToStopGame(ForWho : char; X, Y: integer): boolean;

var count : integer;

begin

Count := 0;

//코딩을 길게 나열하는 비효율적으로 하자면 이런방식입니다.

 

if StonePiece[X , Y ].WhiteOrBlack = ForWho then

Inc(Count);

if StonePiece[X + 1 , Y ].WhiteOrBlack = ForWho then

Inc(Count);

if StonePiece[X + 2 , Y ].WhiteOrBlack = ForWho then

Inc(Count);

if StonePiece[X + 3 , Y ].WhiteOrBlack = ForWho then

Inc(Count);

if StonePiece[X + 4 , Y ].WhiteOrBlack = ForWho then

Inc(Count);

 

// 나머지 모든 경우의 수를 반영해야합니다.

 

 

if (Count = 5) then

Result := true

else Result := false;

end;

 

 

여기  StonePiece[X , Y ]

       StonePiece[X + 1 , Y ]

       ,   ,   ,

       StonePiece[X + 4 , Y ] 처럼

X좌표값을 1씩 증가하며 같은색의 돌의 Count 5개가 되는지 확인하고 함수의 Result 겂을 전달합니다.

그런데 프로그램을 실행해서 위 그림처럼 (1,1), (2,1), (3,1), (4,1), (5,1) 순서로 놓으면 무슨 일이 일어날까요?

게임의 승자를 선언할까요?

아닙니다!

 

 

그럼 이제 위 그림의 1화살표의 반대방향으로 순서대로 (5,1), (4,1), (3,1), (2,1), (1,1) 에 놓아보죠!

 

이럴 경우 게임 승자를 정상적으로 선언하죠!

왜냐면 맨 마지막 놓여진 X, Y 좌표 값에서부터 검사를 하기 때문입니다.

그래서 우리는 마지막 놓여진 돌의 좌표점을 기준으로 양방향으로 5개의 연속돌이 있나를 확인해야 합니다.

그러므로 다음처럼 TMainForm.IsThereFiveToStopGame을 추가합니다.

 

 

function TMainForm.IsThereFiveToStopGame(ForWho : char; X, Y: integer): boolean;

begin

   ,   ,  ,

//2차로 반대 방향으로 검사하기

if (Count <> 5) then

begin

Count := 0;

if StonePiece[X , Y ].WhiteOrBlack = ForWho then

Inc(Count);

if StonePiece[X - 1 , Y ].WhiteOrBlack = ForWho then

Inc(Count);

if StonePiece[X - 2 , Y ].WhiteOrBlack = ForWho then

Inc(Count);

if StonePiece[X - 3 , Y ].WhiteOrBlack = ForWho then

Inc(Count);

if StonePiece[X - 4 , Y ].WhiteOrBlack = ForWho then

Inc(Count);

end;

   ,   ,    ,

end;

      

 

StonePiece[X , Y ],       StonePiece[X - 1 , Y ] ,    ,     ,   StonePiece[X - 4 , Y ]

X좌표값이  1씩 감소해면서 반대방향으로 검색을 할 수 있습니다.

그래서 이렇게 (4개의 경우 방향 x  반대방향 ) 의 총  8개의 경우 수를 비효율적으로 프로그램 해야 합니다.

    

 

4. 연속 5돌을 확인하기 위한 좌표상 8개 방향을 확인하는 효율적 프로그램 하기

       이제 코딩의 재사용이 가능한 효율적 프로그래밍 방법을  알아 보겠습니다.

 

8waystoCheck.png

           [마지막 포석점을 기준으로 한 8 방향의 검사를 나타낸 그림]

 

 

A. 위 그림처럼 빨간색 마지막 포석 점을 기준으로 다음 논리를 생각해보고  프로그램을 해봅니다.

1번. 빨간점 기준 > 1번 방향으로 5돌 검색 (X, Y-1) 하고

         연속5돌이 없으면

                빨간점 기준 > 5번 방향으로 검색 (X, Y+1) .

2번. 빨간점 기준 > 2번 방향으로 5돌 검색 (X+1, Y-1) 하고

        연속5돌이 없으면

                빨간점 기준 > 6번 방향으로 검색 (X-1, Y+1) .

3번. 빨간점 기준 > 3번 방향으로 5돌 검색 (X+1, Y) 하고

         연속5돌이 없으면

                빨간점 기준 > 7번 방향으로 검색 (X-1, Y) .

4번. 빨간점 기준 > 4번 방향으로 5돌 검색 (X+1, Y+1) 하고

        연속5돌이 없으면

               빨간점 기준 > 8번 방향으로 검색 (X-1, Y-1) .

 

 

B. 이런 기준을 세우고 IsThereFiveToStopGame 함수를 코딩을 수정 해보겠습니다.

 

 

function TMainForm.IsThereFiveToStopGame(ForWho : char; X, Y: integer): boolean;

begin

, , ,

if CheckFourDirForFive(ForWho, 1, X, Y) then

    Result := true

else if CheckFourDirForFive(ForWho, 2, X, Y) then

    Result := true

else if CheckFourDirForFive(ForWho, 3, X, Y) then

    Result := true

else if CheckFourDirForFive(ForWho, 4, X, Y) then

    Result := true

else Result := false;

end;

 

 

여기서 CheckFourDirForFive(ForWho, 1, X, Y)  함수를 호출하는데

A 에서 언급한 것처럼 포석 점을 기준으로 1번 부터 ~ 4번까지의  방향으로 검사를 합니다.

 

 

function TMainForm.CheckFourDirForFive(ForWho: char; WhichWay, X, Y: integer): boolean;

var

XFactor, YFactor, SquareCount, Count, EmptyCount : integer;

begin

case WhichWay of

1 : begin XFactor := 0; YFactor := -1; end;

2 : begin XFactor := 1; YFactor := -1; end;

3 : begin XFactor := 1; YFactor := 0; end;

4 : begin XFactor := 1; YFactor := 1; end;

5 : begin XFactor := 0; YFactor := 1; end;

6 : begin XFactor := -1; YFactor := 1; end;

7 : begin XFactor := -1; YFactor := 0; end;

8 : begin XFactor := -1; YFactor := -1; end;

end;

 

여기서 선언된 XFactor, YFactor 4-A에서 설명된 좌표점 변화를 구현한 것입니다.

( 이 내용은 나중에는 GetXYFactor(WhichWay, XFactor, YFactor)라는 함수로 분리할 예정입니다.)

   

 

 

C. 아래의 그림에서는 3가지 경우를 생각해야 합니다.

   

caseforblock_empty.png  

 

1) 다른 색 돌로 막혀있을 경우?

그림의 1번처럼 빨간 포석점부터 카운트를 하다 검은돌로 막히면

더 이상 진행하지않고 반대방향으로 검사합니다.

이를 코딩에선 BlockedBy 변수를 통해 구현 합니다.  

 

2) 빈 자리가 있을 경우?

그림의 2번 처럼 카운트를 하다 빈 자리가 나오면 더 이상 진행하지 않고

반대방향으로 검사합니다. 이를 코딩에선 EmptyCount 로 확인합니다.

3) 좌표의 이동을 반영하려면?

SquareCount 는 실제 검사한 좌표 점 의 값을 저장하는 변수이며 빈 좌표점일 경우도

값이 올라갑니다. 이는 (XFactor * SquareCount) (YFactor * SquareCount)

형태로 사용되어 좌표 값의 이동을 반영합니다.

 

 

그럼 다음의 코딩 결과를 얻을 수 있습니다.

 

 

 

 

function TMainForm.CheckFourDirForFive(ForWho: char; WhichWay, X, Y: integer): boolean;

begin

    Count := 0;

    SquareCount := 0;

    EmptyCount := 0;

   ,   ,     ,

 

 While ((Not BlockedBy) and (SquareCount < 5) and (EmptyCount = 0)) do

  begin

    if ((X + (XFactor * SquareCount) > 0) and (X + (XFactor * SquareCount) < 20)

      and (Y + (YFactor * SquareCount) > 0) and (Y + (YFactor * SquareCount) < 20) ) then

   begin

     if StonePiece[X + (XFactor * SquareCount), Y + (YFactor * SquareCount)].WhiteOrBlack = ForWho then

     begin

Inc(Count); inc(SquareCount);

     end

    else if StonePiece[X + (XFactor * SquareCount), Y + (YFactor * SquareCount)].WhiteOrBlack = ' ' then

    begin

        inc(EmptyCount); Inc(SquareCount);

    end

    else if StonePiece[X + (XFactor * SquareCount), Y + (YFactor * SquareCount)].WhiteOrBlack <> ForWho then

       BlockedBy := true;

   end;

end;

 

 

여기서 다음 코딩은 바둑판 경계를 벋어나지 않을 때만 수행하도록 제한합니다.  

 

if ((X + (XFactor * SquareCount) > 0) and (X + (XFactor * SquareCount) < 20)

and (Y + (YFactor * SquareCount) > 0) and (Y + (YFactor * SquareCount) < 20) ) then

begin

 

 

게임을 해보시면 아시겠지만 [그림- 8 방향의 검사를 나타낸 그림]에서 언급한거처럼

1번 방향, 2번 방향, 3번 방향, 4번 방향은 정상 작동하지만

5, 6, 7, 8 번 방향은 아직 미완이죠!

그래서 이제 1, 2, 3, 4 번의 반대 방향으로 검사하도록 해야합니다.

 

 

D. GetXYFactor 프로시져를 선언해서  함수의 재활용이 가능히게 하고 XFactor, YFactor 값 받아오려고 합니다.

아래의 선언된 GetXYFactor 프로시져 파라미터를 보면 var 선언을 하면 XFactor, YFactor 값을

전달할 수 있습니다.

 

 

procedure TMainForm.GetXYFactor(WhichWay: integer; var XFactor, YFactor: integer);

begin

case WhichWay of

1 : begin XFactor := 0; YFactor := -1; end;

2 : begin XFactor := 1; YFactor := -1; end;

3 : begin XFactor := 1; YFactor := 0; end;

4 : begin XFactor := 1; YFactor := 1; end;

5 : begin XFactor := 0; YFactor := 1; end;

6 : begin XFactor := -1; YFactor := 1; end;

7 : begin XFactor := -1; YFactor := 0; end;

8 : begin XFactor := -1; YFactor := -1; end;

end;

end;

 

 

그리고  위에 선언된 GetXYFactor 프로시져를 아래의 CheckFourDirForFive 안에서  호출하여

좌표방향에 따른  XFactor, YFactor 값을 전달 받습니다.

그리고 1, 2, 3, 4 방향으로 연속5개가 있나 검사한 후 연속5개가 없는 경우에는  

반대방향인 5, 6, 7, 8 방향으로 다시 연속5개가 있나 검사합니다.

 

 

function TMainForm.CheckFourDirForFive( , , , ): boolean;

begin

, , ,

//대칭 방향으로 검사하기

  if (count < 5) then

  begin

GetXYFactor(WhichWay + 4, XFactor, YFactor);     // WhichWay + 4는 대칭 방향으로 검사하기위함

 

 

count := count - 1; // 마지막 포석점은 이미 위에서 카운트 했기에 -1

BlockedBy := false;

SquareCount := 0; EmptyCount := 0;

   ,   ,    ,

 

 end;

  

 

if (Count = 5) then

begin

    GameOver := true;

    Result := true

end

else Result := false;

end;

 

 

5. 게임 종료후 GameOver를 사용하기

      그리고 마지막으로 게임이 끝났으면 GameOver 변수를 True롤 선언하고 계속 진행할수 없게

     다음처럼 TTTBoardMouseUp에서 GameOver 상태를 검사하도록 변경합니다.

 

 

procedure TMainForm.TTTBoardMouseUp( , ,, )

begin

, , ,

if Not GameOver then

begin

, , ,

end;

 

 

end;

 

 

 

6. 논리흐름도 이해하기

  

 

ps 다음 논리 흐름도는  컴퓨터와 사람의 게임을 위한  논리를 구현했던 것입니다.

    고민해 보시죠!

     다음은 논리 흐름도상의 내용입니다.

1: 5개의 있는지 검사하기

2: 컴퓨터가 5개를 만들 경우가 있는지 검사

3: 사람이 4개가 있는지 검사

4: 컴퓨터가 4개를 만들 경우가 있는지 검사

5: 사람이 3개가 있는지 검사

6: 사람이 함정 수가 있는지 검사

7: 컴퓨터가 함정 수를 만들 경우가 있는지 검사

8: 컴퓨터가 3개를 만들 경우가 있는지 검사

 

 

  LogicFlow.jpg

 

   

 

내용이 길었습니다. 설명하는 능력이 모자라서 이해가 되셨는지 모르겠습니다.

첨부 파일을 실행해서 연구하다 보면 도움이 많이 되리라 생각됩니다.

 

그럼 좋은 하루 보내세요.

  

 

 

 

 

번호 제목 글쓴이 날짜 조회 수
공지 [DelphiCon 요약] 코드사이트 로깅 실전 활용 기법 (Real-world CodeSite Logging Techniques) 관리자 2021.01.19 14387
공지 [UX Summit 요약] 오른쪽 클릭은 옳다 (Right Click is Right) 관리자 2020.11.16 13023
공지 [10.4 시드니] What's NEW! 신기능 자세히 보기 관리자 2020.05.27 15532
공지 RAD스튜디오(델파이,C++빌더) - 고객 사례 목록 관리자 2018.10.23 21064
공지 [데브기어 컨설팅] 모바일 앱 & 업그레이드 마이그레이션 [1] 관리자 2017.02.06 22297
공지 [전체 목록] 이 달의 기술자료 & 기술레터 관리자 2017.02.06 17932
공지 RAD스튜디오(델파이, C++빌더) - 시작하기 [1] 관리자 2015.06.30 38228
공지 RAD스튜디오(델파이,C++빌더) - 모바일 앱 개발 사례 (2020년 11월 업데이트 됨) 험프리 2014.01.16 173741
269 ComPort(시리얼 통신) 컴포넌트 설치안내 [11] file 험프리 2013.12.04 106645
268 델파이 XE2에서 dbExpress를 이용해 오라클 연결하기 file 박병일 2012.02.15 17716
267 다중 클라이언트를 위한 DataSnap 서버 만들기 관리자 2011.12.22 16102
266 델파이XE2의 VCL Styles 활용하기 박병일 2012.01.19 15351
265 dbExpress 의 SQLConnection 에서 트랜젝션 처리하기 박병일 2012.02.08 13972
264 [델파이 문법 시리즈] #1. 제네릭 - 개요 file 관리자 2012.03.21 13163
263 델파이 XE2 DataSnap Client 만들기 [1] 관리자 2011.12.22 12884
262 [델파이 업그레이드 마이그레이션 무상 컨설팅] 체크리스트와 가이드(첨부 파일) [1] file 관리자 2012.02.08 12868
261 "모바일 앱" 개발 또는 유지관리, 데브기어와 해결하세요! file 관리자 2015.03.26 12726
260 [델파이 문법] 클래스와 객체 #8 file 관리자 2012.05.07 12567
259 델파이로 만든 구글 지오코딩 쎔플 입니다. 박병일 2012.02.06 11015
258 [델파이 문법] 클래스와 객체 #1 file 관리자 2012.04.12 10863
257 [오픈소소] 델파이용 TProcessInfo 클래스 file 관리자 2012.05.23 10437
256 VCL Style Util 박병일 2012.01.11 10286
255 [델파이 문법 시리즈] #2. 제네릭 - 용어 file 관리자 2012.03.22 10005
254 [델파이 문법] 클래스와 객체 #10 file 관리자 2012.05.22 9820
253 [델파이 문법] 클래스와 객체 #3 file 관리자 2012.04.18 9310
252 델파이 XE2 Update4의 새기능 - 더 쉬운 모바일 REST 접속 file 박병일 2012.03.06 9143
251 [델파이 문법] 프로그램과 유닛 #1 file 관리자 2012.09.03 9055