틱택토 게임 만들기. 개발의 이론적 기초

안녕하세요, 친애하는 친구 여러분! 이번 강의에서는 자바스크립트로 브라우저 게임인 tic-tac-toe를 만드는 방법을 보여드리겠습니다! 여러분 모두 이 게임이 무엇인지, 어떻게 플레이하는지 알고 계시겠지만, 다시 한 번 상기시켜 드리겠습니다.

Tic-tac-toe는 3 x 3 셀(아마도 더 클 수 있음)의 정사각형 필드에서 두 플레이어 간의 논리적 게임입니다. 하나는 "십자가"로 연주하고 다른 하나는 "발가락"으로 연주합니다.

추신 모든 유사한 자바스크립트 레슨과 마찬가지로 기사 마지막 부분에서 소스 파일을 다운로드할 수 있으며 데모 예제에서 작업 결과도 볼 수 있습니다!

제작 중인 게임에 대한 설명

게임의 기능을 살펴 보겠습니다.

  • 페이지가 로드된 후 게임이 즉시 시작됩니다.
  • 먼저 갈 권리는 무작위로 선택됩니다(걷기 시작하거나 컴퓨터 중 하나).
  • 귀하가 베팅할 부호는 무작위로 선택됩니다(십자가 또는 0).
  • 플레이어가 이기면 승리 기호(십자 표시 또는 0 표시)가 녹색으로 강조 표시됩니다.
  • 플레이어가 컴퓨터에게 지면 막대가 빨간색으로 강조 표시됩니다.
  • 필드 위에는 결과(승리 또는 패배)가 표시되는 정보 줄이 있습니다.

논리

나는 3x3 정사각형 경기장에 대한 복잡한(범용) 알고리즘을 생각해내지 않았고, 반대 방향으로 나아갔습니다. 즉, 무차별 대입이었습니다! (이에 대해서는 조금 나중에 자세히 설명합니다). 나는 모든 논리의 기반이 되는 세 가지 주요 순차적 단계를 식별했습니다.

1단계: 플레이어가 이겼는지 확인하는가?

이 단계에서는 동일한 플레이어 기호(십자 또는 0)로 채워진 3개의 셀(동일한 줄에)이 있는지 확인합니다. 저것들. 어떤 수를 사용하든(첫 번째 수라도) 항상 먼저 플레이어가 승리했는지 확인합니다. 승리의 모습은 다음과 같습니다.

2단계: 확인 - 다음 동작으로 컴퓨터가 이길 수 있을까요?

이 단계에서 우리는 컴퓨터에 의해 채워지는 셀 2개와 빈 셀 1개가 있는 라인을 찾고 있습니다. 즉, 플레이어의 부주의로 인해 승리하려고 합니다. 패배는 다음과 같습니다(즉, 컴퓨터 승리).

3단계: 우리는 당신이 이기도록 두지 않습니다!

여기서 우리는 두 번째 단계와 동일한 라인을 찾고 있습니다. 단 2개의 셀만 플레이어의 게임 기호로 채워져야 합니다. 즉, 이 단계에서는 빈 셀에 기호를 배치하여 컴퓨터가지는 것을 허용하지 않습니다. 각 단계는 독립적인 기능을 나타냅니다. 아래 js 코드에서 이를 확인할 수 있습니다.

구현

경기장의 레이아웃은 매우 간단합니다. 메인 블록에는 정보 라인(클래스 - 결과)과 셀(클래스 - 블록)인 9개의 블록이 포함되어 있습니다. 셀의 HTML 레이아웃:

네 차례 야!

경기장에서 원하는 셀을 정확하게 식별하려면 보조 클래스 셀이 필요합니다. 경기장을 위한 CSS 스타일:

