GPT5来了,编程能力强的一批,小白也能编程了的感觉。下面以中国象棋游戏,人机对弈为例,来展示一下过程,我使用的dify工作流来对接的GPT5。





完整代码:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>中国象棋人机对弈(α-β 搜索)</title>
<style>
body { text-align:center; }
canvas { background-color: #f8d796; margin-top:20px; }
</style>
</head>
<body>
<h2>中国象棋人机对弈(完整规则 + α-β 搜索 AI)</h2>
<canvas id="board" width="540" height="600"></canvas>
<script>
const canvas=document.getElementById('board');
const ctx=canvas.getContext('2d');
const gridSize=60; const cols=9, rows=10;
// 初始棋局
let pieces=[
{x:0,y:0,s:'b',t:'車'},{x:1,y:0,s:'b',t:'馬'},{x:2,y:0,s:'b',t:'象'},
{x:3,y:0,s:'b',t:'士'},{x:4,y:0,s:'b',t:'將'},{x:5,y:0,s:'b',t:'士'},
{x:6,y:0,s:'b',t:'象'},{x:7,y:0,s:'b',t:'馬'},{x:8,y:0,s:'b',t:'車'},
{x:1,y:2,s:'b',t:'炮'},{x:7,y:2,s:'b',t:'炮'},
{x:0,y:3,s:'b',t:'卒'},{x:2,y:3,s:'b',t:'卒'},{x:4,y:3,s:'b',t:'卒'},
{x:6,y:3,s:'b',t:'卒'},{x:8,y:3,s:'b',t:'卒'},
{x:0,y:9,s:'r',t:'車'},{x:1,y:9,s:'r',t:'馬'},{x:2,y:9,s:'r',t:'相'},
{x:3,y:9,s:'r',t:'仕'},{x:4,y:9,s:'r',t:'帥'},{x:5,y:9,s:'r',t:'仕'},
{x:6,y:9,s:'r',t:'相'},{x:7,y:9,s:'r',t:'馬'},{x:8,y:9,s:'r',t:'車'},
{x:1,y:7,s:'r',t:'炮'},{x:7,y:7,s:'r',t:'炮'},
{x:0,y:6,s:'r',t:'兵'},{x:2,y:6,s:'r',t:'兵'},{x:4,y:6,s:'r',t:'兵'},
{x:6,y:6,s:'r',t:'兵'},{x:8,y:6,s:'r',t:'兵'},
];
let sel=null, turn='r';
function pieceAt(x,y){ return pieces.find(p=>p.x===x && p.y===y); }
function drawBoard(){
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.strokeStyle='#000'; ctx.lineWidth=1;
// 横线
for(let i=0;i<rows;i++){
ctx.beginPath();
ctx.moveTo(0.5,0.5+i*gridSize);
ctx.lineTo((cols-1)*gridSize+0.5,0.5+i*gridSize);
ctx.stroke();
}
// 竖线
for(let i=0;i<cols;i++){
ctx.beginPath();
ctx.moveTo(0.5+i*gridSize,0.5);
if(i===0||i===cols-1){
ctx.lineTo(0.5+i*gridSize,(rows-1)*gridSize+0.5);
} else {
ctx.lineTo(0.5+i*gridSize,(rows/2-1)*gridSize+0.5);
ctx.moveTo(0.5+i*gridSize,(rows/2)*gridSize+0.5);
ctx.lineTo(0.5+i*gridSize,(rows-1)*gridSize+0.5);
}
ctx.stroke();
}
// 九宫斜线
ctx.beginPath();
ctx.moveTo(3*gridSize+0.5,0.5);
ctx.lineTo(5*gridSize+0.5,2*gridSize+0.5);
ctx.moveTo(5*gridSize+0.5,0.5);
ctx.lineTo(3*gridSize+0.5,2*gridSize+0.5);
ctx.moveTo(3*gridSize+0.5,(rows-3)*gridSize+0.5);
ctx.lineTo(5*gridSize+0.5,(rows-1)*gridSize+0.5);
ctx.moveTo(5*gridSize+0.5,(rows-3)*gridSize+0.5);
ctx.lineTo(3*gridSize+0.5,(rows-1)*gridSize+0.5);
ctx.stroke();
// 楚河汉界
ctx.font='28px serif';
ctx.fillText('楚河',gridSize*1.5,gridSize*4.5);
ctx.fillText('汉界',gridSize*5.5,gridSize*4.5);
}
function drawPieces(){
pieces.forEach(p=>{
let px=p.x*gridSize, py=p.y*gridSize;
ctx.beginPath();
ctx.fillStyle='#fff';
ctx.arc(px,py,24,0,Math.PI*2);
ctx.fill();
ctx.strokeStyle='#000'; ctx.stroke();
ctx.fillStyle= p.s==='r'?'red':'black';
ctx.font='20px serif';
ctx.textAlign='center';
ctx.textBaseline='middle';
ctx.fillText(p.t,px,py);
if(sel===p){
ctx.strokeStyle='blue';
ctx.lineWidth=2; ctx.stroke();
ctx.lineWidth=1;
}
});
}
// 工具函数
function inPalace(x,y,side){
if(side==='r') return (x>=3&&x<=5&&y>=7&&y<=9);
else return (x>=3&&x<=5&&y>=0&&y<=2);
}
function faceToFace(){
let rGen=pieces.find(p=>p.t==='帥'), bGen=pieces.find(p=>p.t==='將');
if(!rGen || !bGen) return false;
if(rGen.x!==bGen.x) return false;
let minY=Math.min(rGen.y,bGen.y), maxY=Math.max(rGen.y,bGen.y);
for(let y=minY+1;y<maxY;y++){ if(pieceAt(rGen.x,y)) return false; }
return true;
}
// 棋规
function canMove(p,x,y){
if(x<0||x>=cols||y<0||y>=rows) return false;
if(p.x===x && p.y===y) return false;
let target=pieceAt(x,y);
if(target && target.s===p.s) return false;
let dx=x-p.x, dy=y-p.y;
let adx=Math.abs(dx), ady=Math.abs(dy);
switch(p.t){
case '車':case '车':
if(dx!==0 && dy!==0) return false;
if(dx===0){
for(let yy=p.y+Math.sign(dy);yy!==y;yy+=Math.sign(dy)){ if(pieceAt(p.x,yy)) return false; }
} else {
for(let xx=p.x+Math.sign(dx);xx!==x;xx+=Math.sign(dx)){ if(pieceAt(xx,p.y)) return false; }
}
break;
case '炮':
if(dx!==0 && dy!==0) return false;
let count=0;
if(dx===0){
for(let yy=p.y+Math.sign(dy);yy!==y;yy+=Math.sign(dy)){ if(pieceAt(p.x,yy)) count++; }
} else {
for(let xx=p.x+Math.sign(dx);xx!==x;xx+=Math.sign(dx)){ if(pieceAt(xx,p.y)) count++; }
}
if(target) return count===1; else return count===0;
case '馬':case '马':
if(!((adx===2&&ady===1)||(adx===1&&ady===2))) return false;
if(adx===2 && pieceAt(p.x+dx/2, p.y)) return false;
if(ady===2 && pieceAt(p.x, p.y+dy/2)) return false;
break;
case '象':case '相':
if(adx!==2 || ady!==2) return false;
if((p.s==='r' && y<5) || (p.s==='b' && y>4)) return false;
if(pieceAt(p.x+dx/2, p.y+dy/2)) return false;
break;
case '士':case '仕':
if(adx!==1 || ady!==1) return false;
if(!inPalace(x,y,p.s)) return false;
break;
case '將':case '帅':
if(!inPalace(x,y,p.s)) return false;
if(adx+ady!==1) return false;
break;
case '兵':
if(p.s==='r'){
if(y>p.y) return false;
if(p.y>4){ if(adx!==0 || ady!==1) return false; }
else { if(adx+ady!==1) return false; }
} else { // 黑方兵
if(y<p.y) return false;
if(p.y<5){ if(adx!==0 || ady!==1) return false; }
else { if(adx+ady!==1) return false; }
}
break;
case '卒':
if(p.s==='b'){
if(y<p.y) return false;
if(p.y<5){ if(adx!==0 || ady!==1) return false; }
else { if(adx+ady!==1) return false; }
} else { // 红方卒
if(y>p.y) return false;
if(p.y>4){ if(adx!==0 || ady!==1) return false; }
else { if(adx+ady!==1) return false; }
}
break;
}
// 将帅不见面
let backup={x:p.x,y:p.y};
let killed=target?pieces.indexOf(target):-1;
p.x=x; p.y=y;
if(killed>=0) pieces.splice(killed,1);
let illegal=faceToFace();
if(killed>=0) pieces.splice(killed,0,target);
p.x=backup.x; p.y=backup.y;
if(illegal) return false;
return true;
}
// 鼠标点击
function handleClick(e){
if(turn!=='r') return;
let rect=canvas.getBoundingClientRect();
let x=Math.round((e.clientX-rect.left)/gridSize);
let y=Math.round((e.clientY-rect.top)/gridSize);
let p=pieceAt(x,y);
if(sel){
if(canMove(sel,x,y)){
let target=pieceAt(x,y);
if(target) pieces=pieces.filter(pp=>pp!==target);
sel.x=x; sel.y=y;
sel=null; turn='b'; draw();
setTimeout(aiMove,300);
} else if(p && p.s==='r'){ sel=p; }
else sel=null;
} else {
if(p && p.s==='r') sel=p;
}
draw();
}
// 评估:红分-黑分
const values = {
'車':900,'车':900,
'馬':450,'马':450,
'炮':450,
'兵':100,'卒':100,
'相':200,'象':200,
'仕':200,'士':200,
'帥':10000,'將':10000
};
function evaluate(){
let score=0;
pieces.forEach(p=>{
let val = values[p.t] || 0;
if(p.s==='r'){
if(p.t==='兵' && p.y<5) val+=30;
score += val;
} else {
if(p.t==='卒' && p.y>4) val+=30;
score -= val;
}
});
return score;
}
function generateMoves(side){
let moves=[];
pieces.filter(pp=>pp.s===side).forEach(p=>{
for(let x=0;x<cols;x++){
for(let y=0;y<rows;y++){
if(canMove(p,x,y)){
moves.push({p,x,y});
}
}
}
});
return moves;
}
function makeMove(m){
m.killed=pieceAt(m.x,m.y);
if(m.killed) pieces=pieces.filter(pp=>pp!==m.killed);
m.oldX=m.p.x; m.oldY=m.p.y;
m.p.x=m.x; m.p.y=m.y;
}
function undoMove(m){
m.p.x=m.oldX; m.p.y=m.oldY;
if(m.killed) pieces.push(m.killed);
}
function alphaBeta(depth, alpha, beta, maxPlayer){
if(depth===0) return evaluate();
let side=maxPlayer?'b':'r';
let moves=generateMoves(side);
if(moves.length===0) return maxPlayer?-999999:999999;
if(maxPlayer){
let v=-Infinity;
for(let mv of moves){
makeMove(mv);
v=Math.max(v, alphaBeta(depth-1, alpha, beta, false));
undoMove(mv);
alpha=Math.max(alpha, v);
if(alpha>=beta) break;
}
return v;
} else {
let v=Infinity;
for(let mv of moves){
makeMove(mv);
v=Math.min(v, alphaBeta(depth-1, alpha, beta, true));
undoMove(mv);
beta=Math.min(beta, v);
if(beta<=alpha) break;
}
return v;
}
}
function aiMove(){
let depth=2;
let bestScore=-Infinity, bestMove=null;
let moves=generateMoves('b');
for(let mv of moves){
makeMove(mv);
let score=alphaBeta(depth-1,-Infinity,Infinity,false);
undoMove(mv);
if(score>bestScore){
bestScore=score; bestMove=mv;
}
}
if(bestMove) makeMove(bestMove);
turn='r'; draw();
}
// 绘制
function draw(){ drawBoard(); drawPieces(); }
canvas.addEventListener('click',handleClick);
draw();
</script>
</body>
</html>
运行效果:

评价:象棋棋盘画的不完整,可以对弈。小白编程的时代来了。GPT5具有智能体(代表老师)的感觉了,引导你一步一步完成初始设定的目标。

1131

被折叠的 条评论
为什么被折叠?



