opponent moves promise in multiplayer game

133 Views Asked by At

I have spent the last four days studying promises, coroutines, fibers, continuations, etc.

I am still unable to see how to resolve my multiplayer turn-based card game moves, in which the starting player is effectively the game 'controller' of up to five, either AI or human players.

The code below works but has one problem:-

it cannot detect human oppo's card moves and therefore continues playing without them, which, of course makes a mess.

Can anyone please suggest either a change to my overall concept or a way to use promise or one of any other 'synchronising' constructs?

Here are the four key areas of my code:

function oppoPlays () {
    // can only go through here if game starter
    if (joiner!="") {return;}
    for (pp=1; pp<numberofplayers; pp++) {
        if (oppoType[pp] == "AI") {
            // an AI player moves
            .
            .
        } else {
            // non-AI player
            var yourTurnmsg="It's "+playerNames[pp]+"'s turn";
            // tell human player that it's their turn
            $("#text_message").val(yourTurnmsg).trigger(jQuery.Event('keypress', { keyCode: 13, which: 13 }));
            // how to detect human oppo's card moved?

        }
    }
}

// chat functionality
$("#text_message").on("keypress", function(e) {
    if (e.keyCode == 13){
        payload = new Object();
        payload.action = 'chat_text';
        payload.chat_text = tmsg; // It's michael29's turn
        payload.user_id = playerNames[pp];
        payload.game_no = game_no;
        socket.send(JSON.stringify(payload));
    }
});

// socket gets oppo's response
function checkJson(res, sttr_id, game_no) {
    if(res.action=="game_move"){
        // find player
        var pp=playerNames.indexOf(res.user_id);
        cpos=res.cardno;
        playCard_oppo(pp, cpos);
    }
}

// turn an oppo's card face up and update scores
function playCard_oppo(pp, cardno) {
    //  and move it to the stack
    topoc= parseInt($("#oppo_card" + cardno).css('top'));
    leftoc=parseInt($("#oppo_card" + cardno).css('left'));
    $("#oppo_card" + cardno).css({ top: topoc, left: leftoc, opacity: "50%" });
    .
    .
    if (joiner=="") {
        // tell oppoPlays fn that the card has moved 
    }
}

The game is similar to uno in concept but with a scoring component

(aimed to help children with basic arithmetic).

3

There are 3 best solutions below

1
sorak On

Consider having a board state that is a global and player/AI moves modify it. Then, when it is time for the AI opponent to make a move, it consults the current board state and decides the move.

If your board state is just represented by elements on the page you'll need a way to scan it and calculate a useful in-memory representation of the board state. Without details of your implementation it's hard to be more specific.

5
Roamer-1888 On

Maybe start thinking in terms of a play-cycle, something like this :

  1. (host) Broadcasts game state.
  2. (host) Awaits confirmation from all clients that broadcast was received.
  3. (clients) Render game state.
  4. (host) Receives confirmations then informs next client (and hence its player) that it is his/her/its turn.
  5. (host) Awaits player's move
  6. (client) Unlocks UI allowing player to make move.
  7. (player) Makes move.
  8. (client) Sends move command and re-locks UI.
  9. (host) Receives move command and modifies game state accordingly.

Then back to 1.

Also, think of AI players as just a special case of human players. If you can get it right for humans, then (re)introducing AI should be fairly simple.

0
cneeds On

The solution hinged around two things:-

  1. separating the AI player code from the human player code;

  2. adding and removing a window event that's triggered after a human's move is detected.

The condensed code now looks like this:-

// if this is game starter give each player a turn
if (joiner == "") {
    //  there's always at least one
    pp = 1;
    if (oppoType[pp] == "AI") { AIplays(); } else { humanPlays(); }
}

function humanPlays () {
    // tell human player that it's their turn
    var yourTurnmsg="It's "+playerNames[pp]+"'s turn"
    $("#text_message").val(yourTurnmsg).trigger(jQuery.Event('keypress', { keyCode: 13, which: 13 }));

    //window.addEventListener("humanPlayed", function(evnt) {
    $(window).on("humanPlayed", function(evnt) {
        endOfTurn();
    });
}

function endOfTurn () {
    if (!(winner)) {
        if (pp++ != numberofplayers) {
            if (oppoType[pp] == "AI") {
                setTimeout(function (){ $("#clickForNextPlayer").show(); }, 1000);
            } else {
                $("#clickForNextPlayer").trigger('click');
            }
        }
    }
}

// click for next player
$("#clickForNextPlayer").on('click', function() {
    $("#clickForNextPlayer").hide();
    $(window).off("humanPlayed");
    if (pp == numberofplayers) {
        // uncover outStack for game starter to play
        $("#outStackcover").hide();
        return;
    }
    if (oppoType[pp] == "AI") { AIplays(); } else { humanPlays(); }
});

function AIplays () {
    AIcardno = chooseCard(pp, diffLevel);
    .
    .
    if ($("#chatWindow").is(":visible")) {
        payload = new Object();
        payload.action="game_move";
        payload.game_no=gamestarted;
        payload.user_id=playerNames[pp];
        payload.cardno=AIcardno;
        socket.send(JSON.stringify(payload));
    }
    $("#oppo_card" + cc).css('background-image', "url(JerseyTeam" + playerNumbers[(pp == numberofplayers ? 1 : pp)] + ".gif)");
    outStackturn();
    endOfTurn();
}