Javascript 计算机算法
我一直在做一个简单的井字游戏,结果碰到了一堵砖墙 虽然游戏的大部分功能都已经到位,但我缺乏适当放置计算机磁贴所需的关键算法 我需要一种算法,该算法可以搜索一个3x3网格的磁贴,并搜索计算机磁贴在网格中的最佳位置 对于我如何设计这个算法的任何指导或见解,我都将不胜感激 不完全Tic-Tac-Toe算法:Javascript 计算机算法,javascript,algorithm,math,Javascript,Algorithm,Math,我一直在做一个简单的井字游戏,结果碰到了一堵砖墙 虽然游戏的大部分功能都已经到位,但我缺乏适当放置计算机磁贴所需的关键算法 我需要一种算法,该算法可以搜索一个3x3网格的磁贴,并搜索计算机磁贴在网格中的最佳位置 对于我如何设计这个算法的任何指导或见解,我都将不胜感激 不完全Tic-Tac-Toe算法: function placeComputerTile(el){ if(computerTurn === true && userTurn === false){ var
function placeComputerTile(el){
if(computerTurn === true && userTurn === false){
var tileIsEmpty = true;
// If the selected tile has at least one child,
// do not allow placement of another tile.
if (el.firstChild) {
tileIsEmpty = false;
}
if(tileIsEmpty === true){
cloneComputerIcon();
}
el.appendChild(newComputerIcon);
addClass(el, "x");
newComputerIcon.style.display = null;
}
}
完整Javascript:
var gameIcons = document.getElementsByClassName('gameIcon');
var turnDisplays = document.getElementsByClassName('turnDisplay');
for (var i = 0; i < gameIcons.length; i++) {
gameIcons[i].style.display = 'none';
}
for (var i = 0; i < turnDisplays.length; i++) {
turnDisplays[i].style.display = 'none';
}
var userTurn = true;
var computerTurn = false;
var currentTurn = 1;
var maxTurn = 10;
var userTurnDisplay = document.getElementById("userTurnDisplay");
var computerTurnDisplay = document.getElementById("computerTurnDisplay");
function evaluateTurn(){
currentTurn += 1;
for(var i = 0; i < maxTurn; i++) {
if(currentTurn % 2 === 0){
userTurn = true;
computerTurn = false;
}else if(currentTurn % 2 !== 0){
userTurn = false;
computerTurn = true;
}
}
if(currentTurn === maxTurn){
alert("Draw!");
userTurnDisplay.style.display = "none";
computerTurnDisplay.style.display = "none";
}
//Change display depending on players turn.
if(userTurn === true && currentTurn !== maxTurn) {
computerTurnDisplay.style.display = null;
userTurnDisplay.style.display = "none";
}else if(computerTurn === true && currentTurn !== maxTurn){
userTurnDisplay.style.display = null;
computerTurnDisplay.style.display = "none";
}
}
var cloneUserIcon = function(){
var userIcon = document.getElementById("userIcon");
newUserIcon = userIcon.cloneNode(true);
}
var cloneComputerIcon = function(){
var computerIcon = document.getElementById("computerIcon");
newComputerIcon = computerIcon.cloneNode(true);
}
function addClass(el, className) {
if (el.classList)
el.classList.add(className)
else if (!hasClass(el, className)) el.className += " " + className
}
function placeUserTile(el){
if(userTurn === true && computerTurn === false){
var tileIsEmpty = true;
// If the selected tile has at least one child,
// do not allow placement of another tile.
if (el.firstChild) {
tileIsEmpty = false;
}
if(tileIsEmpty === true){
cloneUserIcon();
}
el.appendChild(newUserIcon);
addClass(el, "o");
newUserIcon.style.display = null;
}
}
///////////////////////////////////////////////////////////////////////////////
// computer move logic //
// //
function placeComputerTile(el){
if(computerTurn === true && userTurn === false){
var tileIsEmpty = true;
// If the selected tile has at least one child,
// do not allow placement of another tile.
if (el.firstChild) {
tileIsEmpty = false;
}
if(tileIsEmpty === true){
cloneComputerIcon();
}
el.appendChild(newComputerIcon);
addClass(el, "x");
newComputerIcon.style.display = null;
}
}
// //
// //
///////////////////////////////////////////////////////////////////////////////
// Search an array of tiles.
function hasTile(tilesArray){
var allHaveChild = tilesArray.length > 0;
for(var i = 0; i < tilesArray.length; i++){
if(!tilesArray[i].firstChild){
allHaveChild = false;
}
}
if(allHaveChild)
return true;
else
return false;
}
function hasClass(element, className) {
return element.className && new RegExp("(^|\\s)" + className + "(\\s|$)").test(element.className);
}
// Row 1 Tiles
const R1C1 = document.getElementById('r1c1');
const R1C2 = document.getElementById('r1c2');
const R1C3 = document.getElementById('r1c3');
//
// // Row 2 Tiles
const R2C1 = document.getElementById('r2c1');
const R2C2 = document.getElementById('r2c2');
const R2C3 = document.getElementById('r2c3');
//
// // Row 3 Tiles
const R3C1 = document.getElementById('r3c1');
const R3C2 = document.getElementById('r3c2');
const R3C3 = document.getElementById('r3c3');
//Set of all row tiles
var rowOneTiles = [R1C1,R1C2,R1C3];
var rowTwoTiles = [R2C1,R2C2,R2C3];
var rowThreeTiles = [R3C1,R3C2,R3C3];
// Set of all column tiles
var columnOneTiles = [R1C1,R2C1,R3C1];
var columnTwoTiles = [R1C2,R2C2,R3C2];
var columnThreeTiles = [R1C3,R2C3,R3C3];
//Set of left-diagonal & right-diagonal tiles
var leftDiagonalTiles = [R1C1,R2C2,R3C3];
var rightDiagonalTiles = [R1C3,R2C2,R3C1];
function checkRow1(){
// If the entire row is filled:
if(hasTile(rowOneTiles)){
var el_1 = rowOneTiles[0];
var el_2 = rowOneTiles[1];
var el_3 = rowOneTiles[2];
if(hasClass(el_1,"x") && hasClass(el_2,"x") && hasClass(el_3,"x")){
alert("Sorry, you've lost.");
}else if(hasClass(el_1,"o") && hasClass(el_2,"o") && hasClass(el_3,"o")){
alert("Congratulations, you've won!");
}
}
}
function checkRow2(){
// If the entire row is filled:
if(hasTile(rowTwoTiles)){
var el_1 = rowTwoTiles[0];
var el_2 = rowTwoTiles[1];
var el_3 = rowTwoTiles[2];
if(hasClass(el_1,"x") && hasClass(el_2,"x") && hasClass(el_3,"x")){
alert("Sorry, you've lost.");
}else if(hasClass(el_1,"o") && hasClass(el_2,"o") && hasClass(el_3,"o")){
alert("Congratulations, you've won!");
}
}
}
function checkRow3(){
// If the entire row is filled:
if(hasTile(rowThreeTiles)){
var el_1 = rowThreeTiles[0];
var el_2 = rowThreeTiles[1];
var el_3 = rowThreeTiles[2];
if(hasClass(el_1,"x") && hasClass(el_2,"x") && hasClass(el_3,"x")){
alert("Sorry, you've lost.");
}else if(hasClass(el_1,"o") && hasClass(el_2,"o") && hasClass(el_3,"o")){
alert("Congratulations, you've won!");
}
}
}
function checkColumn1(){
// If the entire row is filled:
if(hasTile(columnOneTiles)){
var el_1 = columnOneTiles[0];
var el_2 = columnOneTiles[1];
var el_3 = columnOneTiles[2];
if(hasClass(el_1,"x") && hasClass(el_2,"x") && hasClass(el_3,"x")){
alert("Sorry, you've lost.");
}else if(hasClass(el_1,"o") && hasClass(el_2,"o") && hasClass(el_3,"o")){
alert("Congratulations, you've won!");
}
}
}
function checkColumn2(){
// If the entire row is filled:
if(hasTile(columnTwoTiles)){
var el_1 = columnTwoTiles[0];
var el_2 = columnTwoTiles[1];
var el_3 = columnTwoTiles[2];
if(hasClass(el_1,"x") && hasClass(el_2,"x") && hasClass(el_3,"x")){
alert("Sorry, you've lost.");
}else if(hasClass(el_1,"o") && hasClass(el_2,"o") && hasClass(el_3,"o")){
alert("Congratulations, you've won!");
}
}
}
function checkColumn3(){
// If the entire row is filled:
if(hasTile(columnThreeTiles)){
var el_1 = columnThreeTiles[0];
var el_2 = columnThreeTiles[1];
var el_3 = columnThreeTiles[2];
if(hasClass(el_1,"x") && hasClass(el_2,"x") && hasClass(el_3,"x")){
alert("Sorry, you've lost.");
}else if(hasClass(el_1,"o") && hasClass(el_2,"o") && hasClass(el_3,"o")){
alert("Congratulations, you've won!");
}
}
}
function checkLeftDiagonal(){
// If the entire row is filled:
if(hasTile(leftDiagonalTiles)){
var el_1 = leftDiagonalTiles[0];
var el_2 = leftDiagonalTiles[1];
var el_3 = leftDiagonalTiles[2];
if(hasClass(el_1,"x") && hasClass(el_2,"x") && hasClass(el_3,"x")){
alert("Sorry, you've lost.");
}else if(hasClass(el_1,"o") && hasClass(el_2,"o") && hasClass(el_3,"o")){
alert("Congratulations, you've won!");
}
}
}
function checkRightDiagonal(){
// If the entire row is filled:
if(hasTile(rightDiagonalTiles)){
var el_1 = rightDiagonalTiles[0];
var el_2 = rightDiagonalTiles[1];
var el_3 = rightDiagonalTiles[2];
if(hasClass(el_1,"x") && hasClass(el_2,"x") && hasClass(el_3,"x")){
alert("Sorry, you've lost.");
}else if(hasClass(el_1,"o") && hasClass(el_2,"o") && hasClass(el_3,"o")){
alert("Congratulations, you've won!");
}
}
}
function checkForWin(){
checkRow1();
checkRow2();
checkRow3();
checkColumn1();
checkColumn2();
checkColumn3();
checkLeftDiagonal();
checkRightDiagonal();
}
function main(el){
evaluateTurn();
if(userTurn === true){
placeUserTile(el);
}
if(computerTurn === true){
placeComputerTile(el);
}
checkForWin();
}
请查看此链接:
想一想你是如何用一系列if-then语句玩tic-tac-toe的:
“如果我先去,然后在中间放一个X”。
“如果我的对手一行有两个O(行、列或对角线中O的计数之和=2),则在该行中放置一个X”
等等等等
清楚地说出你在玩游戏时使用的规则。如果你不想无聊至死,也不想看到实际的代码,请向下滚动到底部
用于此的典型算法是or,alpha-beta是minimax的优化版本(可能对tac-tic-toe有点过分)。这两种方法基本上都是对你可能得到的位置进行深度优先搜索,在那里你在寻找最好的位置。下面是一个过程的基本示例(下面是一个尝试性的解释):
以下是我对minimax的实现:
/**
*初始化MiniMax(0,player1),其中player1是计算机
*@param depth当前深度或度数
*@param player最大值或最小值
*@return int[]{score,bestRow,bestCol}
*/
int[]miniMax(int深度,int播放器){
//用于放置检查器的可能列的列表
列表移动=下一步移动();
int bestRow=-1;
int-bestCol=-1;
int previousPlayer=(player+1)&1;
//如果达到最大深度或节点是终端节点(游戏结束位置)
//即,如果(当前位置有一个3排| | |您达到最大深度| |电路板已满(绘制))
if(isWin(previousPlayer)| |(depth==this.depth)| | moves.isEmpty()){
返回新的int[]{eval(),bestRow,bestCol};
}
//最佳现值
INTV;
//如果它的最大值(计算机)
如果(玩家==0){
//假设最坏情况下,最大值为-无穷大,
//如果出现更好的值,则用它替换v
v=-inf;
//对于每个可用的移动
对于(int[]move:moves){//move={row,column}
makeMove(move[0],move[1],0);
int分数=极大极小值(深度+1,1)[0];
撤消移动(移动[0],移动[1],0);
//如果得分更好,则更新v
如果(分数>v){
v=分数;
bestRow=移动[0];
bestCol=move[1];
}
}
返回新的int[]{v,bestRow,bestCol};
}
//如果它的最小回合(对手)
否则{
//假设最坏情况下,最小值为无穷大,
//如果出现更好的值,则用它替换v
v=inf;
//对于每个可用的移动
对于(int[]move:moves){//move={row,column}
makeMove(移动[0],移动[1],1);//移动
int分数=极大极小值(深度+1,0)[0];
撤消移动(移动[0],移动[1],1);//撤消移动
//如果得分更好,则更新v
如果(v>分数){
v=分数;
bestRow=移动[0];
bestCol=move[1];
}
}
返回新的int[]{v,bestRow,bestCol};
}
}
对于2名玩家,您为每个玩家分配一个骰子:
1. maximizing player (the computer)
2. minimizing player (the computer's opponent)
最大化玩家
获取可用的可能移动的最大
,类似地最小化玩家
获取可用的可能移动的最小
为了阐明此操作,给定您正在查看的位置(或您可能的一次移动导致的位置),您可以通过一些评估函数或参数为其赋值。例如,如果我在玩X
,并且有以下位置:
=======
|O|X| |
=======
| |X|O|
=======
| |X| |
=======
给出的值应为∞代码>(或一些大数字),因为我已经赢了。但如果我有这个职位:
=======
|O| | |
=======
|O|X| |
=======
|X| |X|
=======
value (position){
if position has a 3 in row return infinity
result = 0
add 1 to result for each 2 in row with an empty space in between
return result
}
虽然X
尚未获胜(船上第三排),但X
肯定会获胜。但是,如果我们想给董事会的当前状态赋值,假设正值表示该位置对X
有利,我们会将其设为正值。为了满足这一点,您可以,例如,假设行中没有3个,计算所有打开的2个行,并将其作为位置值返回:
=======
|O| | |
=======
|O|X| |
=======
|X| |X|
=======
value (position){
if position has a 3 in row return infinity
result = 0
add 1 to result for each 2 in row with an empty space in between
return result
}
因此,对于上面的示例,您将给它一个值2
(底部水平加上向上向右的对角线)。还有其他更有效的方法来执行评估功能,但上述方法已足够。你也可以返回infinity
,如果你赢了,但是你的搜索深度可能很大,这不是tic-tac-toe的问题
下面是一个java类,如果您想尝试,它的底部有main方法(和示例):
import java.util.*;
公共f级{
int inf=100000000;//无穷大
智力深度;
字符计算机='O';
字符对手='X';
/*
[0]=计算机
[1] =对手
计算机和对手的移动以二进制数字存储
其中1表示有一个棋盘格,0表示一个棋盘格
空位
=======
|O | O ||
=======
||X||
=======
||X||
=======
上述董事会的代表如下:
计算机=110000000(计算机为“O”)
对手=000010010(对手为“X”)
*/
int[]游戏板={0B10001000000,0b000000000};
//计算i中的位数
整数计数(整数i){
int n=0;
而(i!=0){
i=i&(i-1);
n++;
}
返回n;
value (position){
if position has a 3 in row return infinity
result = 0
add 1 to result for each 2 in row with an empty space in between
return result
}
initial board:
=======
|O| | |
=======
| |O| |
=======
| | | |
=======
board after computer move:
=======
|O| | |
=======
| |O| |
=======
| | |O|
=======