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

井字棋Server/Clients开发问题:如何通知败方客户端游戏结果

Hey there, let's dig into this Tic-Tac-Toe problem you've been stuck on for two weeks—getting the losing client notified when the game ends, right? Let's break down what's missing in your current code and fix it step by step.

Problem Context

Our team is working on a course project modifying a Tic-Tac-Toe codebase to add win/draw detection. We've got the winning client and server showing the victory message, but we can't get the server to notify the other client that they've lost. Here's the relevant code:

Server Connections Code

public void execute() { 
    // wait for each client to connect 
    for ( int i = 0; i < players.length; i++ ) { 
        try { 
            players[ i ] = new Player( server.accept(), i ); 
            runGame.execute( players[ i ] ); // execute player runnable 
        } catch ( IOException ioException ) { 
            ioException.printStackTrace(); 
            System.exit( 1 ); 
        } 
    } 
    gameLock.lock(); // lock game to signal player X's thread 

    public boolean validateAndMove( int location, int player ) { 
        // while not current player, must wait for turn 
        while ( player != currentPlayer ) { 
            gameLock.lock(); // lock game to wait for other player to go 
            try { 
                otherPlayerTurn.await(); // wait for player's turn 
            } catch ( InterruptedException exception ) { 
                exception.printStackTrace(); 
            } finally { 
                gameLock.unlock(); // unlock game after waiting 
            } 
        } 
    }

Server Run Code

public void run() { 
    // send client its mark (X or O), process messages from client 
    try { 
        displayMessage( "Player " + mark + " connected\n" ); 
        output.format( "%s\n", mark ); // send player's mark 
        output.flush(); // flush output 

        // if player X, wait for another player to arrive 
        if ( playerNumber == PLAYER_X ) { 
            output.format( "%s\n%s", "Player X connected", "Waiting for another player\n" ); 
            output.flush(); // flush output 
            gameLock.lock(); // lock game to wait for second player 
            try { 
                while( suspended ) { 
                    otherPlayerConnected.await(); // wait for player O 
                } 
            } catch ( InterruptedException exception ) { 
                exception.printStackTrace(); 
            } finally { 
                gameLock.unlock(); // unlock game after second player 
            } 
            // send message that other player connected 
            output.format( "Other player connected. Your move.\n" ); 
            output.flush(); // flush output 
        } else { 
            output.format( "Player O connected, please wait\n" ); 
            output.flush(); // flush output 
        } 

        // while game not over 
        while ( !isGameOver() ) { 
            int location = 0; // initialize move location 
            if ( input.hasNext() ) location = input.nextInt(); // get move location 

            // check for valid move 
            if ( validateAndMove( location, playerNumber ) ) { 
                displayMessage( "\nlocation: " + location ); 
                output.format( "Valid move.\n" ); // notify client 
                ticTacBoard[location] = markLocation(location); 

                if(checkForXWin()) { 
                    output.format( "Player X Has Won!\n" ); 
                    displayMessage( "\nPlayer X Has Won!" ); 
                } else if(checkForOWin()) { 
                    output.format( "Player O Has Won!\n" ); 
                    displayMessage( "\nPlayer O Has Won!" ); 
                } else if(checkForDraw()) { 
                    output.format( "We have a Draw!\n" ); 
                    displayMessage( "We have a Draw!\n"); 
                    gameLock.lock(); 
                } 
                output.flush(); 
            } else { 
                output.format( "Invalid move, try again\n" ); 
                output.flush(); // flush output 
            } 
        }

Client Connection Code

public void startClient() { 
    try { 
        // make connection to server 
        connection = new Socket( InetAddress.getByName( ticTacToeHost ), 12345 ); 
        // get streams for input and output 
        input = new Scanner( connection.getInputStream() ); 
        output = new Formatter( connection.getOutputStream() ); 
    } catch ( IOException ioException ) { 
        ioException.printStackTrace(); 
    } 
    // create and start worker thread for this client 
    ExecutorService worker = Executors.newFixedThreadPool( 1 ); 
    worker.execute( this ); // execute client 
}

Client Read/Write Code

private void processMessage( String message ) { 
    // valid move occurred 
    if ( message.equals( "Valid move." ) ) { 
        displayMessage( "Valid move, please wait.\n" ); 
        setMark( currentSquare, myMark ); // set mark in square 
    } else if ( message.equals( "Invalid move, try again" ) ) { 
        displayMessage( message + "\n" ); // display invalid move 
        myTurn = true; // still this client's turn 
    } else if ( message.equals( "Opponent moved" ) ) { 
        int location = input.nextInt(); // get move location 
        input.nextLine(); //skip next line 
        int row = location / 3; // calculate row 
        int column = location % 3; // calculate column 
        setMark( board[ row ][ column ], ( myMark.equals( X_MARK ) ? O_MARK : X_MARK ) ); // mark move 
        displayMessage( "Opponent moved. Your turn.\n" ); 
        myTurn = true; // now this client's turn 
    } else { 
        displayMessage( message + "\n" ); // display the message 
    } 
}

What's Wrong With the Current Code

The core issue is straightforward: when you detect a win or draw, you're only sending a message to the current player (the one who just made the move). You never notify the other client that they lost, and you don't properly terminate the game loop for both players once the game ends.


Step-by-Step Fixes

1. Modify Server Win/Draw Detection to Notify Both Players

Update the win/draw check section in your server's run() method to send tailored messages to both players, then trigger game termination:

boolean gameEnded = false;
if(checkForXWin()) {
    // Notify X they won, O they lost
    players[PLAYER_X].output.format("Player X Has Won!\n");
    players[PLAYER_O].output.format("Player X Has Won! You lost.\n");
    displayMessage( "\nPlayer X Has Won!" );
    gameEnded = true;
} else if(checkForOWin()) {
    // Notify O they won, X they lost
    players[PLAYER_O].output.format("Player O Has Won!\n");
    players[PLAYER_X].output.format("Player O Has Won! You lost.\n");
    displayMessage( "\nPlayer O Has Won!" );
    gameEnded = true;
} else if(checkForDraw()) {
    // Notify both it's a draw
    players[PLAYER_X].output.format("We have a Draw!\n");
    players[PLAYER_O].output.format("We have a Draw!\n");
    displayMessage( "We have a Draw!\n");
    gameEnded = true;
}

// Handle game termination
if(gameEnded) {
    players[PLAYER_X].output.flush();
    players[PLAYER_O].output.flush();
    setGameOver(true); // Mark game as over
    // Wake up any waiting players to exit their loops
    gameLock.lock();
    try {
        otherPlayerTurn.signalAll();
    } finally {
        gameLock.unlock();
    }
} else {
    // Only send valid move message if game is still running
    output.format( "Valid move.\n" );
    output.flush();
}

2. Add a Game Over Flag to the Server

Add a volatile boolean field and helper methods to track game state across all player threads:

private volatile boolean gameOver = false;

public boolean isGameOver() {
    return gameOver;
}

public void setGameOver(boolean value) {
    gameOver = value;
}

3. Fix the ValidateAndMove Loop

Update the validateAndMove() method to exit early if the game ends, so players don't get stuck waiting forever:

public boolean validateAndMove( int location, int player ) {
    // Exit immediately if game is over
    if(isGameOver()) {
        return false;
    }
    // Wait for turn only if game is still running
    while ( player != currentPlayer && !isGameOver()) {
        gameLock.lock();
        try {
            otherPlayerTurn.await();
        } catch ( InterruptedException exception ) {
            exception.printStackTrace();
        } finally {
            gameLock.unlock();
        }
    }
    // Rest of your existing validation logic...
}

4. (Optional) Disable Client Input After Game Ends

Add a game over flag to your client to prevent users from sending moves after the game finishes:

private boolean gameOver = false;

Update processMessage() to set the flag when game ends:

else if (message.contains("You lost.") || message.contains("Has Won!") || message.contains("Draw")) {
    displayMessage(message + "\n");
    gameOver = true;
}
// Keep existing logic for other messages

Then add a check in your client's input handling code (where you send moves to the server) to ignore input if gameOver is true.


Why This Works
  • We directly access both players' output streams to send targeted messages (win for one, loss for the other) when a game-ending condition is met.
  • The volatile game over flag ensures all player threads see the updated state immediately, so their run loops exit cleanly.
  • We signal all waiting threads to wake up, preventing them from getting stuck in the await() call after the game ends.

Give these changes a shot—this should resolve the issue where the losing client never gets notified. If you hit any snags with implementation, feel free to ask for clarification!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 09:51:02