Krestiki_noliki( 너비: 306px; 여백: 0 자동; ) .krestiki_noliki .block( 너비: 100px; 높이: 100px; 테두리: 1px 솔리드 #ccc; 커서: 포인터; 부동: 왼쪽; 텍스트 정렬: 중앙; 글꼴 크기: 100px; 줄 높이: 94px; )

이제 전체 JS 코드를 살펴본 후 주요 사항에 대해 이야기하겠습니다.

$(document).ready(function())( var znak_user = "O"; var znak_comp = "X"; var rand_num = Math.round((Math.random() * (9 - 1) + 1)); if (rand_num > 3)( var znak_comp = "O"; var znak_user = "X"; $(".cell"+rand_num).text(znak_comp); ) var exit_flag = false; var win_user_array = ["123", " 456","789","147","258","369","159","357"]; //플레이어의 승리 함수 결정 check_3_user(znak)( for (var i = 0; i< 8; i++) { var first = "cell" + win_user_array[i].substr(0,1); var second = "cell" + win_user_array[i].substr(1,1); var third = "cell" + win_user_array[i].substr(2,1); if($("."+first).text() == znak && $("."+second).text() == znak && $("."+third).text() == znak){ $("."+first+",."+second+",."+third).css("background-color", "#83e2c3"); $(".result").text("Вы выиграли!"); $(".krestiki_noliki .block").unbind("click"); exit_flag = true; } } } //Определяем возможность победы компьютера function check_2_comp(znak){ for (var i = 0; i < 8; i++) { var first = "cell" + win_user_array[i].substr(0,1); var second = "cell" + win_user_array[i].substr(1,1); var third = "cell" + win_user_array[i].substr(2,1); if($("."+first).text() == znak && $("."+second).text() == znak && $("."+third).text() == "" && exit_flag == false){ $("."+third).text(znak); $("."+first+",."+second+",."+third).css("background-color", "#EF7C7C"); $(".result").text("Вы проиграли!"); $(".krestiki_noliki .block").unbind("click"); exit_flag = true; } if($("."+first).text() == znak && $("."+second).text() == "" && $("."+third).text() == znak && exit_flag == false){ $("."+second).text(znak); $("."+first+",."+second+",."+third).css("background-color", "#EF7C7C"); $(".result").text("Вы проиграли!"); $(".krestiki_noliki .block").unbind("click"); exit_flag = true; } if($("."+first).text() == "" && $("."+second).text() == znak && $("."+third).text() == znak && exit_flag == false){ $("."+first).text(znak); $("."+first+",."+second+",."+third).css("background-color", "#EF7C7C"); $(".result").text("Вы проиграли!"); $(".krestiki_noliki .block").unbind("click"); exit_flag = true; } } } //Определяем ход компьютера function check_2_user(znak){ for (var i = 0; i < 8; i++) { var first = "cell" + win_user_array[i].substr(0,1); var second = "cell" + win_user_array[i].substr(1,1); var third = "cell" + win_user_array[i].substr(2,1); if(exit_flag == false){ if($("."+first).text() == znak && $("."+second).text() == znak && $("."+third).text() == ""){ $("."+third).text(znak_comp); exit_flag = true; } } if(exit_flag == false){ if($("."+first).text() == znak && $("."+second).text() == "" && $("."+third).text() == znak){ $("."+second).text(znak_comp); exit_flag = true; } } if($("."+first).text() == "" && $("."+second).text() == znak && $("."+third).text() == znak){ $("."+first).text(znak_comp); exit_flag = true; } if(exit_flag) break; } } $(".krestiki_noliki .block").click(function(){ //Если клетка пустая if($(this).text() == ""){ $(this).text(znak_user); check_3_user(znak_user); check_2_comp(znak_comp); check_2_user(znak_user); if(exit_flag == false){ for (var i = 1; i < 10; i++) { if($(".cell"+i).text() == ""){ $(".cell"+i).text(znak_comp); break; } } }else exit_flag = false; } }); });

먼저 변수를 선언합니다. znak_user - 이 변수에는 사용자가 플레이할 기호를 저장합니다(기본적으로 0이 여기에 저장됩니다. 이는 영어 상자 "O"입니다). znak_comp - 이 변수에는 컴퓨터가 재생할 기호를 저장합니다(기본적으로 십자가가 여기에 저장됩니다. 이는 영어 상자 "X"입니다).

논리는 다음과 같습니다. 난수가 3보다 크면 컴퓨터는 0으로 플레이하고 컴퓨터(컴퓨터)가 먼저 움직입니다.

이 논리를 원하는 대로 변경할 수 있습니다. 예를 들어, 누가 첫 번째가 될 것인지, 어떤 신호가 올 것인지에 대한 더 많은 옵션을 만들기 위해 여러 개의 난수를 생성할 수 있습니다. exit_flag - 이 플래그(변수)는 함수 종료를 담당합니다. 즉, 예를 들어 컴퓨터가 이미 이동을 했고 함수를 종료하고 이동을 플레이어에게 전달해야 하는 경우입니다. win_user_array - 이 배열은 셀 채우기에 대한 모든 승리 옵션을 저장합니다. 명확히 하기 위해 다음 그림을 살펴보겠습니다.

배열의 각 요소는 세 개의 숫자로 구성된 문자열로, 이는 승리 조합입니다. 예를 들어 숫자 1, 2, 3 아래의 셀을 채우면 승리(또는 패배)가 발생합니다. 보시다시피 총 8가지 승리 옵션이 있으며, 우리의 임무는 이러한 옵션을 모두 살펴보는 것입니다. 다음은 3가지 기능입니다:

  1. check_3_user();
  2. check_2_comp();
  3. check_2_user();

이러한 기능의 목적은 위의 논리 섹션에 세 단계로 설명되어 있습니다. 이러한 함수는 필드의 셀을 클릭하면 호출됩니다. 매개변수(znak)가 각 함수에 전달됩니다. 이는 플레이어 또는 컴퓨터의 기호(십자 또는 0)입니다. 예를 들어 플레이어의 승리를 결정하는 함수(check_3_user)에 플레이어의 기호를 순서대로 전달합니다. 같은 줄에서 같은 표지판 3개를 찾으세요.

세 가지 기능을 수행한 후(컴퓨터가 아직 이동하지 않은 경우) 컴퓨터는 빈 셀 중 하나를 채웁니다. 예를 들어, 중앙 셀이 비어 있으면(셀 번호 5) 거기에 먼저 베팅하고, 비어 있으면 비어 있는 코너 중 하나에 베팅하도록 하는 등 게임 플레이를 복잡하게 만들 수 있습니다. 셀 번호 1, 3, 7, 9) 등 - 일반적으로 이는 귀하의 재량에 달려 있습니다.

원칙적으로 이것이 그러한 게임을 만드는 데 필요한 전부입니다.

이제 데모 예제를 사용하여 게임을 시청하고 소스 파일(총 1개 파일)을 다운로드할 수 있습니다.

2018년 6월 8일 - 편지 작성자인 Patvakan Baghdasaryan에 대한 세심한 배려에 감사드립니다. 컴퓨터에 여러 개가 있을 때 버그가 수정되었습니다. 가능한 옵션승리와 그의 모든 승리 동작은 칠해졌습니다(3개가 아닌 4개에서 6개 셀로).

저는 그게 전부입니다. 이 강의가 여러분에게 도움이 되었기를 바랍니다. 행운을 빕니다. 안녕!

tic-tac-toe에서 이길 수 없는 봇을 작성하는 방법 또는 미니맥스 규칙 소개

수백 번의 tic-tac-toe 게임을 마친 후 최적의 알고리즘이 무엇인지 궁금해했을 가능성이 높습니다. 하지만 당신이 여기 있다면 아마도 이 게임의 구현을 작성하려고 시도했을 것입니다. 우리는 더 나아가 틱택토(tic-tac-toe)에서 이길 수 없는 봇을 작성할 것입니다. "왜?"라는 질문을 예상하여 알고리즘 덕분에 답변해 드리겠습니다.

프로 체스 선수와 마찬가지로 이 알고리즘은 게임이 끝날 때까지(승리, 패배 또는 무승부) 몇 수 앞서 상대방의 행동을 계산합니다. 이 최종 상태에 도달하면 AI는 승리 시 양수 점수(이 경우 +10), 패배 시 음수(-10), 무승부 시 중립(0)을 부여합니다.

동시에 알고리즘은 플레이어의 움직임에 대해 유사한 계산을 수행합니다. AI가 움직이면 점수가 가장 높은 움직임을 선택하고, 플레이어가 움직이면 점수가 가장 낮은 움직임을 선택합니다. 이 전략을 사용하여 minimax는 패배를 피합니다.

이 게임을 플레이해보세요.

minimax 알고리즘은 다음과 같은 재귀 함수로 가장 쉽게 설명됩니다.

  1. 최종 상태(+10, 0, -10)가 발견되면 값을 반환합니다.
  2. 필드의 모든 빈 셀을 통과하고,
  3. 각각에 대해 minimax 함수를 호출합니다(재귀).
  4. 얻은 값을 평가합니다.
  5. 그리고 가장 좋은 것을 반환합니다.

재귀에 익숙하지 않다면 Harvard CS50 과정에서 다음 강의를 시청해야 합니다.

minimax의 작동 방식을 이해하기 위해 구현을 작성하고 동작을 모델링해 보겠습니다. 이에 대해서는 다음 두 섹션에서 다루겠습니다.

미니맥스 구현

게임이 끝나가는 상황을 살펴보겠습니다(아래 그림 참조). Minimax는 가능한 모든 게임 상태(그리고 수십만 개가 있음)를 거치기 때문에 최종 게임을 고려하는 것이 합리적입니다. 이렇게 하면 더 적은 수의 재귀 함수 호출(총 9개)을 추적해야 합니다.

AI가 0으로 십자가, 사람을 가지고 놀게하십시오.

필드 작업을 더 쉽게 하기 위해 셀 내용과 동일한 값을 갖는 9개 요소의 배열로 선언해 보겠습니다. 위 그림처럼 십자가와 발가락으로 채우고 origBoard라고 부르자.

Var origBoard = ["O",1,"X","X",4,"X",6,"O","O"];

다음으로 aiPlayer와 huPlayer 변수를 선언하고 각각 "X"와 "O" 값을 할당하겠습니다.

또한, 당첨된 조합을 검색하고 검색에 성공하면 참값을 반환하는 함수와 사용 가능한 셀의 인덱스를 저장하는 함수가 필요합니다.

/* 초기 상태보드 O | | 엑스 --------- 엑스 | | 엑스 --------- | 오 | O */ var origBoard = [“O”,1 ,”X”,”X”,4 ,”X”, 6 ,”O”,”O”]; // 사람 var huPlayer = “O”; // AI var aiPlayer = “X”; // 보드의 빈 셀 인덱스 목록을 반환 function emptyIndices(board)( return board.filter(s => s != "O" && s != "X"); ) // 고려한 조합 승리 인덱스 함수 승리(보드, 플레이어)( if((보드 == 플레이어 && 보드 == 플레이어 && 보드 == 플레이어) || (보드 == 플레이어 && 보드 == 플레이어 && 보드 == 플레이어) || (보드 = = 플레이어 && 보드 == 플레이어 && 보드 == 플레이어) || (보드 == 플레이어 && 보드 == 플레이어 && 보드 == 플레이어) || (보드 == 플레이어 && 보드 == 플레이어 && 보드 == 플레이어) | | (보드 == 플레이어 && 보드 == 플레이어 && 보드 == 플레이어) || (보드 == 플레이어 && 보드 == 플레이어 && 보드 == 플레이어) || (보드 == 플레이어 && 보드 == 플레이어 && 보드 = = 플레이어)) ( true 반환; ) else ( false 반환; ) )

이제 newBoard와 player라는 두 개의 인수를 사용하여 minimax 함수를 정의해 보겠습니다. 그런 다음 필드에서 사용 가능한 셀의 인덱스를 찾아 availSpots 변수에 전달합니다.

// 주요 minimax 함수 function minimax(newBoard, player)( // 사용 가능한 셀 var availSpots = emptyIndices(newBoard);

또한 최종 상태를 추적하고 적절한 값을 반환해야 합니다. "0"이 이기면 -10을 반환해야 하고, "십자"가 - +10이면 반환해야 합니다. availSpots 배열의 크기가 0이면 사용 가능한 셀이 없으며 게임은 무승부로 종료되며 0이 반환되어야 합니다.

// 터미널 상태(승/패/무승부)를 확인하고 // 그에 따라 값을 반환합니다. if (wining(newBoard, huPlayer))( return (score:-10); ) else if (wining(newBoard, aiPlayer)) ( return (점수:10); ) else if (availSpots.length === 0)( return (점수:0); )

그런 다음, 빈 셀 각각에서 포인트를 수집해야 합니다. 이를 위해 이동 배열을 만들고 루프의 모든 빈 셀을 통과하여 이동 객체에 각 이동의 인덱스와 포인트를 배치합니다.

그런 다음 origBoard에 숫자로 저장된 빈 셀의 인덱스를 이동 객체의 index 속성과 동일하게 설정합니다. 그런 다음 현재 플레이어로서 새 필드 newBoard 의 빈 셀로 이동하고 다른 플레이어와 결과 필드 newBoard 에서 minimax 함수를 호출합니다. 그런 다음 minimax 함수에서 반환된 객체의 점수 속성을 이동 객체의 점수 속성에 넣어야 합니다.

minimax가 최종 상태를 찾지 못하면 최종 상태에 도달할 때까지 계속해서 게임 속으로 더 깊이 들어가게 됩니다. 그런 다음 이 재귀 "수준"의 지점을 한 수준 더 높은 수준으로 전송합니다.

마지막으로 함수는 newBoard에 대한 변경 사항을 재설정하고 이동 객체를 이동 배열에 배치합니다.

// 모든 객체를 저장할 배열 var move = ; // 사용 가능한 셀을 반복합니다. for (var i = 0; i< availSpots.length; i++){ //create an object for each and store the index of that spot var move = {}; move.index = newBoard]; // совершить ход за текущего игрока newBoard] = player; //получить очки, заработанные после вызова минимакса от противника текущего игрока if (player == aiPlayer){ var result = minimax(newBoard, huPlayer); move.score = result.score; } else{ var result = minimax(newBoard, aiPlayer); move.score = result.score; } // очистить клетку newBoard] = move.index; // положить объект в массив moves.push(move); }

그런 다음 Minimax는 이동 배열에서 최상의 이동을 선택해야 합니다. AI가 움직이면 가장 높은 점수의 움직임이 필요하고, 인간의 움직임이라면 가장 낮은 점수의 움직임이 필요합니다. 따라서 player 값이 aiPlayer이면 알고리즘은 bestScore 변수를 매우 작은 숫자로 초기화하고 이동 배열을 반복합니다. 이동 점수가 bestScore보다 더 많은 점수의 가치가 있으면 알고리즘은 해당 이동을 기억합니다. 동일한 포인트로 이동하는 경우 알고리즘은 첫 번째 것을 기억합니다.

player가 huPlayer와 동일한 경우 모든 것이 유사합니다. 이제 bestScore는 큰 숫자로 초기화되고 minimax는 가장 적은 포인트로 이동을 찾습니다.

결과적으로 minimax는 bestMove에 저장된 객체를 반환합니다.

// AI 이동인 경우 이동을 반복하고 포인트가 가장 많은 이동을 선택합니다. var bestMove; if(player === aiPlayer)( var bestScore = -10000; for(var i = 0; i< moves.length; i++){ if(moves[i].score >bestScore)( bestScore = move[i].score; bestMove = i; ) ) )else( // 그렇지 않으면 동작을 반복하고 포인트가 가장 적은 동작을 선택합니다. var bestScore = 10000; for(var i = 0; i< moves.length; i++){ if(moves[i].score < bestScore){ bestScore = moves[i].score; bestMove = i; } } } // вернуть выбранный ход (объект) из массива ходов return moves; }

다음 섹션에서는 프로그램이 어떻게 작동하는지 이해하기 위해 프로그램을 시뮬레이션하겠습니다.

미니맥스 작동 중

아래 다이어그램을 사용하여 알고리즘의 단계별 모델을 분석해 보겠습니다.

메모: 다이어그램에서 큰 숫자는 함수 호출의 일련 번호를 나타내고, 레벨은 알고리즘이 앞으로 몇 이동했는지 나타냅니다.

  1. origBoard와 aiPlayer가 알고리즘에 제공됩니다. 발견된 세 개의 빈 셀 목록을 만들고, 상태의 유한성을 확인하고, 모든 빈 셀을 반복합니다. 그런 다음 알고리즘은 newBoard 를 변경하여 aiPlayer를 첫 번째 빈 사각형에 배치합니다. 그런 다음 newBoard 및 huPlayer에서 자신을 호출하고 두 번째 호출이 값을 반환할 때까지 기다립니다.
  2. 첫 번째 함수 호출이 계속 실행되는 동안 두 번째 함수 호출이 실행되어 두 개의 빈 셀 목록을 만들고 유한 상태를 확인하고 모든 빈 셀을 반복합니다. 그런 다음 두 번째 호출은 첫 번째 빈 사각형에 huPlayer를 배치하여 newBoard를 수정합니다. 그 후 newBoard 및 aiPlayer에서 자신을 호출하고 세 번째 호출이 값을 반환할 때까지 기다립니다.

  3. 두 번째 호출에서 두 개의 빈 셀을 찾았으므로 minimax는 두 번째 빈 셀에 huPlayer를 배치하여 newBoard를 수정합니다. 그런 다음 newBoard 및 aiPlayer에서 자신을 호출합니다.

  4. 알고리즘은 빈 셀의 목록을 컴파일하고 상태의 유한성을 확인한 후 플레이어의 승리를 기록합니다. 따라서 점수 필드가 (-10)인 개체를 반환합니다.

    두 번째 함수 호출에서는 알고리즘이 세 번째, 네 번째 함수 호출에 의해 하위 수준에서 반환된 값을 받습니다. huPlayer의 움직임으로 인해 이 두 가지 결과가 생성되었으므로 알고리즘은 더 작은 결과를 선택합니다. 동일하기 때문에 알고리즘은 첫 번째 것을 선택하여 첫 번째 함수 호출에 전달합니다.

    이 시점에서 첫 번째 함수 호출은 aiPlayer가 첫 번째 빈 셀로 이동하는 추정치를 수신했습니다. 그런 다음 두 번째 빈 사각형에 aiPlayer를 배치하여 newBoard를 수정합니다. 그 후에는 newBoard 및 huPlayer에서 자신을 호출합니다.

  5. 다섯 번째 함수 호출에서 알고리즘은 빈 셀의 목록을 컴파일하고 상태의 유한성을 확인한 후 AI의 승리를 기록합니다. 따라서 점수 필드가 +10인 개체를 반환합니다.

    그 후 첫 번째 호출은 세 번째 빈 셀에 aiPlayer를 배치하여 newBoard를 수정합니다. 그런 다음 newBoard 및 huPlayer에서 자신을 호출합니다.

  6. 여섯 번째 호출은 두 개의 빈 셀 목록을 만들고 상태의 유한성을 확인한 다음 모든 빈 셀을 반복합니다. 그런 다음 첫 번째 빈 사각형에 huPlayer를 배치하여 newBoard를 수정합니다. 그런 다음 newBoard 및 aiPlayer에서 자신을 호출하고 일곱 번째 호출이 값을 반환할 때까지 기다립니다.
  7. 새로운 호출은 하나의 빈 셀 목록을 구성하고, 유한 상태를 확인하고, 빈 셀에 aiPlayer를 배치하도록 newBoard를 수정합니다. 그런 다음 newBoard 및 huPlayer에서 자신을 호출하고 이 호출이 값을 반환할 때까지 기다립니다.
  8. 여덟번째 도전은 빈 목록셀을 비우고 팔다리 상태를 확인한 후 aiPlayer의 승리를 기록합니다. 따라서 일곱 번째 호출에서는 상위 레벨에 대해 (+10) 개수 필드가 있는 객체를 반환합니다.

    일곱 번째 호출은 하위 수준에서 단 하나의 양수 값만 받았습니다. 이 값은 aiPlayer의 차례 동안 얻은 것이므로 알고리즘은 얻은 가장 큰 값을 반환합니다. 그래서 6번째 호출인 레벨 업에 양수 값(+10)을 반환합니다.

    여섯 번째 호출에서 두 개의 빈 셀을 찾았으므로 minimax는 두 번째 빈 셀에 huPlayer를 배치하여 newBoard를 수정합니다. 그런 다음 newBoard 및 aiPlayer에서 자신을 호출합니다.

  9. 그 후, 알고리즘은 빈 셀의 목록을 컴파일하고 상태의 유한성을 확인한 후 aiPlayer의 승리를 기록합니다. 따라서 상위 수준의 점수 필드가 (+10)인 객체를 반환합니다.

    이때 여섯 번째 챌린지는 일곱 번째 챌린지가 반환한 점수(+10)와 아홉 번째 챌린지가 반환한 점수(-10) 중 하나를 선택해야 합니다. huPlayer의 움직임으로 인해 이 두 가지 결과가 생성되었으므로 알고리즘은 더 작은 결과를 선택하고 점수 및 인덱스 필드가 있는 개체로 다음 수준으로 반환합니다.

    마지막으로 첫 번째 호출의 세 가지 분기가 모두 평가됩니다(-10, +10, -10). aiPlayer의 이동으로 인해 이 세 가지 결과가 생성되었으므로 알고리즘은 가장 많은 포인트 수(+10)와 해당 인덱스(4)를 포함하는 개체를 선택합니다.

위의 시나리오에서 minimax는 다음을 결정합니다. 최적의 선택필드의 중앙 광장으로 이동합니다.

끝!

지금까지 당신은 minimax 알고리즘이 어떻게 작동하는지 이해했을 것입니다. 구현을 직접 작성해 보거나 GitHub 또는 CodePen의 예제를 보고 최적화하세요.

게임 내 AI 주제에 관심이 있다면 이 주제에 대한 자료를 읽어 보시기 바랍니다.

1단계. 양식 매개변수 구성1. 양식을 맞춤설정하려면 크기를 설정하세요.
가로 510도트, 가로 480도트
수직의. 최대 및 최소
동일한 값과 동일한 크기를 나타냅니다.
2. 모양의 이름을 "Tic Tac Toe"로 지정하세요.
3. 양식 배경은 폴더에 있는 파일을 사용하세요.
background.png라는 이름으로 이미지를 배치하고
양식 중앙에 있습니다.
4. 제목 줄에 있는 아이콘의 경우
폴더의 파일 사용
menu4.ico라는 이름의 이미지.
5. 폼의 배경색을 설정해야 합니다.
민트크림.

2단계. 양식에 버튼과 라벨 추가하기

1. 배치된 요소의 경우 변경
글꼴 크기를 12로 설정하고 배경을 설정합니다.
투명한.

1. 항목이 포함된 메뉴 표시줄을 만듭니다.
이미지에 표시된 것처럼.

3단계. 양식에 메뉴 막대 추가

1. 파일 메뉴 항목에서 명령을 생성합니다.
출구.
2.

출구
처방하자
프로그램 코드는 다음과 같습니다.
이전 신청서.

3단계. 양식에 메뉴 막대 추가

1. 게임 메뉴 항목에서 팀을 만듭니다.
새로운 게임.
2. New game 명령에 대해 다음과 같이 작성합니다.
앞으로는 프로그램 코드를 통해
몇 걸음.

3단계. 양식에 메뉴 막대 추가

1. 도움말 메뉴 항목에서 명령을 만듭니다.
프로그램에 대해.
2. 프로그램 정보 명령에 대해 새 명령을 만듭니다.
프로그램 코드를 형성하고 작성
전작과 비슷하다
애플리케이션.

1. PictureBox 개체를 양식으로 드래그하여
크기를 100x100으로 변경합니다.
2. 투명한 배경을 설정하세요.
3. 그림과 같이 PictureBox를 배치합니다.
게임 필드의 첫 번째 셀 위에 있는 그림입니다.

4단계. 양식에 Picturebox 개체 추가하기

1
2
3
4
5
6
7
8
9
1. 우리가 배치하는 나머지 셀 위에
PictureBox 개체, 첫 번째 복사본
개체에 표시된 번호에 따라
이미지.

1. 코드를 작성하기 전에
폴더에 필요한
\비주얼 스튜디오
2010\프로젝트\Tic Tac Toe\X's
zeros\bin\Debug\를 다시 ​​롤링해야 합니다.
이미지 폴더의 x.png, 0.png, none.png 파일.
2. 첫 번째 항목을 마우스 왼쪽 버튼으로 더블클릭합니다.
PictureBox.

5단계. Picturebox 개체에 대한 코드 추가

1. 다음의 모든 요소에 액세스할 수 있는 2차원 배열을 만듭니다.
정수로 구성된 3x3 요소 형태로 생성됩니다. 곧바로
선언할 때 0으로 채웁니다. 빈 셀의 경우
"십자형"에는 1, "0"에는 -1을 사용합니다.

5단계. Picturebox 개체에 대한 코드 추가

첫 번째를 클릭하는 과정에서
PictureBox, 연산자 추가
선택
어느
~ 할 것이다
상태 확인
배열 셀. 값이
배열 셀은 0과 같습니다.
이는 "0"도 없고 "0"도 없다는 것을 의미합니다.
"십자가"를 입력한 다음 이 셀에
배열은 1로 작성되고
PictureBox1
표시됨
"십자가"의 이미지, 그리고 만약
배열 셀의 값은 다음과 같습니다.
1과 같으면 다음을 포함합니다.
"십자가"와 그 안에 0이 적혀 있습니다.
빈 셀이 표시됩니다.

5단계. Picturebox 개체에 대한 코드 추가

1
2
3
4
5
6
7
8
9



필드의 나머지 셀에 대해 코드를 추가하십시오.
처음과 동일하지만 변경 사항만 변경됩니다.
PictureBox 개체 번호 및 셀 주소
정렬.
두 번째 셀의 예:

버튼을 클릭했을 때의 과정에서
추가하다
운영자
주기
누가 확인하는가
처음부터 시작하여 모든 셀
빈 셀의 존재. 그리고 만약에
셀이 비어 있습니다.
그럼 그녀에게
"0"이라고 쓰여 있습니다.
배열 셀은 -1로 기록됩니다.
향후 작업의 편의를 위해
변수
나,
제이
어느
루프를 반복하는 데 사용됩니다.
전체 양식에 대해 공지하겠습니다.

6단계. 걷기 버튼에 대한 코드 추가

0을 표시하려면
노름
필드
필요한
본문에 프로그램 코드 추가
셀이 비어 있는지 확인하는 주기입니다.
중첩된 문 사용
분기
~ 할 것이다
일어나다
배열 셀 주소 분석
올바른 값으로 0을 출력
PictureBox.
또한 break 문도 추가합니다.
조기 종료를 위해
빈 것을 찾을 때 루프
세포.

6단계. 걷기 버튼에 대한 코드 추가

게임 상태를 표시하려면
인터페이스 요소가 사용됨
라벨1. 플레이어는 항상 움직이기 때문에
첫 번째
저것
~에
다운로드
애플리케이션
필요한
V
요소
라벨1
필요한
반영하다
"당신의
이동하다."
을 위한
이것
만들어 보자
변하기 쉬운
답변
어느
이 문구를 지정해 보겠습니다. ㅏ
양식을 변수로 로드할 때
요소에 할당되어야 합니다.
Label1, 필요한 생성
절차
필요한
먼저 더블클릭하세요
형태에 따라

7단계. 새 게임 메뉴 항목에 대한 프로그램 코드 추가

new 명령을 눌렀을 때
게임이 재설정됩니다
배열의 모든 셀, 모두 교체
"십자가"와 "발가락"
빈 셀. 또한 결론은
"당신의 움직임" 표시

8단계. 게임 결과 출력

이동 결과를 확인하려면
필요한
분석하다
행, 열의 내용
대각선. 세 가지 요소가 모두 있는 경우
가 1이면 이는 플레이어의 승리이며,
3 -1이면 패배입니다.
플레이어.
승리 확인 필요
지휘하다
~ 전에
진전
컴퓨터,

확인하다
패배 후.
프로시저의 마지막 명령
결과가 화면에 표시됩니다
진전.

8단계. 게임 결과 출력

사용자의 승리를 확인하는 프로그램 코드:
if (znacheniyeYacheyki == 1 && znacheniyeYacheyki == 1 && znacheniyeYacheyki == 1) otvet = "당신이 승리했습니다";
if (znacheniyeYacheyki == 1 && znacheniyeYacheyki == 1 && znacheniyeYacheyki == 1) otvet = "당신이 승리했습니다";
if (znacheniyeYacheyki == 1 && znacheniyeYacheyki == 1 && znacheniyeYacheyki == 1) otvet = "당신이 승리했습니다";
if (znacheniyeYacheyki == 1 && znacheniyeYacheyki == 1 && znacheniyeYacheyki == 1) otvet = "당신이 승리했습니다";
if (znacheniyeYacheyki == 1 && znacheniyeYacheyki == 1 && znacheniyeYacheyki == 1) otvet = "당신이 승리했습니다";
if (znacheniyeYacheyki == 1 && znacheniyeYacheyki == 1 && znacheniyeYacheyki == 1) otvet = "당신이 승리했습니다";
if (znacheniyeYacheyki == 1 && znacheniyeYacheyki == 1 && znacheniyeYacheyki == 1) otvet = "당신이 승리했습니다";
if (znacheniyeYacheyki == 1 && znacheniyeYacheyki == 1 && znacheniyeYacheyki == 1) otvet = "당신이 승리했습니다";
사용자의 승리를 확인하는 프로그램 코드:
if (znacheniyeYacheyki == -1 & znacheniyeYacheyki == -1 & znacheniyeYacheyki == -1) otvet = "당신은 패배했습니다";
if (znacheniyeYacheyki == -1 & znacheniyeYacheyki == -1 & znacheniyeYacheyki == -1) otvet = "당신은 패배했습니다";
if (znacheniyeYacheyki == -1 & znacheniyeYacheyki == -1 & znacheniyeYacheyki == -1) otvet = "당신은 패배했습니다";
if (znacheniyeYacheyki == -1 & znacheniyeYacheyki == -1 & znacheniyeYacheyki == -1) otvet = "당신은 패배했습니다";
if (znacheniyeYacheyki == -1 & znacheniyeYacheyki == -1 & znacheniyeYacheyki == -1) otvet = "당신은 패배했습니다";
if (znacheniyeYacheyki == -1 & znacheniyeYacheyki == -1 & znacheniyeYacheyki == -1) otvet = "당신은 패배했습니다";
label1.Text = 답변;

9단계 플레이 가능성 향상

연주성을 향상시키기 위해
직렬 출력 대신
첫 번째 빈 것들에게
"0"의 셀을 구현합니다.
랜덤 생성기를 통한 출력
숫자.
이렇게 하려면 다음을 추가해야 합니다.
하나의 논리 변수
uslovie를 실행하고 루프 유형을 For에서 변경합니다.
on 동안, 우리는 모르기 때문에
정확한 반복 횟수
발전기 난수안녕
빈 셀에 들어가지 않습니다.

러시아 교육과학부

고등 전문 교육을 위한 연방 주 예산 교육 기관

"볼로그다 주립대학교"

자동화컴퓨터공학과

해당 분야의 코스 프로젝트에 대한 설명 메모

프로그래밍 및 알고리즘 기초

"틱택토"

완전한그룹 EM-21의 학생

Butorova L.Yu.

수락됨 Rzheutskaya S. Yu.

소개

1. 개발할 프로그램에 대한 문제 분석 및 요구 사항 결정

1 프로그램의 목적, 사용자, 주요 기능 및 개발 과정에서 추구하는 목표

2 유사한 기능을 수행하는 잘 알려진 프로그램 검토

3 개발의 이론적 기초

4 개발 도구 선택

개발의 디자인 부분

1 사용자 인터페이스 개발

2.2 데이터 구조 개발(외부 및 RAM)

2.3 알고리즘 개발 및 분석

C++ 언어로 프로그램 구현

1 프로그램 아키텍처

2 표준 시각적 및 비시각적 구성 요소 선택

시험 결과

결론

서지

응용

소개

Tic-tac-toe는 3 x 3 셀 이상의 정사각형 필드(최대 "무한 필드")에서 두 상대 간의 논리적 게임입니다. 플레이어 중 한 명은 "십자가"로 플레이하고 두 번째 플레이어는 "발가락"으로 플레이합니다. 이 게임은 컴퓨터가 출현하기 오래 전부터, 일반 종이와 펜을 사용하여 플레이하기 전부터 인기를 얻었습니다. 중국의 전통 게임은 검은 돌과 흰 돌을 사용합니다.

이에 코스 작업게임 필드의 기본 규칙과 표준 크기(3x3 셀)는 그대로 유지됩니다. 게임의 편의를 위해 첫 번째 이동 권한은 사용자에게 있습니다. 즉, "크로스"입니다.

Tic Tac Toe는 사용자를 즐겁게 하도록 설계된 프로그램이므로 이 과정 작업의 인터페이스는 게임 프로세스의 감정적인 부분을 높이는 긍정적인 색상의 조합을 사용하여 게임 스타일로 만들어졌습니다.

게임에는 세 가지 유형이 있습니다. X 대 0(사용자 대 사용자), "컴퓨터를 사용하는 1레벨"(세계 게임의 기본을 이제 막 배우는 사람들을 위한), "컴퓨터를 사용하는 2레벨"(컴퓨터를 사용하는 2레벨) 그들의 승리를 절대적으로 확신하고 있습니다. 레벨 1과 2에서는 "승리", "패배", "무승부"라는 세 가지 가능한 결과가 있습니다. 수직, 수평 또는 대각선이 십자 또는 0으로 완전히 채워지면 상금이 고정됩니다.

필드의 여유 셀이 종료되었지만 아무도 승리하지 못한 경우 게임은 "무승부"로 끝나는 것으로 간주됩니다.

1. 개발할 프로그램에 대한 문제 분석 및 요구 사항 결정

프로그램 크로스 인터페이스

1.1 프로그램의 목적, 사용자, 주요 기능 및 개발 중 추구하는 목표

이 프로그램의 목적은 무엇보다도 사용자를 즐겁게 하고, 사람의 대기 시간을 밝게 하는 것입니다. 모든 작업에는 휴식이 필요하기 때문이며, 이 간단한 게임은 긴장을 풀고 일상 업무에서 벗어나는 데 도움이 될 것입니다. "Tic Tac Toe"는 또한 논리적 사고를 훈련하고 집중력을 집중시키며 기억력을 개발할 수 있도록 고안된 지적 및 논리적 게임 클래스에 속합니다.

사용자의 타겟층은 어린이와 청소년, 성인입니다. 제품 사용의 주요 기준은 프로그램에 작성된 텍스트를 읽는 능력과 버튼을 사용하여 컴퓨터에 필요한 작업을 선택하는 능력입니다.

이것으로부터 우리는 주요 작업이 오락 작업과 사람의 논리적 잠재력을 개발하는 작업이라는 결론을 내릴 수 있습니다.

1.2 유사한 기능을 수행하는 잘 알려진 프로그램 검토

인터넷에서 이 게임을 구현한 수많은 작품을 찾을 수 있습니다. 현재 이 게임에는 원래 표준에서 벗어난 유사 게임이 많이 있습니다. 그러한 프로그램의 예로는 "끝없는 들판의 Tic-tac-toe"와 "Tic-tac-toe 3D"가 있습니다. 또한 많은 게임에서 "십자가"와 "발가락"은 예를 들어 "돌"과 같은 다른 기호로 대체됩니다.

내 강좌 프로젝트는 PC 애플리케이션입니다. 이 게임은 상대가 인공 지능(또는 컴퓨터)인 한 명의 사용자와 두 명의 사용자를 모두 대상으로 합니다. 그것은 고전적인 3x3 필드에 표시됩니다.

내 생각에 가장 흥미롭고 특이한 게임은 "Tic Tac Toe 3D" 게임이었습니다. 그래서 비교 대상으로 선택했습니다.

3D tic-tac-toe는 종이나 일반 보드보다 훨씬 더 흥미롭습니다. 승리하고 패배할 기회가 더 많아지며, 무승부는 덜 일반적입니다. 혼자(컴퓨터를 상대로) 플레이할 수도 있고, 친구와 함께 플레이할 수도 있습니다. 그리고 여기서 가장 특이한 점은 승리하려면 한 레벨뿐만 아니라 벽 평면을 따라, 심지어 전체 필드를 대각선으로 가로지르는 세 개의 색상(검은색 또는 흰색) 공의 조합을 만들 수 있다는 것입니다. 그림 1.1).

