在 Tic Tac Toe 中改变游戏模式 [The Odin Project]

Changing game mode in Tic Tac Toe [The Odin Project]

这是我的第一个问题,如果我做错了,请先道歉!

我正在学习使用 Odin 项目 编写代码。到目前为止,我一直在做每个项目,没有遇到任何重大问题:偶尔会遇到一些困难,但经过一番思考,我能够成功解决它们。

目前,我在 Tic Tac Toe JavaScript 项目中。我已经让游戏运行起来,实现了两种游戏模式:VS Player 2VS CPU。两者都是先通过HTML欢迎window选择的,我为了避免使用提示,都运行没有问题。但是,每当我尝试通过放置在界面中的按钮更改游戏模式时,我都会失败并且模式保持不变。

例如,如果我想从VS Player 2模式更改为VS CPU模式,分数会重置并且玩家的名字改变了,但游戏保持 运行ning 在 VS Player 2 模式。没有显示控制台错误,所以我确定这与范围或逻辑有关!

我已经尝试了很多东西,包括生成 HTML 欢迎 window 和 JavaScript DOM 操作,但我无法使其工作。我遇到这个问题已经两周了,但我仍然碰壁!

这是管理模式选择的代码部分:

const modeSelector = {
constants: [
    /* 0 */ modalOverlay = document.getElementById("modal-overlay"),
    /* 1 */ modalWindow = document.getElementById("modal-window"),
    /* 2 */ modalContent = document.getElementById("modal-content"),
    /* 3 */ gameButtons = document.getElementById("game-buttons"),
    /* 4 */ popUpVs = document.getElementById("popUpVs"),
    /* 5 */ popUpCpu = document.getElementById("popUpCpu"),     
    /* 6 */ vsPlayerTwo = document.getElementById("vsplayer2"),
    /* 7 */ vsCPU = document.getElementById("vsCPU"),
],
gameMode(mode) {
    if (mode === "") {
    } else if (mode === "vsPlayerTwoMode") {
        gameBoard.cleanBoard();
        p1 = playerCreator.createPlayer();
        p1.name = playerCreator.names[0].innerHTML;
        playerCreator.scores[0].innerHTML = "0";
        p2 = playerCreator.createPlayer();
        p2.name = playerCreator.names[1].innerHTML;
        playerCreator.scores[1].innerHTML = "0";
        movements.movement();
        rules.turnChanger();
    } else if (mode === "vsCPUMode") {
        gameBoard.cleanBoard();
        p1 = playerCreator.createPlayer();
        p1.name = playerCreator.names[0].innerHTML;
        playerCreator.scores[0].innerHTML = "0";
        p2 = playerCreator.createPlayer();
        p2.name = "CPU"
        playerCreator.names[1].innerHTML = p2.name;
        playerCreator.scores[1].innerHTML = "0";
        movements.vsCpuMovement();
        rules.turnChanger();
    }
},
reset() {
    gameBoard.cleanBoard();
    modeSelector.gameMode("");
    playerCreator.names[0].innerHTML = "0";
    playerCreator.names[1].innerHTML = "0";
    playerCreator.scores[0].innerHTML = "0";
    playerCreator.scores[1].innerHTML = "0";
},    
popUpMode(mode) {
    if (mode === "popUpVsMode") {
        modeSelector.constants[2].innerHTML = "Insert Player 1 name";
    } else if (mode === "popUpCPUMode") {
        modeSelector.constants[2].innerHTML = "Insert Player name";
    }
    modeSelector.constants[2].style.marginLeft = "130px";
    modeSelector.constants[4].remove();
    modeSelector.constants[5].remove();
    let p1NameBar = document.createElement("input");
    p1NameBar.id = "p1NameBar";
    p1NameBar.maxLength = "6";
    modeSelector.constants[3].appendChild(p1NameBar);
    let p1NameOk = document.createElement("h2");
    p1NameOk.class = "popUpButton";
    p1NameOk.type = "button";
    p1NameOk.id = "popUpP1NameOk";
    p1NameOk.innerHTML = "OK";
    modeSelector.constants[3].appendChild(p1NameOk);
    p1NameOk.addEventListener("click", () => {
        if (mode === "popUpVsMode") {
            playerCreator.names[0].innerHTML = p1NameBar.value;
            modeSelector.constants[2].innerHTML = "Insert Player 2 name";
            modeSelector.constants[2].style.marginLeft = "130px";
            p1NameBar.remove();
            p1NameOk.remove();
            let p2NameBar = document.createElement("input");
            p2NameBar.id = "p2NameBar";
            p2NameBar.maxLength = "6";
            modeSelector.constants[3].appendChild(p2NameBar);
            let p2NameOk = document.createElement("h2");
            p2NameOk.className = "popUpButton";
            p2NameOk.type = "button";
            p2NameOk.id = "popUpP2NameOk";
            p2NameOk.innerHTML = "OK";
            modeSelector.constants[3].appendChild(p2NameOk);
            p2NameOk.addEventListener("click", () => {
                playerCreator.names[1].innerHTML= p2NameBar.value;
                document.body.removeChild(modalOverlay);
                modeSelector.gameMode("vsPlayerTwoMode");                    
            });
        } else if (mode === "popUpCPUMode") {
            playerCreator.names[0].innerHTML = p1NameBar.value;
            document.body.removeChild(modalOverlay);
            modeSelector.gameMode("vsCPUMode");
        }
    });
},
inGameMode(mode) {
    modeSelector.reset()
    let inGameOverlay = document.createElement("div");
    inGameOverlay.id = "inGameOverlay";
    inGameOverlay.className = "modal-overlay";
    document.body.appendChild(inGameOverlay);
    let inGameWindow = document.createElement("div");
    inGameWindow.className = "modal-window";
    inGameWindow.id = "inGameWindow";
    inGameOverlay.appendChild(inGameWindow);
    let inGameTitleBar = document.createElement("div");
    inGameTitleBar.className = "modal-titlebar";
    inGameTitleBar.id = "inGameTitleBar";
    inGameWindow.appendChild(inGameTitleBar);
    let inGameTitle = document.createElement("span");
    inGameTitle.className = "modal-title";
    inGameTitle.id = "inGameTitle";
    if (mode === "VSP2") {
        inGameTitle.innerHTML = "VS Player 2 Mode";
        inGameTitle.style.marginLeft = "160px";
        inGameTitleBar.appendChild(inGameTitle);
        let inGameContent = document.createElement("div");
        inGameContent.className = "modal-content";
        inGameContent.id = "inGameContent";
        inGameContent.innerHTML = "Insert Player 1 name";
        inGameContent.style.marginLeft = "120px";
        inGameWindow.appendChild(inGameContent);
        let inGameButtons = document.createElement("div");
        inGameButtons.className = "modal-buttons";
        inGameButtons.id = "inGameButtons";
        inGameWindow.appendChild(inGameButtons);
        let inGameP1NameBar = document.createElement("input");
        inGameP1NameBar.id = "p1NameBar";
        inGameP1NameBar.maxLength = "6";
        inGameButtons.appendChild(inGameP1NameBar);
        let inGameP1NameOk = document.createElement("h2");
        inGameP1NameOk.className = "popUpButton";
        inGameP1NameOk.type = "button";
        inGameP1NameOk.id = "popUpP1NameOk";
        inGameP1NameOk.innerHTML = "OK";
        inGameButtons.appendChild(inGameP1NameOk);
        inGameP1NameOk.addEventListener("click", () => {
            playerCreator.names[0].innerHTML = inGameP1NameBar.value;
            inGameContent.innerHTML = "Insert Player 2 name";
            inGameContent.style.marginLeft = "130px";
            inGameP1NameBar.remove();
            inGameP1NameOk.remove();
            let inGameP2NameBar = document.createElement("input");
            inGameP2NameBar.id = "p2NameBar";
            inGameP2NameBar.maxLength = "6";
            inGameButtons.appendChild(inGameP2NameBar);
            let inGameP2NameOk = document.createElement("h2");
            inGameP2NameOk.className = "popUpButton";
            inGameP2NameOk.type = "button";
            inGameP2NameOk.id = "popUpP2NameOk";
            inGameP2NameOk.innerHTML = "OK";
            inGameButtons.appendChild(inGameP2NameOk);
            inGameP2NameOk.addEventListener("click", () => {
                playerCreator.names[1].innerHTML= inGameP2NameBar.value;
                document.body.removeChild(inGameOverlay);
                modeSelector.gameMode("vsPlayerTwoMode");
            })
        });
    } else if (mode === "VSCPU") {
        inGameTitle.innerHTML = "VS CPU Mode";
        inGameTitle.style.marginLeft = "210px";
        inGameTitleBar.appendChild(inGameTitle);
        let inGameContent = document.createElement("div");
        inGameContent.className = "modal-content";
        inGameContent.id = "inGameContent";
        inGameContent.innerHTML = "Insert Player 1 name";
        inGameContent.style.marginLeft = "120px";
        inGameWindow.appendChild(inGameContent);
        let inGameButtons = document.createElement("div");
        inGameButtons.className = "modal-buttons";
        inGameButtons.id = "inGameButtons";
        inGameWindow.appendChild(inGameButtons);
        let inGameP1NameBar = document.createElement("input");
        inGameP1NameBar.id = "p1NameBar";
        inGameP1NameBar.maxLength = "6";
        inGameButtons.appendChild(inGameP1NameBar);
        let inGameP1NameOk = document.createElement("h2");
        inGameP1NameOk.className = "popUpButton";
        inGameP1NameOk.type = "button";
        inGameP1NameOk.id = "popUpP1NameOk";
        inGameP1NameOk.innerHTML = "OK";
        inGameButtons.appendChild(inGameP1NameOk);
        inGameP1NameOk.addEventListener("click", () => {
            playerCreator.names[0].innerHTML = inGameP1NameBar.value;
            document.body.removeChild(inGameOverlay);
            modeSelector.gameMode("vsCPUMode");
        });    
    }
    let inGameInfo = document.createElement("div");
    inGameInfo.id = "modal-info";
    inGameInfo.innerHTML = "You can reset the game mode during the match by clicking on any of the buttons to the right";
    inGameWindow.appendChild(inGameInfo);
},    

};

