Game Server 7.* supports two types of two-player games (2PG): cooperative and adversarial. This documents describes essential points in which managing 2PG differes from managing single-player games (1PG).
When planning an experiment with a two-player game, the experiment designer will have to construct a trial list file in the following way.
When a player follows a link to a two-player experiment plan, the Rule Game server's actions will be somewhat different if that's an "odd-numbered" player (1st, 3rd, 5th etc) who has followed the link and started interacting with the system, or an "even-numbered" one (2nd, 4th, 6th...).
An "odd-numbered" player, upon arrival to the Rule Game server, will be assigned to the one player list within the experiment plan. He will be then presented, one by one, the pages of the instruction booklet. Once done with the booklet, he most likely will be shown a "please wait for the second player" page, until the second player in the pair has arrived and has gone through the instruction booklet as well.
When an "even-numbered" player arrives, the system will immediately match him with the preceding "odd-numbered" player. He, too, will be shown the instruction booklet, and -- if somehow he finishes reading his booklet before his partner -- he may see the "waiting for the second player" page as well.
If desired by the PI, we can attempt to make the "cooperative" or "competitive" aspect of the game a bit more real for the players by letting him pick human-readable player names. Maybe we can assign the names to them randomly (e.g. by randomly combining geographic names and animal species names, e.g. "Connecticut Woodchuck" or "Saskatchewan Koala", or the names of famous sports teams), or can offer them to invent names themselves (hoping that they won't use obscenities). So the display board shown to the player will show both parties' names.
Once both players in a pair have gone through their instruction booklets, they will be shown playing board and the appropriate progress/statistical information. The main differences from the presentations currently provided in single-player games will include the following:
It should be made clear to the player when it's his turn to make a move attempt, and when it isn't. This probably can be made by changing the shape of the cursor (and also greying it), as well as perhaps somewhat greying the board itself. Of course, for the benefits of people who aren't good at understanding graphic cues, we probably should write somewhere in big letters, "YOUR MOVE!" / "YOUR OPPONENT'S MOVE" (or "YOUR PARTNER'S MOVE").
The player should be shown the opponents/partners actions (both successful moves and failed move attempts), using more or less the same display tools that show to him his own actions. This includes game pieces moving across the board, smiling/frowning faces as pieces fit or don't fit into buckets, and (in certain display modes) showing pieces already put into buckets.
The display elements showing progress (how many moves attempts have been made, how many of them have been successful) will need to be expanded, to show the relevant numbers for both players and their sum.
Rewards earned so far for both players will be shown as well.
The stopping criteria for each parameter set (i.e. a series of episodes with a given rule set) will be determined by the appropriate parameters in the parameter set. This is discussed in a seprate section below.
Once the last series of a trial list is completed, both players will be directed to the demographics pages, as usual.
ISSUE: Drop outs. Although the players recruited through Amazon M-Turk or Prolific are paid, not all of them will diligently complete their game. (On our M-Turk sample, out of ca. 2277 players, 336 (15%) did not complete even a single episode (may have dropped out while reading the instruction booklet), 511 (22%) played one or more episodes but never got a completion code, and only 1430 (63%) got a completion code. M-Turkers in our single players did not have time pressure, and could take breaks as needed, e.g. when their mother or wife would call them to have a dinner, or when they received a phone call. If participants in two-player games drop out at the same rate, we will end up with a large number of incompleted experiments, when one player dropped out (or just took a long break so that the other player did not bother waiting for hime to return). I am not sure what's the best strategy to reduce the number of unhappy abandoned partners and incomplete experiments.
Incentive schemes, stopping considerations, etc
In the single-player games, while each parameter set always specifies the default number of episodes to be played with the rule set (max_boards), we also support a variety of options for "flow control" within the series. In particular, a player may be allowed to "give up" on an rule set, thus terminating the series early; in this case, he is just given reward for any episodes he has completed. A player may be allowed to request a "bonus subseries" -- a number of episodes that he needs to play well (with few errors) in order to get a bonus reward on top of the standard reward for each episode. (That's the BONUS incentive scheme). The series may have a provision for doubling the reward if a certain level of proficiency has been achieved, and for quadrupling the reward and automatic early termination. (The DOUBLING or LIKELIHOOD incentive schemes).
What control options/incentive schemes can we use in two-player games?
* If "giving up" on a rule set is allowed, how does it work? If it terminates the series for both players, then it's probably annoying for the player who still wants to play. If it terminates the series for the player (player B) who has elected to give up, then player A will have to complete the series as in a single-player game, while player B will have to twiddle his thumbs until the next series starts. While doing that, player B may just walk away... and then player A won't have a partner for the subsequent para sets. Overall, not a good situation either way.
* If we want something like the current BONUS incentive scheme, how would that work? Would either player be allowed to ask for a bonus subseries, regardless of how the other player feels? Presumably, if a bonus subseries is played, it will be continued for as long as at least one of the players maintaind sufficiently good performance; for the purposes of issuing the bonus reward, each player will be judged individually, based on his own moves.
* Doubling/quadrupling incentive schemes (DOUBLING or LIKELIHOOD). It seems like these should be handled differently for "cooperative" and "competitive" games. In cooperative games, we are looking at the "team's mastery of the rules"; i.e. the mastery criterion will be based on the sequence of moves without regard to who made them; the doubling and quadrupling double and quadruple both players' rewards, and when mastery is achieved, the game is stopped, and both players are asked for their ideas. In competitive games, we can look at only the moves of one player to decide if he has reached partial mastery (doubling the reward) or full mastery (quadrupling). I suppose as soone as one player has achieved full mastery, we can end the series; the other player is thus a (comparative) "loser", since he did not get his quadrupling.
The following rules have been agreed upon.
After that, we can give W points to each player; or we can divide the total of 2*W points among the two players in a slightly different way, e.g. taking into account the number of successful and failed moves that each player has made. E.g. if the first player has removed n1 pieces from the board, and the second player has removed n2 pieces, we can give w1 = (2*W)*n1/(n1+n2) points to the first player for this series, and w2 = (2*W)*n2/(n1+n2) points to the second player.
Since the players are competing, we should compute each player's score based on his own actions. For each episode, the base reward for player j (j=0 or 1) can be computed by, first, using the Kantor-Lupyan formula, but only taking into account that player's errors during that episode; then, prorating by the number of game pieces that were removed by that player. Thus, if players 0 and 1 removed n0 and n1 pieces from the board of n=n0+n1
pieces, and made e0 and e1 errors respectively, their base rewards will be
r0 = KL(e0) ⋅ n0 / n,
r1 = KL(e1) ⋅ n1 / n.
(I have introduced the prorating term, nj / n , in order to avoid the counterintuitive assignment of a higher reward to the partner who made fewer errors because he also removed fewer pieces).
When a mastery-based incentive scheme (LIKELIHOOD or DOUBLING) is used, the "mastery metric" (the length of a "good stretch", or the Bayesian R product) will be computed individually for each player, based only on his moves (and entirely ignoring the other player's moves). Based on that metric, the reward of the player who has demonstrated mastery will be doubled or quadrupled, as appropriate.
Below, Player A is the one who is given the first turn in the first episode.
This section, outlying major changes to the Game Server's data stored data structures, is of little interest to the PIs, but I keep it here for my later reference.
We will say that in each two-player game there is the "first player" (Playe 0) and the "second player" (Player 1). The PlayerInfo table will have an extra column used to link the two, so that the first player's table entry would contain a link to the second player. The Episode table won't need to change; it will link episodes to the first player.
The transcript of an episode (as dumped into the transcipt CSV file) will contain an extra column, indicating for each move [attempt] whether the move was made by the first or second player of the game.
The following two sections describe first the HTTP message exchange in GS 6.*, and then the proposed message exchange in GS 7.*
The data exchange in GS 6.* is built purely on HTTP requests and responses. The available API calls (HTTP requests) are described in more detail in Game API; here we just describe the most essential parts of this exchange. The following notation is used:
REQUEST ==================> <================= RESPONSE
/player =====================> <======================= confirmation of registration trial list ID /newEpisode =====================> <======================= episode ID /display =====================> <======================= initial game state /move or /pick (describes the player action) =====================> <======================= response code (accept/reject) + new game state /display =====================> <======================= current game state
The "game state" sent by the server in response to /move, /pick, and /display calls includes the current state of the board, the finishCode (0 if the episode continues, or some other value to indicate that the episode has finished, and in what way it finished), various progress indicators. If the episode has finished, this also includes
While it is possible to entirely replace the exchange of HTTP requests and responses, I am in favor of a more conservative appoaches: mostly keeping the HTTP requests as they are, but adding to some responses some additional information.
In particular:
HTTP REQUEST ==================> <================= HTTP RESPONSE websocket opening connection ...................> websocket message <...................
/player =====================> <======================= confirmation of registration ; info on game type (isAdveGame, isCoopGame) trial list ID /newEpisode =====================> <======================= episode ID, or "wait" flag client opening a socket connection, and identifying itself ........................> <....................... "READY EPI", asking the client to make a /display call /display =====================> <======================= initial game state; possibly a "wait" flag /move or /pick (describes the player action) =====================> <======================= response code (accept/reject) + new game state (+ possibly a WAIT flag) <............................... "READY DIS", teling the client to make a /display call /display =====================> <======================= current game state and the partner's recent move(s) <............................... "READY DIS" /display =====================> <======================= current game state
This section describes important changes in the behavior of some calls as compared to that seen in the GS 6.* API
This is the call made by the GUI client in the beginning of the player's interaction with the system. The returned structure now includes the following fields:
If the client sees that twoPlayerGame==true, it should open a websocket connection, so that it will be able to receieve socket messages in the future. The WS URL (the server endpoint) for that WS connection is /websocket/watchPlayer (relative to the base URL of the GS web application; so for example if the base URL of the Game Server you're using is is http://localhost:8080/w2020/ , then the absolute URL for the WS server endpoint will be http://localhost:8080/w2020/websocket/watchPlayer ). Once the connection has been opened, the GUI client should identify itself to the server, by send to it, over the WS connection, the message with the text
IAM xxxxwhere xxxx stands for the player's playerId.
The WS connection should stay open for the duration of the session; if the client detects that it's been closed by the server, it should reopen it.
This call is made once the player has made it through the intro pages (the instruction booklet), and is ready to play his first episode. Later, such a call is made to start every subsequent episode.
In GS 7.*, the returned structure may include the field
This 3 calls return essentially the same structure, describing the state of the game, either after the player has made a move/pick attempt (the /move or /pick call), or simply because the the client wants to see the current state. The only difference in the structure returned by these calls is that in the /move and /pick calls, the code fields contains the result of the players pick or move attempt (successful or not), while in the /display call this field has a special value (-8, i.e. EPISODE.CODE.JUST_A_DISPLAY).
Arguments:
Return structure:
This call also takes the playerId=... parameter now, so that the server will correctly record the guess.
The GUI client should enable the chat GUI element, consisting from a text entry box and the message exchange display box, if needChat==true was receieved in the response of the /player call.
When the player enters text in the text entry box, the client should send the entered text, with the prefix "CHAT ", prepended, as a text message over the WS connection (which, in any 2PG, must have been opened since the beginning of the session).
In 2PG, the client must be watching over the WS connection all the time, in order to receieve "READY" messages. Whenever it receives a message and discovers that it starts with the prefix "CHAT ", rather thean "READY ", it should treat the text that follows that prefix as a chat message from the partner, and add it to the list of messages displayed in the message exchange display box.
In games with a mastery-based incentive scheme, make sure to offer "guess entry" box to the losing player too.
To see how the server works when playing a 2PG, you can use the HTML play interface. This is how to use it:
During the game, the HTML pages used in the HTML play cause the browser to make server requests with URLs similar to those that the GUI client would be using: e.g. /displayHtml and /moveHtml instead of /display and /move. Each of these calls returns a new HTML page that would contain most of the same elements (board display and various progress indicators, buttons, etc) that the GUI client would display at the same stage in the game.
At the bottom of each screen, you would see the entire content of the JSON structure that the apppropriate API call (/display, /move, etc) would return in this situation. It is the content of this data structure that is used by the server to create the HTML page you see --- and in the GUI client it will be the same data structure whose content will be used to show everything the GUI client shows.
One can see how these HTML pages are generated by looking at the source code of the server class edu.wisc.game.rest.GameService2Html; the structure from which it takes all the necessary data is an instance of EpisodeInfo.ExtendedDisplay, and it is exactly this structure which, in JSON form, is sent to the GUI client as the response to the /move, /pick, and /display calls.
(Note: if you look at the GameService2Html code, you will see that very occasionally this class uses data that are not available in EpisodeInfo.ExtendedDisplay. For example, it accesses methods PlayerInfo.isAdveGame(), isCoopGame(), is2PG(). What the GUI client should do instead is to save in its variables the fields isAdveGame etc from the response of the /player call, and then use these variables later, whenever processing the response of /move or /display etc calls.)
The HTML play pages also have a built-in functionality for exchanging WS messages. Every time an HTML page of the HTML play loads, it opens a WS connection and sends an identifying message. (This is done by the JS code in the file js/socket1.js, included into the HTML coded). The JS code attaches a listener to its WS endpoint; that listener analyzes incoming messages from the server, and when the "READY EPI" or "READY DIS" message comes, it triggers the submission of an appropriate form in the HTML page, which makes an appropriate server HTTP call (such as /displayHtml), which reloads the page.
The GUI client, which is writte in TypeScript/React, will of course do this a little bit different; it may choose to open the connection only once, early on (although subsequently it will need to monitor its status, and if it closes, reopen it). When the listener detects a "READY ..." message, the client will simply make an appropriate /newEpisode or /display call, to obtain the data to be processed, much like the current client already does.
(The M-Turk population: 847 players did not get a completion code, among them 336 did not complete a single episode, 139 competed a single episode, etc 1430 players got a completion code) mysql> select count(*) from PlayerInfo where REGEXP_LIKE(PlayerId, '^A[A-Z0-9]........', 'c') and completionCode is null order by PlayerId; +----------+ | count(*) | +----------+ | 847 | mysql> select count(*) from PlayerInfo where REGEXP_LIKE(PlayerId, '^A[A-Z0-9]........', 'c') and completionCode is not null order by PlayerId; +----------+ | count(*) | +----------+ | 1430 | +----------+ select count(*) from PlayerInfo p where REGEXP_LIKE(playerId, '^A[A-Z0-9]........', 'c') and (select count(*) from Episode e where p.id=e.PLAYER_ID) = 0; +----------+ | count(*) | +----------+ | 336 | +----------+ 1 row in set (0.03 sec) select count(*) from PlayerInfo p where REGEXP_LIKE(playerId, '^A[A-Z0-9]........', 'c') and (select count(*) from Episode e where p.id=e.PLAYER_ID) = 1; +----------+ | count(*) | +----------+ | 139 | +----------+ select count(*) from PlayerInfo p where REGEXP_LIKE(playerId, '^A[A-Z0-9]........', 'c') and (select count(*) from Episode e where p.id=e.PLAYER_ID) = 1; REGEXP_LIKE(playerId, '^A[A-Z0-9]........', 'c') and p.completionCode is not null group by p.id; (count(*) from Episode e where PlayerInfo.id=e.PLAYER_ID) c from PlayerInfo p where REGEXP_LIKE(playerId, '^A[A-Z0-9]........', 'c') and p.completionCode is not null group by p.id; (ISSUE: what happens if one drops out?)