[따라하기] 인공지능 오목게임 [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 17933
공지 RAD스튜디오(델파이, C++빌더) - 시작하기 [1] 관리자 2015.06.30 38228
공지 RAD스튜디오(델파이,C++빌더) - 모바일 앱 개발 사례 (2020년 11월 업데이트 됨) 험프리 2014.01.16 173741
1342 [DelphiCon 요약] 델파이로 웹 다루기 (Powering the Web with Delphi) file 관리자 2021.02.16 1095
1341 [고객 사례- POS, C++빌더] YG-POS - 소상공인을 위한 무료 소프트웨어 관리자 2021.02.08 632
1340 [고객 사례- 금융, 델파이] 주식, 화폐 등 금융 정보 분석 소프트웨어 - 게임스탑, 비트코인, 아마존, 구글 등 관리자 2021.02.04 613
1339 ‘장기 활용 가능성’이 가장 뛰어난 프레임워크는? (델파이 VS. WPF VS. ELECTRON) 관리자 2021.02.01 457
1338 [델파이 웹개발] 델파이로 풀스택 웹 개발하기 - uniGUI 활용 (Full Stack Web Development with uniGUI for Delphi) file 관리자 2021.02.01 4277
1337 버전별 업데이트된 주요 기능들 (C++빌더6 / 델파이7부터 최신 버전까지!) 관리자 2021.01.29 374
1336 이 달의 기술자료 - 2021년 02월 file 험프리 2021.01.26 506
1335 [샘플 프로젝트] 심박 측정기-델파이/C++빌더와 IOT 연동하기 관리자 2021.01.25 450
1334 C++ 유니코드 문자열 리터럴(Unicode String Literal) 활용 방법 관리자 2021.01.22 439
1333 C++빌더 마이그레이션, C++ 프로젝트를 간편하게 업데이트 하는 방법 관리자 2021.01.19 307
1332 [고객 사례- 델파이, 게임] 체스 오프닝 위저드 - 60,000여명의 체스 플레이어가 실제로 사용하는 앱 관리자 2021.01.18 702
1331 생산성을 끌어올려줄 겟잇(GetIt)의 최신 컴포넌트들 관리자 2021.01.18 498
1330 [DelphiCon 요약] High DPI 고해상도를 VCL에서 활용하기 (Leveraging High DPI in VCL Applications) 관리자 2021.01.13 763
1329 [DelphiCon 요약] Spring4D 소개 - 델파이 개발을 한수준 높이기 (Introduction to Spring4D - Taking Delphi Development to the Next Level) 관리자 2021.01.08 1135
1328 새해 목표: LEARN, TEACH, REPEAT. 관리자 2021.01.05 313
1327 [DelphiCon 요약] 델파이로 함수형 프로그래밍하기 (Functional Programming With Delphi) 관리자 2020.12.29 707
1326 [DelphiCon 요약] 델파이 고성능 구현 (High Performance Delphi) 관리자 2020.12.27 890
1325 [TOP 10] 2020년 하반기, 개발자가 사랑한 기술자료는? 관리자 2020.12.24 350
1324 이 달의 기술자료 - 2021년 01월 file 험프리 2020.12.24 402