还有一些用于“点击”的 addEventListeners 在不同的按钮中实现上述代码,但我不想添加更多代码,所以我也分享了完整的事情在 GitHub:

https://github.com/roznerx/tic_tac_toe

提前致谢,再次抱歉,如果我问错了!

如果我们首先以 vsPlayerTwoMode 模式开始,那么 movements.movement() 会为棋盘上的每个框添加一个事件侦听器。单击一个框时,它会检查它是否为空,并根据轮到谁(基于 playerCreator.turns[0]playerCreator.turns[1] 的值)将 gameBoard.board[i].innerHTML 设置为 XO.

现在让我们切换到vsCPUMode模式。板上的每个框现在都附加了原始事件侦听器(来自 movements.movement()),以及来自 movements.vsCpuMovement() 的新事件侦听器。单击框时,它将首先评估上段中的逻辑(如 movements.movement() 事件侦听器中定义的),然后是 movements.vsCpuMovement() 函数中的逻辑(值得注意的是,检查是否 gameBoard.board[i].innerHTML == "").

但是,由于 movements.movement() 事件在 movements.vsCpuMovement() 事件之前调用,因此 gameBoard.board[i].innerHTML 将被设置为 XO取决于 playerCreator.turns[0]playerCreator.turns[1] 的先前值。然后,movements.vsCpuMovement() 检查 gameBoard.board[i].innerHTML 是否为空字符串,事实并非如此,因此函数 returns.

重要的是,当模式为vsCPUMode启动时,movements.movement()事件侦听器永远不会被注册,所以无论值是多少都没有关系playerCreator.turns[0]playerCreator.turns[1] 是。

我们如何解决它?一种选择是重置 playerCreator.turns[...] 的值,以便在调用 movements.movement() 事件时,它只是忽略依赖于这些值的逻辑( 有点等价 vsCPUMode 模式开始游戏)。也就是说,在line 215:

后面加上这两行
playerCreator.turns[0] = ""
playerCreator.turns[1] = ""

有没有更好的方法解决问题?你打赌!我建议在添加新的侦听器之前将您的代码发布到 Code Review and asking people for suggestions. If you are only interested in solving this immediate problem, I would keep track of all the event listeners you add to the gameboard boxes, and if you change the modes, remove 任何现有的侦听器上。