쌀. 1.1

유사한 주제에 대한 다양한 게임 중에서 우리는 각 작품에서 계획의 고유한 구현을 강조할 수 있습니다. 각 프로젝트는 개성이 다른 프로젝트와 다릅니다.

1.3 개발의 이론적 기초

분석

각 당사자에는 상대방의 게임에서 무승부를 보장하고 상대방이 실수를 하면 승리할 수 있도록 하는 잘 알려진 알고리즘이 있습니다. 그러니까 게임이 그런 상태야. "아무도 죽지 않아"<#"877528.files/image002.gif">

그림 1.2. 게임 상황 트리

tic-tac-toe 게임에 대한 게임 상황의 부분 트리가 그림 1.2에 나와 있습니다. tic-tac-toe 게임의 게임 상황 트리입니다. "십자가" 플레이어가 먼저 가서 위의 알고리즘에 따라 행동하고, "발가락" 플레이어는 원하는 것은 무엇이든 할 수 있습니다(그리고 하나의 꼭지점은 다음과 같습니다). 합리적인 행동과 비합리적인 행동, 즉 다른 행동에 대해 제공됨)은 50개의 노드로 구성됩니다.

1.4 개발 도구 선택

목표를 달성하려면 통합 애플리케이션 개발 환경이 필요합니다. 따라서 프로젝트 개발은 Microsoft Visual Studio 2008 프로그래밍 환경에서 진행되었습니다.

