[따라하기] 인공지능 오목게임 [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 25145
공지 [UX Summit 요약] 오른쪽 클릭은 옳다 (Right Click is Right) 관리자 2020.11.16 23453
공지 [10.4 시드니] What's NEW! 신기능 자세히 보기 관리자 2020.05.27 25437
공지 RAD스튜디오(델파이,C++빌더) - 고객 사례 목록 관리자 2018.10.23 31350
공지 [데브기어 컨설팅] 모바일 앱 & 업그레이드 마이그레이션 [1] 관리자 2017.02.06 32342
공지 [전체 목록] 이 달의 기술자료 & 기술레터 관리자 2017.02.06 27783
공지 RAD스튜디오(델파이, C++빌더) - 시작하기 [1] 관리자 2015.06.30 48829
공지 RAD스튜디오(델파이,C++빌더) - 모바일 앱 개발 사례 (2020년 11월 업데이트 됨) 험프리 2014.01.16 185798
803 이전 버전 C ++ 빌더 애플리케이션을 C ++ 빌더 10 시애틀로 마이그레이션 [1] file 김원경 2017.11.24 1733
802 이 달의 기술자료 - 2017년 12월 file 험프리 2017.11.24 697
801 수원과학대 학생이 델파이로 개발한 '축구 리그 앱' 관리자 2017.11.24 1021
800 [발표자료] 20171117 델파이/C++빌더 제조/제어 개발 세미나 관리자 2017.11.20 408
799 [발표자료] 20171116 델파이/C++빌더 의료 개발 세미나 관리자 2017.11.17 364
798 [발표자료] 20171109 RAD스튜디오 100% 활용하기: 최신 기술 적용과 확장 관리자 2017.11.10 469
797 RAD 스튜디오 - 사물인터넷 관련기술과 고객사례 험프리 2017.10.31 1752
796 [고객사례-설치도구, 델파이] RAD&Installer 관리자 2017.10.31 605
795 이 달의 기술자료 - 2017년 11월 file 험프리 2017.10.30 575
794 안면인식(Face Detection) 라이브러리(안드로이드, iOS) 험프리 2017.10.30 2545
793 TTS(Text-to-Speech) 라이브러리(윈도우, 맥OS, iOS, 안드로이드) [2] 험프리 2017.10.30 1485
792 델파이로 빅데이터 데이터베이스 연동하기(Cassandra, Couchbase, MongoDB) 험프리 2017.10.30 1204
791 [고객사례-생산성도구, 델파이] Change-Pro for Excel - 엑셀 변경항목 분석 프로그램 관리자 2017.10.27 731
790 RAD스튜디오 로드맵 - 2017년 9월 관리자 2017.10.23 629
» [따라하기] 인공지능 오목게임(7) - 오목게임 승자(5돌) 결정 지능프로그램 file 대화마을 2017.10.17 1702
788 [따라하기] 인공지능 오목게임(6) - 흑백 바둑알 놓고 위치저장 file 대화마을 2017.10.01 1138
787 엔터프라이즈 커넥터로 '구글 드라이브 VCL 애플리케이션' 만들기 file 관리자 2017.09.29 1466
786 이 달의 기술자료 - 2017년 10월 file 험프리 2017.09.26 637
785 [따라하기] 인공지능 오목게임(5) - 바둑알 놓기 [2] file 대화마을 2017.09.22 1015
784 [FMX] TListView 더보기 버튼 구현하기 험프리 2017.09.20 1081