From: Carl Worth Date: Fri, 5 Jun 2020 23:18:57 +0000 (-0700) Subject: game: Allow either player to play the first move X-Git-Url: https://git.cworth.org/git?p=empires-server;a=commitdiff_plain;h=76232144e294f384cde30933687de514fd90584a game: Allow either player to play the first move When players originally join the game they do not belong on any team. And while we could require the players to divide themselves up and join separate teams, it's much easier for them if they aren't required to coordinate on that. So, with this commit we allow any player, (not yet in a team), to make the first move. At that point, we assign that player the appropriate team for playing first, and assign at least one player (if there are others in the game) to each successive team value in the order they originally joined the game. This means that in the common case of a 2-player game, if both players are in the game, and one player makes a move, then the two players will be assigned to separate teams. --- diff --git a/game.js b/game.js index 72c3c7f..24e36ad 100644 --- a/game.js +++ b/game.js @@ -50,6 +50,7 @@ class Game { this.state = { team_to_play: "" }; + this.first_move = true; /* Send a comment to every connected client every 15 seconds. */ setInterval(() => {this.broadcast_string(":");}, 15000); @@ -80,23 +81,71 @@ class Game { * to override this (and call super.add_move early!) to implement * the actual logic for a move. */ add_move(player, move) { - /* Cannot move if you are not on a team. */ - if (player.team === "") - { - return { legal: false, - message: "You must be on a team to take a turn" }; - } - /* Cannot move if it's not this player's team's turn. */ - if (player.team !== this.state.team_to_play) - { - return { legal: false, - message: "It's not your turn to move" }; + /* The checks here don't apply on the first move. */ + if (! this.first_move) { + + /* Discard any move asserting to be the first move if it's no + * longer the first move. This resolves the race condition if + * multiple players attempt to make the first move. */ + if (move.assert_first_move) { + return { legal: false, + message: "Your opponent beat you to the first move" }; + } + + /* Cannot move if you are not on a team. */ + if (player.team === "") + { + return { legal: false, + message: "You must be on a team to take a turn" }; + } + + /* Cannot move if it's not this player's team's turn. */ + if (player.team !== this.state.team_to_play) + { + return { legal: false, + message: "It's not your turn to move" }; + } } return { legal: true }; } + /* Assign team only if player is unassigned. + * Return true if assignment made, false otherwise. */ + assign_player_to_team_perhaps(player, team) + { + if (player.team !== "") + return false; + + player.team = team; + this.broadcast_event("player-update", player.info_json()); + + return true; + } + + /* This function is called after the child add_move has returned + * 'result' so that any generic processing can happen. + * + * In particular, we assign teams for a two-player game where a + * player assumed a team by making the first move. */ + post_move(player, result) + { + if (this.first_move && result.legal) { + this.first_move = false; + + this.assign_player_to_team_perhaps(player, this.teams[0]); + + /* Yes, start at 1 to skip teams[0] which we just assigned. */ + for (let i = 1; i < this.teams.length; i++) { + const other = this.players.find(p => p !== player && p.team === ""); + if (!other) + return; + this.assign_player_to_team_perhaps(other, this.teams[i]); + } + } + } + add_player(session, connection) { /* First see if we already have a player object for this session. */ const existing = this.players_by_session[session.id]; diff --git a/lmno.js b/lmno.js index c254046..b85041e 100644 --- a/lmno.js +++ b/lmno.js @@ -324,6 +324,9 @@ for (let key in engines) { const result = game.add_move(player, move); + /* Take care of any generic post-move work. */ + game.post_move(player, result); + /* Feed move response back to the client. */ response.json(result); diff --git a/test b/test index b480b5f..d8a46ec 100755 --- a/test +++ b/test @@ -372,25 +372,30 @@ TEST_END TEST_SUBSECTION "Tic Tac Toe /move" -TEST "Illegal to move when not on a team" +TEST "First move doesn't require a team" +result=$(tictactoe_move 0) +test "$result" = '{"legal":true}' +TEST_END + +TEST "Second move does require a team" result=$(tictactoe_move 4) -test "$result" = '{"legal":false,"message":"You must be on a team to take a turn"}' +test "$result" = '{"legal":false,"message":"It'"'"'s not your turn to move"}' TEST_END TEST "Illegal to move when it's not your turn" -tictactoe_player_team O +tictactoe_player_team X result=$(tictactoe_move 4) test "$result" = '{"legal":false,"message":"It'"'"'s not your turn to move"}' TEST_END TEST "Legal move to center square" -tictactoe_player_team X +tictactoe_player_team O result=$(tictactoe_move 4) test "$result" = '{"legal":true}' TEST_END TEST "Move to center square again is now illegal" -tictactoe_player_team O +tictactoe_player_team X result=$(tictactoe_move 4) test "$result" = '{"legal":false,"message":"Square is already occupied"}' TEST_END