Microsoft Visual Studio는 Microsoft 제품 라인입니다. , 통합 개발 환경 포함 소프트웨어 및 기타 여러 도구. 이 제품을 사용하면 콘솔 기반으로 개발할 수 있습니다. 애플리케이션 및 GUI 애플리케이션 , Windows Forms 기술 지원 포함 , 그리고 웹사이트 , 웹 서비스 네이티브처럼 , 제어 Windows에서 지원하는 모든 플랫폼에 대한 코드 ,윈도우 모바일 ,윈도우CE , .넷 프레임 워크 , 엑스박스 , 윈도우 폰 .NET 컴팩트 프레임워크 그리고 실버라이트 .

2. 개발의 디자인 부분

2.1 사용자 인터페이스 개발

게임 애플리케이션을 만들 때 제품 성공의 주요 구성 요소 중 하나인 인터페이스를 고려해야 합니다. 프로그램의 사용자 인터페이스는 무엇보다도 사용자가 이해하기 쉽고 매력적이어야 합니다. 사용자의 주의를 산만하게 하거나 불편함을 야기하는 모든 순간을 제거해야 합니다. 전체 프로그램 인터페이스는 두 가지 구성 요소로 나눌 수 있습니다.

) 프로그램의 메인 메뉴

쌀. 2.1 - 프로그램의 메인 메뉴

