如何解决是否有可能对这款 5*5 井字游戏进行暴力破解?
所以我编造了这个游戏,但我找不到一个总是获胜的好策略。
它与original 3×3 Tic-Tac-Toe非常相似;但有变化。
所以你画了一个 5×5 的板。然后每个玩家轮流放置一个十字架和圆圈。然后我们计算“分数”。
This is a completed game 我画的。得分是通过计算每 3 次连续击球来计算的。因此,例如在第一行,交叉玩家获得 1 分。
然后,您可以水平、垂直和双向对角进行计数;对于每一行、每一列和对角线。
对于 4-in-a-row,您会得到 2 分,因为您可以将其视为 2 个不同的 3-in-row。同样,5 连胜将获得 3 分。
在示例游戏中,cross 获胜,因为它得到 9,而 circle 只得到 7。
我经常玩这个;但总是很难说出下一步该往哪里走。我注意到先开始给你一个显着的优势。为了弥补这一点,我将最先开始的玩家的分数降低了一个。
是否有可能强制计算机学习每场比赛的最佳动作?
提前致谢!
旁注:如果有人可以将其编程为具有随机计算机移动的简单游戏,那就太好了。我刚刚开始编程,我很难弄清楚如何去做。
解决方法
蛮力
下面的代码片段将暴力破解玩家 X 和玩家 O 的所有最终游戏
有 33,542,145 个排列可供测试,其中约 5,000,000 个是有效的最终游戏。为了防止页面锁定,它将任务分成 250,000 个排列的组
X 的最佳得分为 16,O 的最佳得分为 14。运行代码段以查看示例最终游戏布局和其他详细信息。
我没有包括任何障碍。
这只能做最终游戏,因为它使用位域来保持高性能。然而,位域没有空板位置的空间。
可以通过反转每个游戏排列的位来优化同时玩两个玩家
const tag = (tag,props = {}) => Object.assign(document.createElement(tag),props);
const append = (par,...sibs) => sibs.reduce((p,sib) => (p.appendChild(sib),p),par);
const log = (el,data,add = true) => add ? append(el,tag("div",{textContent: data.toString()})) : el.textContent = data.toString();
const scores = {
[0b00111] : 1,[0b01110] : 1,[0b11100] : 1,[0b11101] : 1,[0b10111] : 1,[0b01111] : 2,[0b11110] : 2,[0b11111] : 3,};
const rotateMat = [
0,5,10,15,20,1,6,11,16,21,2,7,12,17,22,3,8,13,18,23,4,9,14,19,24
];
const diagonMat = [
2,-1,24,];
const diagonMatUp = [
10,];
function transform(board,matrix) {
var i = 25,b = 0;
while (i--) { matrix[i] > -1 && (board & (1 << matrix[i])) && (b |= 1 << i) }
return b;
}
function scoreLines(board) {
var i = 5,score = 0,l = 0;
while (i--) { score += scores[(board >> (i * 5)) & 0b11111] ?? 0 }
return score;
}
function score(board) {
return scoreLines(board) +
scoreLines(transform(board,rotateMat)) +
scoreLines(transform(board,diagonMat)) +
scoreLines(transform(board,diagonMatUp));
}
function isValidGame(board,side) {
var c = 0,i = 25,bits = side + 1;
while (i-- && c < bits) { (board & (1 << i)) && c++ }
return c === side;
}
function showBoard(board,score,side) {
var i = 5;
log(games,"------------------------");
log(games,"End game score: " + score + " player: " + (side===13 ? "X" : "O"));
log(games,"Example end game");
while (i--) {
const line = ((board >> (i * 5)) & 0b11111).toString(2).padStart(5,"0");
const lined = side === 13 ?
line.replace(/1/g,"X").replace(/0/g,"O") :
line.replace(/1/g,"O").replace(/0/g,"X");
log(games,lined);
}
}
function brute(side = 13) {
function doSet(i) {
var ii = 251357;
while (i >= min && ii--) {
if (isValidGame(i,side)) {
gameCount ++;
const s = score(i);
if (s >= maxScore) {
if (s > maxScore) {
bestEndGames.length = 0
game = i;
}
maxScore = s;
bestEndGames.push(i);
}
}
i--;
}
if (i >= min) {
setTimeout(doSet,i);
log(progress,status + " is: " + maxScore + " tested: " + ((max - min) - (i - min)) + " of " + (max - min) + " permutations",false);
} else {
log(games,status + " is: " + maxScore + " of " + gameCount + " end games");
log(games,"Number of end games with best score: " + bestEndGames.length);
showBoard(game,maxScore,side);
if (side === 13) { setTimeout(brute,1000,12) }
else { log(progress,"Done",false) }
}
}
var game,gameCount = 0;
var maxScore = 0;
const bestEndGames = [];
const status = "Best score player: " + (side===13 ? "X" : "O");
const [min,max] = side === 13 ? [0b1111111111111,0b1111111111111000000000000] : [0b111111111111,0b1111111111110000000000000];
doSet(max)
}
brute(13);
<div id="progress"></div>
<div id="games"></div>
要为游戏得分,下一个片段将这样做。有点黑客,在输入字段中输入游戏并会吐出分数。
const tag = (tag,side,asStr) {
var c = 0,bits = side + 1;
while (i-- && c < bits) { (
(asStr[24-i] !== "-" && asStr[24-i] !== " ") && board & (1 << i)) && c++
}
return [c <= side,c];
}
function showBoard(board,asStr) {
var i = 0;
var j = 0;
while (i < 5) {
const line = ((board >> ((4-i) * 5)) & 0b11111).toString(2).padStart(5,"0");
const lined = line.replace(/1/g,"O");
var str = "";
j = 0;
while (j < 5) {
str += (asStr[i * 5 + j] !== "-" && asStr[i * 5 + j] !== " ") ? lined[j] : ".";
j++;
}
log(games,str);
i++;
}
}
gameVal1.addEventListener("input",testGame);
gameVal2.addEventListener("input",testGame);
gameVal3.addEventListener("input",testGame);
gameVal4.addEventListener("input",testGame);
gameVal5.addEventListener("input",testGame);
function testGame() {
var board = gameVal1.value.slice(0,5).padEnd(5,"-");
board += gameVal2.value.slice(0,"-");
board += gameVal3.value.slice(0,"-");
board += gameVal4.value.slice(0,"-");
board += gameVal5.value.slice(0,"-");
board = board.replace(/[^OX\- ]/gi,"-");
const X = eval("0B" + board.replace(/X/gi,"1").replace(/O|-| /gi,"0"));
const O = eval("0B" + board.replace(/X|-| /gi,"0").replace(/O/gi,"1"));
const [vx,movesX] = isValidGame(X,board);
const [vo,movesY] = isValidGame(O,board);
vx && log(resultX,"Player X score: " + score(X) + " moves: " + movesX,false);
vo && log(resultO,"Player O score: " + score(O) + " moves: " + movesY,false);
games.innerHTML = "";
showBoard(X,board);
}
testGame()
input {
font-family: monospace;
font-size: large;
}
.fixFont {
font-family: monospace;
font-size: large;
}
#games {
position: absolute;
top: 28px;
left: 80px;
border: 1px solid black;
padding: 0px 3px;
letter-spacing: 3px;
}
#resultX {
position: absolute;
top: 40px;
left: 160px;
}
#resultO {
position: absolute;
top: 60px;
left: 160px;
}
<div class="fixFont">
Enter "x" "x" "O" or "o". Empty slots "-" or space<br>
<input type="text" id="gameVal1" value="XXXXX" size="5"><br>
<input type="text" id="gameVal2" value="XXXXO" size="5"><br>
<input type="text" id="gameVal3" value="XXXXO" size="5"><br>
<input type="text" id="gameVal4" value="OOOOO" size="5"><br>
<input type="text" id="gameVal5" value="OOOOO" size="5"><br>
<div id="resultX"></div>
<div id="resultO"></div>
<div id="games"></div>
</div>
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。