You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js开发多人井字棋第二个客户端turn变量不更新问题

核心问题

  1. 你的main.js中socket.on('move')回调的参数turn和全局的turn变量重名,回调内修改的是局部参数,全局的回合状态根本没有更新,导致第二个窗口落子时始终读取初始值turn=0,只会放置X。
  2. 所有游戏状态(落子记录、回合信息)仅在操作的客户端本地更新,接收消息的客户端没有同步这些状态,后续的胜负判定也会出错。
  3. 服务端没有存储全局游戏状态,新用户加入或页面刷新后状态会完全不一致。

修改方案

1. 调整server.js,增加全局状态存储和同步逻辑

const express = require('express');
const app = express();
const server = require('http').createServer(app);
const io = require('socket.io')(server, {cors: {origin: "*"}});

app.use(express.static(__dirname + '/public'));

// 服务端全局存储游戏状态
let gameState = {
  turn: 0,
  placed: Array(9).fill(""),
  playerOnePick: [],
  playerTwoPick: []
}

app.get('/', (req, res) => {
  res.sendFile("/home/pi/nodeTacTow/index.html");
})

server.listen(3000, () => {
  console.log("server started at port 3000...")
});

io.on('connection', (socket) => {
  console.log("Welcome MR" + socket.id);
  socket.join("GameRoom");
  // 新连接进来先同步当前游戏状态
  socket.emit('syncState', gameState);

  socket.on('move', (moveOne, turn) => {
    // 服务端更新全局状态
    gameState.turn = turn;
    gameState.placed[moveOne-1] = moveOne;
    if(turn === 1) {
      gameState.playerOnePick.push(moveOne);
    } else {
      gameState.playerTwoPick.push(moveOne);
    }
    console.log(turn);
    socket.broadcast.emit('move', moveOne, turn);
  });

  socket.on('win', (player) => {
    console.log("WINNER IS: " + player);
    // 对局结束重置服务端状态
    gameState = {
      turn: 0,
      placed: Array(9).fill(""),
      playerOnePick: [],
      playerTwoPick: []
    }
  });

  // 同步开局操作
  socket.on('startGame', () => {
    gameState = {
      turn: 0,
      placed: Array(9).fill(""),
      playerOnePick: [],
      playerTwoPick: []
    }
    socket.broadcast.emit('startGame');
  })
})

2. 调整main.js,修复turn更新问题、同步所有游戏状态

var gridOne = document.getElementById('grid-item-one');
var gridTwo = document.getElementById('grid-item-two');
var gridThree = document.getElementById('grid-item-three');
var gridFour = document.getElementById('grid-item-four');
var gridFive = document.getElementById('grid-item-five');
var gridSix = document.getElementById('grid-item-six');
var gridSeven = document.getElementById('grid-item-seven');
var gridEight = document.getElementById('grid-item-eight');
var gridNine = document.getElementById('grid-item-nine');

var gridArray = [gridOne, gridTwo, gridThree, gridFour, gridFive, gridSix, gridSeven, gridEight, gridNine];

const socket = io('http://192.168.10.130:3000/')

var cell = [];

var playerOnePick = [];
var playerTwoPick = [];

var textField = document.getElementById('text-field');

var turn = 0;

var placed = ["", "", "", "", "", "", "", "", ""];

function placeBet(place, numb){
  var player;
  var name;
    
  if(turn === 0 && place.innerHTML === ""){
        
    playerOnePick.push(numb);

    place.innerHTML = '<h1 class="gameX">X</h1>';
    turn = 1;
    textField.innerHTML= "Player 2's turn";
    player = playerOnePick;
    name = "PLAYER ONE";
    
  } else if(turn === 1 && place.innerHTML === ""){
    playerTwoPick.push(numb);
    place.innerHTML = '<h1 class="gameO">O</h1>';
    turn = 0;
    textField.innerHTML= "Player 1' turn";
    player = playerTwoPick;
    name = "PLAYER TWO"  
        
  }

  socket.emit('move', numb, turn);
    
  placed[numb - 1] = numb;
  console.log("Player one picked: " + playerOnePick + " Player two picked: " + playerTwoPick);
    
  checkWin(player, name);
 
}

var a;
var b;
var c;
function checkWin(player, name){

  var valueOne = '<h1 class="gameO">O</h1>';
  var valueTwo = '<h1 class="gameX">X</h1>';

  var winCombos = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
    [1, 4, 7],
    [2, 5, 8],
    [3, 6, 9],
    [1, 5, 9],
    [3, 5, 7]
  ];

  for(var i = 0; i < winCombos.length; i++){

    const winCondition = winCombos[i];
    a = winCondition[0];
    b = winCondition[1];
    c = winCondition[2];
        
    if (a === '' || b === '' || c === '') {
      continue;
    }
    if (player.includes(a) && player.includes(b) && player.includes(c)) {
      textField.innerHTML = name + " IS THE WINNER";
      socket.emit("win", name);
      break;
    }
  }  
}

function startGame(){
  playerOnePick=[];
  playerTwoPick=[];
  textField.innerHTML="Player 1's turn";
  turn = 0;
  placed = Array(9).fill("");
  // 通知服务端和其他客户端重置对局
  socket.emit('startGame');
  for(var i = 0; i < gridArray.length; i++){
    gridArray[i].innerHTML = "";
  }
}

socket.on("connection", () => {
  console.log("Welcome")
});

// 修复turn更新问题,同步落子记录
socket.on('move', (number, serverTurn) => {
  // 直接更新全局turn变量
  turn = serverTurn;
  if(turn === 1){
    gridArray[number-1].innerHTML='<h1 class="gameX">X</h1>';
    textField.innerHTML="Player 2's turn";
    playerOnePick.push(number);
  } else if(turn === 0){
    gridArray[number-1].innerHTML='<h1 class="gameO">O</h1>';
    textField.innerHTML="Player 1's turn";
    playerTwoPick.push(number);
  }
  placed[number-1] = number;
})

socket.on('win', (name) => {
  textField.innerHTML= name + "IS THE WINNER!";
})

// 新加入对局同步当前状态
socket.on('syncState', (state) => {
  turn = state.turn;
  placed = state.placed;
  playerOnePick = state.playerOnePick;
  playerTwoPick = state.playerTwoPick;
  // 同步渲染棋盘
  playerOnePick.forEach(num => {
    gridArray[num-1].innerHTML='<h1 class="gameX">X</h1>';
  })
  playerTwoPick.forEach(num => {
    gridArray[num-1].innerHTML='<h1 class="gameO">O</h1>';
  })
  textField.innerHTML = turn === 0 ? "Player 1's turn" : "Player 2's turn";
})

// 同步其他客户端的开局操作
socket.on('startGame', () => {
  startGame();
})

内容的提问来源于stack exchange,提问作者catfish

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 09:39:02