메인 메뉴는 사용자가 게임 분위기에 참여할 수 있도록 디자인되었으므로 인터페이스는 다채롭고 유쾌한 색상으로 디자인되었습니다. 메뉴를 통해 경기장으로 이동하거나, 게임 규칙을 확인하거나, 게임을 종료할 수 있습니다.

) 축구 따위의 경기장

그림 2.2 - 경기장

경기장에는 플레이어와 컴퓨터가 아이콘을 배치하는 직접적인 플레이 영역이 포함됩니다. 게임을 시작하기 전에 사용자는 "X 대 0", "컴퓨터로 1레벨" 또는 "컴퓨터로 2레벨"과 같은 게임 유형을 선택해야 합니다. 그렇지 않으면 프로그램에서 무엇을 해야 할지에 대한 메시지를 표시합니다. 플레이어가 메인 메뉴로 돌아가는 데 도움이 되는 버튼입니다. 마지막에는 참가자에게 경기 결과를 알리는 추가 창이 나타납니다.

쌀. 2.3 - 추가 게임 결과 창

2.2 데이터 구조 개발(외부 및 RAM)

RAM은 경기장의 상태를 저장하는 9개 요소로 구성된 1차원 배열에 사용됩니다. 여기서 배열의 각 셀은 경기장의 셀에 해당합니다. 레벨 번호, 턴 순서 등 정적 변수에도 메모리가 사용됩니다.

