我正在尝试实施一个单人游戏,在这个游戏中,计算机玩家永远不会失败(每次强制平局或获胜)。在搜索之后似乎使用minimax策略是相当标准的,但我似乎无法使我的算法正常工作,这导致计算机选择了一个非常糟糕的举动。任何人都可以帮我找到我的代码出错的地方吗?
'O'是用户(最大),'X'是计算机(最小)
编辑:我重新发布了我的代码,我修复了getComputerNextMove中的一些内容以及一些小错误...现在我的代码找到了更好的分数,但如果有人获胜,它并不总是有用(功能似乎没问题,我想我只是没有检查正确的位置)。它也没有采取最佳举措。底部有一些测试人员功能可以查看minimax如何工作/测试游戏状态(获胜者/抽奖)。
tictactoe.js:
// Setup
var user = new Player('user', 'O', 'green');
var computer = new Player('computer', 'X', 'pink');
window.players = [user, computer];
var gameBoard = new GameBoard();
gameBoard.initializeBoard();
// Trackers for the board and moves
var wins = ['012', '345', '678', '036', '147', '258', '048', '246'];
var moves = 1;
var currentPlayer = players[0];
var game = true;
function Player(name, selector, color) {
this.name = name;
this.moves = [];
this.selector = selector;
this.color = color;
this.makeMove = function(boxId){
if(!gameBoard.isGameOver() && gameBoard.isValidMove(boxId)){
markBox(this, boxId);
this.updateMoves(boxId);
gameBoard.updateBoard(this.selector, boxId);
setTimeout(function () { nextTurn() }, 75);
}
else{
if(gameBoard.isXWinner())
endGame("Computer");
else if(gameBoard.isOWinner())
endGame("You");
else
endGame();
}
}
this.updateMoves = function(boxId){
moves = moves + 1;
this.moves.push(boxId);
}
}
function Square(mark){
this.mark = mark;
this.position;
this.id;
this.score = 0; //for minimax algorithm
}
function GameBoard(){
this.state = [9];
this.initializeBoard = function(){
for(var space = 0; space < 9; space++){
var square = new Square("-");
this.state[space] = square;
this.state[space].position = space;
this.state[space].id = space;
this.state[space].score = 0;
}
}
this.getAvailableSpaces = function(){
var availableSpaces = [];
for(var space = 0; space < this.state.length; space++){
if(this.state[space].mark === "-")
availableSpaces.push(this.state[space]);
}
return availableSpaces;
}
this.updateBoard = function(selector, position){
this.state[position].mark = selector;
}
this.getPositionOfMarks = function(){
var positionOfMarks = [];
for(var sqr=0; sqr<this.state.length; sqr++){
var positionOfMark = {
'position' : this.state[sqr].position,
'mark' : this.state[sqr].mark
}
positionOfMarks.push(positionOfMark);
}
return positionOfMarks;
}
this.getXMovesAsString = function(){
var positionOfMarks = this.getPositionOfMarks();
var xMoves = "";
for(var pm=0; pm < positionOfMarks.length; pm++){
if(positionOfMarks[pm].mark === "X"){
var m = parseInt(positionOfMarks[pm].position);
xMoves += m;
}
}
return xMoves;
}
this.getOMovesAsString = function(){
var positionOfMarks = this.getPositionOfMarks();
var oMoves = "";
for(var pm=0; pm < positionOfMarks.length; pm++){
if(positionOfMarks[pm].mark === "O"){
var m = parseInt(positionOfMarks[pm].position);
oMoves += m;
}
}
return oMoves;
}
this.isValidMove = function(boxId){
return this.state[boxId].mark === "-"
}
this.isOWinner = function(){
var winner = false;
var oMoves = this.getOMovesAsString();
for(var win=0; win<wins.length; win++){
if(oMoves.search(wins[win]) >= 0)
winner = true
}
return winner;
}
this.isXWinner = function(){
var winner = false;
var xMoves = this.getXMovesAsString();
for(var win=0; win<wins.length; win++){
if(xMoves.search(wins[win]) >= 0)
winner = true;
}
return winner;
}
this.isDraw = function(){
var draw = true;
if(!this.isOWinner() && !this.isXWinner()){
for(var space=0; space < this.state.length; space++){
if(this.state[space].mark === "-"){
draw = false;
}
}
}
return draw;
}
this.isGameOver = function(){
var gameOver = false;
if(gameBoard.isDraw() ||
gameBoard.isOWinner() ||
gameBoard.isXWinner())
gameOver = true;
return gameOver;
}
this.printBoardToConsole = function(){
var row1 = [];
var row2 = [];
var row3 = [];
for(var space=0; space<this.state.length; space++){
if(space < 3)
row1.push((this.state[space].mark));
else if(space >= 3 && space < 6)
row2.push((this.state[space].mark));
else
row3.push((this.state[space].mark));
}
console.log(row1);
console.log(row2);
console.log(row3);
}
GameBoard.prototype.clone = function(){
var clone = new GameBoard();
clone.initializeBoard();
for(var square=0; square < this.state.length; square++){
clone.state[square].mark = this.state[square].mark;
clone.state[square].position = this.state[square].position;
clone.state[square].id = this.state[square].id;
clone.state[square].score = this.state[square].score;
}
return clone;
}
}
// Handle clicks
$(".box").click(function (event) {
if (getCurrentPlayer() === user && game == true){
user.makeMove(event.target.id);
}
else overlay("Wait your turn!");
});
// *** MOVES ***
// With every move, these functions update the state of the board
function getComputerNextMove(board){
var availableSquares = board.getAvailableSpaces();
var prevScore = -100000;
var bestMove;
for(var square = 0; square < availableSquares.length; square++){
var gameBoardChild = board.clone();
gameBoardChild.updateBoard("X", availableSquares[square].position);
console.log("gameBoardChild: " + gameBoardChild.printBoardToConsole());
console.log("===================");
var currentScore = min(gameBoardChild);
console.log("Child Score: " + currentScore);
if(currentScore > prevScore){
prevScore = currentScore;
bestMove = availableSquares[square].position;
console.log("Best Move: " + bestMove);
}
}
console.log("====================================");
return bestMove;
}
function max(board){
if(board.isOWinner()) return 1;
else if(board.isXWinner()) return -1;
else if(board.isDraw()) return 0;
var availableSquares = board.getAvailableSpaces();
var bestScore = -100000;
for(var square=0; square<availableSquares.length; square++){
var gameBoardChild = board.clone();
gameBoardChild.updateBoard("O", availableSquares[square].position);
var move = min(gameBoardChild);
if(move > bestScore)
bestScore = move;
}
return bestScore;
}
function min(board){
if(board.isOWinner()) return 1;
else if(board.isXWinner()) return -1;
else if(board.isDraw()) return 0;
var availableSquares = board.getAvailableSpaces();
var bestScore = 100000;
for(var square=0; square<availableSquares.length; square++){
var gameBoardChild = board.clone();
gameBoardChild.updateBoard("X", availableSquares[square].position);
var move = max(gameBoardChild);
if(move < bestScore)
bestScore = move;
}
return bestScore;
}
function markBox(player, boxId) {
$("#" + boxId).text(player.selector).addClass(player.color);
}
// *** SETTERS & GETTERS
function getCurrentPlayer() {
return currentPlayer;
}
function setCurrentPlayer(player) {
currentPlayer = player;
return currentPlayer;
}
function nextTurn() {
if(!gameBoard.isGameOver()){
if (getCurrentPlayer() === user) {
setCurrentPlayer(computer);
computer.makeMove(getComputerNextMove(gameBoard));
}
else setCurrentPlayer(user);
}
else{
if(gameBoard.isOWinner())
endGame("You");
else if(gameBoard.isXWinner())
endGame("Computer");
else
endGame();
}
}
function endGame(winner) {
game = false;
if (winner) {
overlay(winner.name + " wins!");
} else overlay("It's a draw!");
}
//Toggles info box (to avoid using alerts)
function overlay(message) {
$("#info").text(message);
$el = $("#overlay");
$el.toggle();
}
function buildTestBoard(test){
var board = new GameBoard();
board.initializeBoard();
if(test === "minimax"){
board.updateBoard("O", 0);
board.updateBoard("O", 2);
board.updateBoard("O", 4);
board.updateBoard("O", 8);
board.updateBoard("X", 1);
board.updateBoard("X", 6);
board.updateBoard("X", 7);
}
else if(test === "xwin"){
board.updateBoard("X", 6);
board.updateBoard("X", 4);
board.updateBoard("X", 2);
}
board.printBoardToConsole();
return board;
}
function testMinimax(){
var board = buildTestBoard("minimax");
var move = getComputerNextMove(board);
console.log("Best move: " + move);
}
function testWinner(){
var board = buildTestBoard("xwin");
return board.isXWinner();
}
的index.html:
<!DOCTYPE html>
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
<!-- Place favicon.ico and apple-touch-icon.png in the root directory -->
<link rel="stylesheet" href="css/normalize.css">
<link rel="stylesheet" href="css/main.css">
<script src="js/vendor/modernizr-2.6.2.min.js"></script>
</head>
<body>
<!--[if lt IE 7]>
<p class="chromeframe">You are using an <strong>outdated</strong> browser. Please
<a href="http://browsehappy.com/">upgrade your browser</a> or
<a href="http://www.google.com/chromeframe/?redirect=true">activate Google Chrome Frame</a>
to improve your experience.
</p>
<![endif]-->
<h2 id="title">Single Player Tic Tac Toe</h2>
<div id='0' class='box'></div>
<div id='1' class='box'></div>
<div id='2' class='box'></div>
<br />
<div id='3' class='box'></div>
<div id='4' class='box'></div>
<div id='5' class='box'></div>
<br />
<div id='6' class='box'></div>
<div id='7' class='box'></div>
<div id='8' class='box'></div>
<div id="overlay">
<div>
<p id="info"></p>
</div>
</div>
<!-- Google Analytics: change UA-XXXXX-X to be your site's ID. -->
<script>
var _gaq=[['_setAccount','UA-XXXXX-X'],['_trackPageview']];
(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];
g.src='//www.google-analytics.com/ga.js';
s.parentNode.insertBefore(g,s)}(document,'script'));
</script>
</body>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="js/vendor/jquery-1.9.1.min.js"><\/script>')</script>
<script src="js/plugins.js"></script>
<script src="js/main.js"></script>
<script src="js/game/tictactoe.js"></script>
</html>
的CSS:
.box {
width:100px;
height:100px;
display:inline-block;
background:#eee;
margin:3px 1px;
text-align:center;
font-size:24px;
font-family:arial;
vertical-align:bottom;
line-height:450%
}
.box:hover {
cursor: pointer;
}
a {
text-decoration:none;
}
.label {
position:relative;
bottom:0;
right:0
}
.green {
background:#90EE90;
}
.pink {
background:#FDCBBB;
}
.credit {
color:#333;
font-family:arial;
font-size:13px;
margin-top:40px;
}
#overlay {
position: absolute;
left: 300px;
top: 0px;
width:20%;
height:20%;
text-align:center;
z-index: 1000;
}
#overlay div {
width:100px;
margin: 100px auto;
background-color: #fff;
border:1px solid #000;
padding:15px;
text-align:center;
align: right;
}