작동하려면 803KB의 여유 메모리가 필요합니다.

.3 알고리즘 개발 및 분석

게임의 사고 알고리즘을 구현하려면 정적 배열 gcnew 배열을 설정해야 합니다. (9); 여기에는 경기장의 상태가 저장되며, 배열의 각 셀은 셀에 해당합니다. "0" - 빈 셀에 해당합니다. 플레이어가 셀로 이동한 경우(즉, "X") 값 "1"이 기록되고 컴퓨터가 이동한 경우(즉 "O") 값이 기록됩니다. “2”. 처음에는 모든 배열 요소가 "0"과 같습니다. 레벨 데이터를 저장하는 정적 변수 lvl을 설정해야 합니다. 이 게임에는 총 3개의 레벨이 있습니다. 사용자가 "X vs O" 게임 유형을 선택한 경우 lvl은 "1" 값을, "컴퓨터를 사용하는 첫 번째 레벨"인 경우 "2" 값을, "3" 값을 갖습니다. ” “컴퓨터로 2단계”인 경우 " 플레이어 변수는 차례 순서를 저장합니다("true"는 플레이어의 차례이고 "false"는 컴퓨터의 차례입니다). 첫 번째 이동 권한이 사용자에게 부여되므로 게임 시작 시 플레이어 = true입니다. flag 정적 변수는 경기장에 빈 셀이 있는지 여부에 대한 정보를 저장합니다. 플래그 = true인 경우, 즉 false인 경우 빈 셀이 없습니다. 검증 알고리즘은 배열 x의 매개변수 반복을 포함해야 하며 자체 솔루션을 제시해야 하며 이는 추가 플레이에 최적입니다. 이 프로그램은 컴퓨터를 가지고 놀 수 있는 2가지 레벨을 제공합니다. 레벨 1에서 컴퓨터의 임무는 상대를 이기는 것이 아닙니다. 그렇기 때문에 이 기능컴퓨터가 이동할 셀의 임의 값을 반환합니다. 이 알고리즘의 코드는 [부록 1]에 나와 있습니다. 그림 2.4는 코드 구현의 블록 다이어그램을 보여줍니다.

게임 시작 시 가장 승리할 수 있는 움직임은 필드 중앙으로 이동하는 것입니다. dif_level() 함수에서는 처음에 조건을 확인합니다. 즉, 플레이어가 중앙 필드로 가지 않으면 컴퓨터가 중앙 필드로 이동합니다. 그렇지 않고 플레이어가 중앙으로 갔다면 check(2) 함수가 호출되어 컴퓨터 조합을 확인하고, 승리할 기회가 있으면 셀 번호를 반환합니다. 컴퓨터가 다음 수에서 승리할 수 없으면 check(1) 함수가 호출되어 플레이어의 조합을 확인합니다. 플레이어가 베팅한 경우 승리할 셀의 번호가 반환됩니다. 그러한 조합이 없으면 low_level() 함수가 호출됩니다.

그림 2.4. - 블록 다이어그램

그림 2.5. - 블록 다이어그램

3. C++ 언어로 프로그램 구현

.1 프로그램 아키텍처

이 프로그램은 메인 메뉴(그림 2.1), 플레이 필드(그림 2.2), 도움말 필드(게임 규칙)의 3가지 형태를 구현합니다. 12개 패널 중 9개가 메인 패널입니다. 또한 게임이 끝나면 결과가 포함된 pictureBox가 나타나며 총 5개가 있습니다(그림 2.3).

패널 클릭 핸들러를 기본으로 사용할 수 있으며, 그 중 정확히 9개가 경기장에 있습니다. 각 핸들러는 여러 함수를 호출합니다. 처음에는 사용자가 게임 유형 "X 대 0"을 선택하면 셀이 단순히 1 또는 2(십자 또는 0) 값으로 채워지는 조건이 있습니다. 다음으로 함수가 나옵니다. 진행 표시(CrossZero())는 십자형을 0으로 변경하고 그 반대로 변경하며, 점유된 셀을 차단하고 Array()를 확인하고 승자()를 찾습니다. Winner() 함수는 가능한 모든 승리 옵션을 고려하므로 플레이어 중 한 명이 자신의 말 3개(십자형 또는 0)를 수직, 수평 또는 대각선으로 정렬하면 승리하게 됩니다. 그렇지 않고 필드가 꽉 찼는데 플레이어 중 누구도 줄을 서지 않은 경우 동점 확인 함수(_friend())가 호출되어 필드에 여유 셀이 남아 있는지 여부를 확인합니다. fr = true이면 필드에 사용 가능한 셀이 없습니다. 값이 변경된 경우 해당 필드에 사용 가능한 셀이 있음을 의미합니다.

두 번째 조건은 두 번째 또는 세 번째 게임 유형을 선택한 경우에 적용됩니다. 그런 다음 컴퓨터가 이동한 함수를 move(int n)라고 합니다. 플레이어가 클릭한 셀의 번호를 전송합니다. 다음으로 진행 표시(CrossZero()), 점유 셀 차단 검사(Array()) 기능이 있습니다. 그런 다음 Winner() 함수가 호출되어 플레이어가 이 동작으로 승리했는지 여부를 확인합니다. 그렇지 않은 경우 자유 셀이 있는지 확인합니다. 사용 가능한 셀이 있으면 컴퓨터가 이동합니다. 다음으로, 플레이어 "1" 또는 "2"가 선택한 레벨에 따라 low_level(), dif_level() 함수가 호출됩니다. low_level() 함수는 0을 무작위로 배치할 위치를 선택하고, dif_level() 함수는 컴퓨터가 승리할 수 있는 특별한 알고리즘을 제시합니다. 다음으로 진행 표시(CrossZero()), 점유 셀 차단 검사(Array()) 기능이 있습니다. 그런 다음 Winner() 함수가 호출되어 컴퓨터가 이 동작으로 승리했는지 여부를 확인합니다. 그렇지 않은 경우 자유 셀이 있는지 확인합니다. 여유 셀이 있으면 플레이어가 이동합니다.

.2 표준 시각적 및 비시각적 구성 요소 선택

이 작업을 구현하기 위해 다음 구성 요소가 선택되었습니다.

1) Form1, 주어진 매개변수텍스트=틱택토, ControlBox=False

2) f2, 지정된 매개변수 BackColor, Text=Game 사용

) 지정된 항목 매개변수가 있는 콤보박스1:

X 대 0

1층은 컴퓨터로

2층은 컴퓨터로

4) 지정된 BackColor 매개변수와 Visible 및 Enabled 매개변수에 대한 다른 값이 있는 패널. 일부 패널의 경우 Click과 같은 이벤트가 작성되었습니다.

5) 버튼, 지정된 매개변수 Font, Fore Color, BackColor, Text를 사용하여 Click과 같은 모든 버튼에 대해 기록되었습니다.

6) 지정된 매개변수 BackColor, Font, Fore Color, Text를 사용하는 레이블.

) 지정된 매개변수가 있는 pictureBox, Image, SizeMode= StretchImage.

) 지정된 매개변수 BackColor, Font, Fore Color, Text=” ”를 사용하는 textBox.

4. 테스트 결과

3가지 유형의 게임을 통해 프로그램을 테스트해 보겠습니다.

메인 메뉴 버튼의 동작을 시도해 보겠습니다. 버튼이 제대로 작동합니다. 게임 종류를 선택하지 않고 게임을 시작해 보겠습니다. 프로그램은 오류 메시지를 표시하고 게임 유형을 선택하라는 메시지를 표시합니다(그림 4.1).

그림 4.1.

"X 대 0"이라는 게임 유형 1개를 선택해 보겠습니다. 사용자 대 사용자. 게임의 이 단계에서 사용자는 스스로 플레이할 수도 있습니다. (그림 4.2)

그림 4.2.

"컴퓨터를 이용한 레벨 1" 게임 중에 컴퓨터는 참가자를 이기기 위한 목표를 스스로 설정하지 않습니다. 그 사람은 그냥 0만 넣었어 무료 장소필드. 이 단계에서 사용자는 쉽게 컴퓨터를 이길 수 있습니다. 이 수준에서는 이벤트 개발을 위한 다른 옵션도 가능합니다.

그림 4.3.

게임 유형: "컴퓨터를 이용한 레벨 2". 이 단계에서 게임은 모든 동작을 분석하고 가장 최적의 동작을 선택하려고 시도합니다. 여기에서도 세 가지 시나리오가 모두 가능합니다. 컴퓨터는 먼저 빈 셀로 이동합니다. 대부분의 경우 게임은 무승부로 이어집니다.

그림 4.4.

프로그램은 모든 테스트 변형에서 오류 없이 성공적으로 실행됩니다.

결론

작업 초기에 설정된 작업이 완료되었다고 자신있게 말할 수 있습니다. 개발 과정에서 유명 게임 '틱택토(Tic Tac Toe)'를 리믹스하는 프로젝트도 기획, 개발됐다. 게임은 지정된 요구 사항을 충족하고 해당 기능을 수행합니다. 작품에 구현됨 다양한 방식게임과 난이도.

작업하는 동안 통합 개발 환경에서 새로운 프로그래밍 방법이 마스터되었습니다. C++ 언어 작업에 대한 기존 지식이 통합되었습니다. 학습 과정을 준비하면서 이 게임을 구현하기 위한 다양한 방법과 알고리즘을 분석했습니다.

이 프로그램은 단순해 보이지만 Visual C++의 모든 기본 기술을 사용하여 구현하기에는 여러 가지 어려움이 있습니다.

이 프로그램의 특징은 다음과 같습니다.

명확하게 구성된 알고리즘

직관적인 인터페이스;

사용하기 쉬운;

아주 명확한 사용자 설명서;

불필요한 추가 기능이 없습니다.

서지

1. http://www.pravilaigr.ru/xo.php

2. http://2igroka.com/stuff/sportivnye/krestiki_noliki_3d/15-1-0-14

3. https://www.draw.io/

http://pol-video.ru/QPW9QHEO2GU/uroki_s_krestiki-noliki_ch1.html

부속서 1

private: int low_level())(// 가벼운 상대를 위한 절차;::Random^ rand = gcnew System::Random();(= rand->Next(0,8);

) while (x[r] != 0);r;

부록 2

private: bool check(int n)(k = -1;// 모든 조합을 확인하고 올바른 이동을 반환합니다(x == n) (((x == n)&&(x == 0)) k =2; ((x == n)&&(x == 0)) k =1;((x == n)&&(x == 0)) k =6;((x == n)&&(x == 0)) k =3;((x == n)&&(x == 0)) k =8;((x == n)&&(x == 0)) k =4;

)(x == n) (((x == n)&&(x == 0)) k =0;((x == n)&&(x == 0)) k =7;((x = = n)&&(x == 0)) k =4;

)(x == n) (((x == n)&&(x == 0)) k =4;((x == n)&&(x == 0)) k =6;((x = = n)&&(x == 0)) k =8;((x == n)&&(x == 0)) k =5;

)(x == n) (((x == n)&&(x == 0)) k =0;((x == n)&&(x == 0)) k =5;((x = = n)&&(x == 0)) k =4;

)(x == n) (((x == n)&&(x == 0)) k =0;((x == n)&&(x == 0)) k =3;((x = = n)&&(x == 0)) k =1;((x == n)&&(x == 0)) k =2;

)(x == n) (((x == n)&&(x == 0)) k =2;

)(x == n) (((x == n)&&(x == 0)) k =8;((x == n)&&(x == 0)) k =7;

)(x == n) (((x == n)&&(x == 0)) k =6;

)(k!=-1) true를 반환하고 그렇지 않으면 false를 반환합니다.

부록 3

private: int dif_level())(//어려운 적

//return check(2);(x == 0) return (4);(check(2)) return k; else(check(1))는 k를 반환합니다. 그렇지 않으면 low_level();

애플리케이션 4

private: void CrossZero())(// 십자가를 0으로 변경합니다(진행률 표시기)(플레이어) (->Visible = true;->Visible = false;

) else (->표시 = true;->표시 = false;

): void 검사Array())(// 셀에 뭔가가 있는지 확인하는 함수, 있으면 더 이상 이 셀을 클릭할 수 없습니다.(x == 1) (panel1->BackgroundImage = panel11->BackgroundImage ;panel1->활성화 = false;)(x == 2) (panel1->BackgroundImage = panel10->BackgroundImage;panel1->활성화 = false;)(x == 1) (panel2->BackgroundImage = panel11->BackgroundImage ;panel2->활성화 = false;)(x == 2) (panel2->BackgroundImage = panel10->BackgroundImage;panel2->활성화 = false;)(x == 1) (panel3->BackgroundImage = panel11->BackgroundImage ;panel3->활성화 = false;)(x == 2) (panel3->BackgroundImage = panel10->BackgroundImage;panel3->활성화 = false;)(x == 1) (panel4->BackgroundImage = panel11->BackgroundImage ;panel4->활성화 = false;)(x == 2) (panel4->BackgroundImage = panel10->BackgroundImage;panel4->활성화 = false;)(x == 1) (panel5->BackgroundImage = panel11->BackgroundImage ;panel5->활성화 = false;)(x == 2) (panel5->BackgroundImage = panel10->BackgroundImage;panel5->활성화 = false;)(x == 1) (panel6->BackgroundImage = panel11->BackgroundImage ;panel6->활성화 = false;)(x == 2) (panel6->BackgroundImage = panel10->BackgroundImage;panel6->활성화 = false;)(x == 1) (panel7->BackgroundImage = panel11->BackgroundImage ;panel7->활성화 = false;)(x == 2) (panel7->BackgroundImage = panel10->BackgroundImage;panel7->활성화 = false;)(x == 1) (panel8->BackgroundImage = panel11->BackgroundImage ;panel8->활성화 = false;)(x == 2) (panel8->BackgroundImage = panel10->BackgroundImage;panel8->활성화 = false;)(x == 1) (panel9->BackgroundImage = panel11->BackgroundImage ;panel9->활성화 = false;)(x == 2) (panel9->BackgroundImage = panel10->BackgroundImage;panel9->활성화 = false;)

): bool Winner())(// 승자를 확인하고 나머지 모든 셀을 차단합니다.

//bool 플래그 = false;(((x == x)&&(x == x)&&(x == 2)) || ((x == x)&&(x == x)&&(x = = 2)) || ((x == x)&&(x == x)&&(x == 2)) || ((x == x)&&(x == x)&&(x == 2 )) || ((x == x)&&(x == x)&&(x == 2)) || ((x == x)&&(x == x)&&(x == 2)) || ((x == x)&&(x == x)&&(x == 2)) || ((x == x)&&(x == x)&&(x == 2)))( (lvl==1) ( picturePo->Visible = true;)(picturePr->Visible = true;)->활성화 = false;->활성화 = false;->활성화 = false;->활성화 = false;-> 활성화 = false;->활성화 = false;->활성화 = false;->활성화 = false;->활성화 = false;true;

)(((x == x)&&(x == x)&&(x == 1)) || ((x == x)&&(x == x)&&(x == 1)) || ((x == x)&&(x == x)&&(x == 1)) || ((x == x)&&(x == x)&&(x == 1)) || (( x == x)&&(x == x)&&(x == 1)) || ((x == x)&&(x == x)&&(x == 1)) || ((x = = x)&&(x == x)&&(x == 1)) || ((x == x)&&(x == x)&&(x == 1)))((lvl==1) (picturePx->Visible = true;)(picturePobeda->Visible = true;)->활성화 = false;->활성화 = false;->활성화 = false;->활성화 = false;->활성화 = false;-> 활성화 = false;->활성화 = false;->활성화 = false;->활성화 = false;true;

): void _friend())(fr = true;(int i = 0; i< 9; i++) if (x[i] == 0) {fr = false; break;}(fr) { pictureN->보이는 = 사실;)

): void move(int n)(// 컴퓨터 이동 함수= false;[n] = 1;= !player;();();(winner()) () ((int i = 0; i< 9; i++) if (x[i] == 0) flag = true;(flag){(lvl == 2) = 2; = 2;= !player;();();();

): System::Void 버튼1_Click(System::Object^ sender, System::EventArgs^ e) (// 새 게임>Visible = false;>Visible = false;>Visible = false; >Visible = false; >Visible = false; = 콤보Box1->Text;(typeGame == "")(::Show("먼저 게임 유형을 선택하세요!");

) else ((typeGame == "X 대 0") lvl = 1;(typeGame == "컴퓨터를 사용하는 첫 번째 레벨") lvl = 2;(typeGame == "컴퓨터를 사용하는 두 번째 레벨") lvl = 3;( ); = true;(int i = 0; i< 9; i++) x[i] = 0;->BackgroundImage = panel12->BackgroundImage;->BackgroundImage = panel12->BackgroundImage;->BackgroundImage = panel12->BackgroundImage;->BackgroundImage = panel12->BackgroundImage;->BackgroundImage = panel12->BackgroundImage;->BackgroundImage = panel12-> BackgroundImage;->BackgroundImage = panel12->BackgroundImage;->BackgroundImage = panel12->BackgroundImage;->BackgroundImage = panel12->BackgroundImage;->활성화 = true;->활성화 = true;->활성화 = true;->활성화 = 참;->활성화 = 참;->활성화 = 참;->활성화 = 참;->활성화 = 참;->활성화 = 참;

): System::Void panel1_MouseClick(System::Object^ 보낸 사람, System::Windows::Forms::MouseEventArgs^ e) (n = 0;(lvl == 1)((player)( = 1;

)= !player;();();();

): System::Void panel2_MouseClick(System::Object^ 보낸 사람, System::Windows::Forms::MouseEventArgs^ e) (n = 1;(lvl == 1)((player)( = 1;

)= !player;();();();

) else if ((lvl == 2)||(lvl == 3))((n);

): System::Void panel3_MouseClick(System::Object^ 보낸 사람, System::Windows::Forms::MouseEventArgs^ e) (n = 2;(lvl == 1)((player)( = 1;

)= !player;();();();

) else if ((lvl == 2)||(lvl == 3))((n);

): System::Void panel4_MouseClick(System::Object^ 보낸 사람, System::Windows::Forms::MouseEventArgs^ e) (n = 3;(lvl == 1)((player)( = 1;

)= !player;();();();

) else if ((lvl == 2)||(lvl == 3))((n);

): System::Void panel5_MouseClick(System::Object^ 보낸 사람, System::Windows::Forms::MouseEventArgs^ e) (n = 4;(lvl == 1)((player)( = 1;

)= !player;();();();

) else if ((lvl == 2)||(lvl == 3))((n);

): System::Void panel6_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) (n = 5;(lvl == 1) ((player)( = 1;

)= !player;();();();

) else if ((lvl == 2)||(lvl == 3))((n);

): System::Void panel7_MouseClick(System::Object^ 보낸 사람, System::Windows::Forms::MouseEventArgs^ e) (n = 6;(lvl == 1) ((player)( = 1;

)= !player;();();();

) else if ((lvl == 2)||(lvl == 3))((n);

): System::Void panel8_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) (n = 7;(lvl == 1) ((player)( = 1;

)= !player;();();();

) else if ((lvl == 2)||(lvl == 3))((n);

): System::Void panel9_MouseClick(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) (n = 8;(lvl == 1) ((player)( = 1;

)= !player;();();();

) else if ((lvl == 2)||(lvl == 3))((n);

): System::Void 버튼2_Click(System::Object^ sender, System::EventArgs^ e) (();

): 시스템::Void picturePx_Click(System::Object^ 보낸 사람, System::EventArgs^ e) (>Visible = false;

): System::Void picturePo_Click(System::Object^ sender, System::EventArgs^ e) (>Visible = false;

): System::Void picturePobeda_Click(System::Object^ sender, System::EventArgs^ e) (>Visible = false;

): System::Void picturePr_Click(System::Object^ sender, System::EventArgs^ e) (>Visible = false;

): System::Void pictureN_Click(System::Object^ sender, System::EventArgs^ e) (>Visible = false;

주목! 이것은 수업의 입문 버전이며 자료가 불완전할 수 있습니다.

학생으로 사이트에 로그인하세요

학교 자료에 접근하려면 학생으로 로그인하세요.

1C 구성 만들기: "Tic Tac Toe" 파트 1/3 작성

우리는 놀이를 통해 배울 것이므로 첫 번째 프로젝트는 모두를 위한 창작이 될 것입니다.
어린 시절부터 친숙한 게임- "Tic Tac Toe".

게임이 1C, 회계 및 거래와 어떤 관련이 있는지 물어볼 수 있습니다. 거의 없음. 그러나 우리는 점진적으로 시작해야 하며 시간이 지나면 창고 자동화에 도달하게 될 것입니다. 지금은 작게 시작해 보겠습니다.

Tic-Tac-Toe 게임 프로그래밍을 시작하기 전에 그것에 대해 생각해 봅시다.

우리는 이미 양식에 요소가 있고 그 중 하나가 버튼이라는 것을 알고 있습니다. 버튼은 명령을 실행할 수 있는 동시에 양식(예: 제목)의 표시를 제어할 수 있는 속성을 가지고 있습니다.

예를 들어, 버튼을 사용하여 9개의 활성 영역(클릭하여 작업을 기록하는 동시에 "O" 및 "X" 형태로 비문을 표시하는 셀)이 있는 필드를 생성할 수 있습니다. 버튼이 이에 적합합니다.

우리는 무엇이 필요한가? 분명히 우리는 우리의 움직임을 기억하고 컴퓨터의 움직임을 기억해야 할 것입니다. 버튼 제목도 변경해야 합니다. 클릭하면 버튼 제목이 항상 "O"이고, 컴퓨터가 움직일 때는 "X"입니다.

먼저, 게임을 생성할 새 데이터베이스를 만들어야 합니다. 이렇게 해보자.

1단계: 빈 데이터베이스 만들기

빈 Tic-Tac-Toe 데이터베이스를 만들어 보겠습니다.

자세한 지침

발사하자 1C 컴퓨터에서 사용 가능한 정보베이스 목록을 여는 바로가기. 귀하는 강의의 평가판을 읽고 계십니다. 전체 강의를 보실 수 있습니다. 우리에겐 창조가 필요하다 새로운 기지, 버튼을 누르세요 " 추가하다":

추가창이 열립니다 정보 기반, 여기서 첫 번째 항목 "을 선택해야 합니다. 정보 기반의 생성"를 선택하고 "다음" 버튼을 클릭합니다.

다음 창에서 두 번째 항목 "을 선택하십시오. 새로운 구성을 개발하기 위해 구성 없이 정보 기반을 생성합니다..."를 클릭하고 "다음" 버튼을 다시 클릭합니다.

다음 창에서는 데이터베이스 목록에 표시될 새 데이터베이스의 이름을 입력하라는 메시지가 표시됩니다. "를 입력해보자 틱택토"를 선택하고 "다음" 버튼을 클릭합니다.

다음 창에서는 데이터베이스가 저장될 빈 폴더의 경로를 지정해야 합니다. 이 경우 "라는 폴더를 만들었습니다. 틱택토" D: 드라이브의 "1C Databases" 폴더에 있습니다.

다음 창에서 모든 설정을 기본값으로 두고 " 준비가 된":

잠시 후 데이터베이스가 생성되어 목록에 추가되었습니다. 데이터베이스 작업에는 두 가지 주요 모드가 있습니다. 1C:기업그리고 구성자:

구성자 모드에서는 데이터베이스를 구성하고 프로그래밍하고, 1C:Enterprise 모드에서는 그 결과를 확인합니다.

2단계: 구성 프로그램 열기

버튼을 누르자 " 구성자"를 입력하여 구성 모드로 들어갑니다.

3단계: 구성 트리 열기

메뉴 명령 "을 실행하십시오. 구성"->"구성 열기":

다양한 구성 섹션이 포함된 구성 트리가 우리 앞에 열립니다. 아직 아무것도 생성하지 않았으므로 다음 섹션은 비어 있습니다.

4단계: 처리 추가

게임의 논리를 배치하기 위해 "처리" 섹션을 사용합니다. 클릭해보자 마우스 오른쪽 버튼으로 클릭섹션 " 트리트먼트"를 선택하고 "추가" 명령을 선택합니다.

새로운 처리를 생성하기 위한 창이 우리 앞에 열렸습니다. 이름을 입력하자 " 틱택토". 동의어는 자체적으로 삽입됩니다. 이는 데이터베이스에 처리(여전히 비어 있음)를 저장하기에 충분합니다. "닫기" 버튼을 클릭합니다.

5단계: 프로그램의 첫 번째 디버깅

사용자 모드( 1C:기업). 구성자에서 직접 들어가려면 메뉴 명령 "을 실행하십시오. 디버깅"->"디버깅 시작":

데이터베이스를 변경했으므로 이 변경 사항을 수락하는 데 동의하는지 묻는 메시지가 표시됩니다. 우리는 개발 과정에서 끊임없이 이 질문을 받게 됩니다. 동의합니다(버튼 " "):

데이터베이스가 "1C:Enterprise" 모드에서 시작되었습니다. 귀하는 강의의 평가판을 읽고 계십니다. 전체 강의를 보실 수 있습니다. 그러나 우리가 볼 수 있듯이 작업하는 것은 여전히 ​​​​어렵습니다. 선택할 것이 없습니다. 이상합니다. 이미 처리를 생성했고 이론적으로는 노란색 패널에 나타나야 하기 때문입니다.




맨 위