Version 6.007, 2023-01-25
This is the "Second Batch" (prefix=/GameService2) of the Game Service web API calls. They are intended for use with a client app that works with players enrolled in experiments; each player is identified by a player ID, and the server assigns each player to a "trial list" (a particular track in the experiment plan with which this player ID is associated with).
For calls that can be used by players not enrolled in experiments (e.g. our own team members developing and testing games), see the First Batch.
On error, the return structure may have an error flag set to true, and an error message. On success, the return structure will contain an integer user id (user.id), which the Android app should save for later use in /player calls. Another value, user.idCode should be remembered as well; in future versions, it may be used for authentication. (As of ver 4.000, this is not yet supported).
Sample return value, when a new user is registered with a nickname and no email (thus, newlyRegistered=true) {"error":false,"newlyRegistered":true,"user":{"date":"2021-10-20T18:38:31.288Z[UTC]","email":"","id":6,"idCode":"user-20211020-143831-GF0LJI","nickname":"The Walrus"}}
Sample return value, when a new user is registered with a nickname and an email address (thus, newlyRegistered=true) {"error":false,"newlyRegistered":true,"user":{"date":"2021-10-20T18:40:19.968Z[UTC]","email":"v.menkov@gmail.com","id":7,"idCode":"user-20211020-144019-I9O244","nickname":"The Walrus"}}
Sample return value, when a new user is registered with no email or nickname, and with the "anon" box checked: {"error":false,"newlyRegistered":true,"user":{"date":"2021-10-20T18:45:44.047Z[UTC]","id":8,"idCode":"anon-20211020-144544-9WGVH3"}}
Sample return value, when the email and nickname matched an already registered (thus, newlyRegistered=false) {"error":false,"newlyRegistered":false,"user":{"date":"2021-10-12T01:55:21Z[UTC]","email":"vmenkov@gmail.com","id":3,"idCode":"user-5L54KP","nickname":"V Menkov Test 1"}}
Sample response:
{"error":false, "ruleInfo":[ {"completed":false,"description":["[Color Match] Each bucket collects pieces of one color"],"display":"Puzzle 1", "episodeCnt":1,"exp":"R:CGS/_1:APP/APP-no-feedback","name":"CGS/_1","playerId":"RepeatUser-7-JRQ5VR-20230125-191032"}, {"completed":false,"description":["[Clockwise] Permitted bucket moves in clockwise order, starting anywhere"],"display":"Puzzle 2", "episodeCnt":0,"exp":"R:CGS/_2:APP/APP-no-feedback","name":"CGS/_2"}, {"completed":false,"description":["[B23 then B01] Alternate lower and upper buckets"],"display":"Puzzle 3", "episodeCnt":0,"exp":"R:CGS/_3:APP/APP-no-feedback","name":"CGS/_3"}, {"completed":false,"description":["[B3 then B1] Alternate B3 (bottom-left) and B1 (top-right)"],"display":"Puzzle 4", "episodeCnt":0,"exp":"R:CGS/_4:APP/APP-no-feedback","name":"CGS/_4"}, ... ]]}As with all other calls, the client should check the error field first. If error=true, then ruleInfo is absent, and the field errmsg contains the human-readable error message.
The field ruleInfo is an array, with 1 element per rule set to be displayed. For each rule set:
The input should include either player ID (pid), or Repeat User ID (uid), or both. In the traditional approach with no repeat users (e.g., the Mechanical Turk use case) only the player ID is supplied. In the repeat-user context (e.g. from the Android app), it is best to supply only the uid; the server will then create a unique pid and return it. Sample output:
{"errmsg":"Debug:\n*M*[0]\n[1]\n[2]\nid=46, curSer=0 b=false, R=$0" ,"error":false,"alreadyFinished":false, "experimentPlan":"P:pilot06:no-feedback", "newlyRegistered":true, "playerId":"RepeatUser-8-V7CMVI-20211020-144752", "trialList":[{"clearing_threshold":1.3,"max_points":10,"b":1.5, "min_points":2,"max_colors":4,"feedback_switches":"free", "min_objects":9,"clear_how_many":2,"bonus_extra_pts":3,"rule_id": "shapeMatch_1","max_objects":9,"grid_memory_show_order":false, "min_shapes":4,"give_up_at":3,"stack_memory_show_order":false, "max_shapes":4,"min_colors":4,"stack_memory_depth":0,"max_boards":5, "activate_bonus_at":3}, ....], "trialListId":"shape_shapeBroad_posBroad"}
For the meaning of the output fields, see the documentation for PlayerResponse. A field named foo will be described there under getFoo() or (for boolean values) isFoo(). The field named playerId normally simply mirrors the input value of the same name; but if playerId was blank and user ID was supplied instead, the server will create a playerId itself, and return it via this field.
Ver. 1.021: added trialList, which is a vector with one element per line of the trial list. Can be used to find the total number of rule sets to which a player will be exposed.
Sample output:
{"errmsg":"Debug:...", "error":false, "newlyRegistered":false, "trialListId":"trial_1"}In practice, if you supplied a pid with the input you don't need any of the returned values, other than the error code. If uid with no pid was supplied on input, you need to save the pid to use it in the subsequent calls to /newEpisode or /mostRecentEpisode.
The response is in the same format as for /newEpisode, below. Check the values of display.finishCode to see the status of the game (continuing/cleared/stalemated/given up/early win), and of display.guessSaved to see if a guess has been recorded.
For the meaning of the output fields, see the documentation for NewEpisodeWrapper2. A field named foo will be described there under getFoo() or (for boolean values) isFoo().
Sample output; note that some fields have been moved from X to display.X. The fields within display.{...} are the same as those given by the /display and /move calls.
{"errmsg":"Debug:...", "error":false, "alreadyFinished":false, "display":{ "board":{"id":0,"value":[{"buckets":[3],"color":"BLUE","id":1,"shape":"SQUARE","x":5,"y":5}]},"code":-8,"errmsg":"Display requested\nDEBUG\n[0][20200827-153052-F66ITP; FC=1; M 2/2 $10][20200827-153246-R7LHH9; FC=1; M 3/3 $10]\n[1][20200827-160602-PYQTAJ; FC=1; M 2/2 $10][20200827-162713-2HRENR; FC=1; M 2/2 $10]\n*M*[2][20200905-120700-PDF966; FC=1; M 1/1 $10][20200905-121136-7L3EHY; FC=0; M 0/1 $0]\nR=$50", "finishCode":0, "numMovesMade":1, "bonus":false, "bonusEpisodeNo":0, "canActivateBonus":true, "episodeNo":1, "guessSaved":false, "seriesNo":2, "totalBoardsPredicted":2, "totalRewardEarned":50 "transcript":[{"pos":32,"bucketNo":3,"code":0}]} // All moves (and move attempts) - new in ver 1.014 }, "ruleSetName":"TD-03", "trialListId":"trial_list_1", "episodeId":"20200905-121136-7L3EHY", "para":{"clearing_threshold":1.3,"max_points":10,"b":1.5,"min_points":2,"max_colors":1,"f":4,"feedback_switches":"fixed","min_objects":1,"m":9,"n":1,"clear_how_many":2,"bonus_extra_pts":3,"rule_id":"TD-03","max_objects":3,"grid_memory_show_order":false,"min_shapes":1,"stack_memory_show_order":false,"max_shapes":1,"min_colors":1,"stack_memory_depth":6,"max_boards":2,"activate_bonus_at":2}}
display.transcript contains the description of all previous moves, as a vector. Each element has pos=the origin position (as an integer, numbered from 1=(1,1) to 36=(6,6), by row from left bottom corner), bucketNo=the destination bucket number, and the success code (same meaning as display.code)
Sample output
{"errmsg":"Debug...", // either used to describe errors, or contains various status info for debugging. "error":false, // true if something bad has happened "alreadyFinished":false, // this will be true if error==true, and the error is caused by the fact that the player has already done all his param sets "board":{"longId":0,"id":0,"value": [{"buckets":[0],"color":"RED","id":0,"shape":"STAR","x":3,"y":1}, {"buckets":[0],"color":"RED","id":0,"shape":"STAR","x":3,"y":5}]}, // description of the initial board "bonus":false, // true if this episode is part of a bonus subseries "bonusEpisodeNo":0, // the number of previous bonus episodes in this series "canActivateBonus":false, // true if you can put an "Activate Bonus" button on the screen in this episode "trialListId":"trial_list_1", Which trial list from the current experiment plan has been chosen for this player? "ruleSetName":"TD-03", The name of the rule set contained in the current parameter set. This is a file path name, relative to the main rule set directory "episodeId":"20200828-021427-FJRX9J", // Save this and use it in /move calls later! "para": {"clearing_threshold":1.3,"max_points":10,"b":1.5,"min_points":2, "max_colors":1,"f":4,"feedback_switches":"fixed","min_objects":1,"m":9,"n":1, "clear_how_many":2,"bonus_extra_pts":3,"rule_id":"TD-02","max_objects":3, "grid_memory_show_order":false,"min_shapes":1,"stack_memory_show_order":false, "max_shapes":1,"min_colors":1,"stack_memory_depth":6,"max_boards":2, "activate_bonus_at":2}, // All the parameters from the current param set // (the current row of the trial list file). Some of them may be used by the //GUI tool to control some points of the appearance "seriesNo":1, // the 0-based sequential number of this series (= 0-based sequential number of this row of the trial list file). // You can increment this number by 1 and display it in a "You are working on Rule No. X" message. "episodeNo":0, // the 0-based number of this episode within the series (i.e. among // the episodes played under a given rule set). // You can increment this number by 1 and display it as Y in a "Rule No. X, Episode No. Y" message. "totalRewardEarned":33 // the sum total of rewards earned by this player in all episodes so far }As of ver. 1.011, in the board description the field dropped is shown only in the pieces that have been already removed from the board; the value is the ientifying number (0 thru 3) of the bucket into which the piece has been dropped.
For the meaning of the output fields, see the documentation for ExtendedDisplay. A field named foo will be described there under getFoo() or (for boolean values) isFoo().
Sample output (in ver. 1.016). The new fields are ruleSrc and ruleLineNo.
{"bonus":false, "totalRewardEarned":0, "seriesNo":0,"episodeNo":0, "bonusEpisodeNo":0, "canActivateBonus":false, "totalBoardsPredicted":2, "guessSaved":false, "rulesSrc":{ // The rules of this game "orders": // array of orders. In this specific game (TD-1) one order is defined, but is not actually used ["Order Custom=[36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1]"], "rows": // array of rules, one element per line ["(1,*,*,*,[0,1,2,3])", "(*,*,*,*,[((p + 3) % 4)])"] }, "ruleLineNo":0, // which line of the rules set is currently active (0-based) "finishCode":0, "board":{"id":0,"value":[{"id":1,"color":"RED","shape":"SQUARE","x":6,"y":3,"buckets":[0,1,2,3]},{"id":0,"color":"RED","shape":"SQUARE","x":1,"y":1,"dropped":3,"buckets":[0,1,2,3]}]}, "code":-8, // It is always -8 on /display calls "errmsg":"null\nDEBUG\n*M*[0][20200910-005354-0JVTXF; FC=0; m 1/2 $0]\n[1]\n[2]\nR=$0","numMovesMade":1, "transcript":[{"pos":1,"bucketNo":3,"code":0}] }
{"board":{"id":0,"value":[{"buckets":[3],"color":"BLUE","dropped":3,"id":0,"shape":"SQUARE","x":5,"y":5}]}, "code":-8, "errmsg":"Display requested\nDEBUG\n[0][20200827-153052-F66ITP; FC=1; M 2/2 $10][20200827-153246-R7LHH9; FC=1; M 3/3 $10]\n[1][20200827-160602-PYQTAJ; FC=1; M 2/2 $10][20200827-162713-2HRENR; FC=1; M 2/2 $10]\n*B*[2][20200905-120700-PDF966; FC=1; M 1/1 $10][20200905-121136-7L3EHY; FC=1; B 1/1 $10]\nR=$60", // just a progress report for this session, handy for debugging "finishCode":1, "numMovesMade":1, "bonus":true, "bonusEpisodeNo":0, "canActivateBonus":false, "episodeNo":1, // sequential number in this series (0-based) "guessSaved":false, // has the guess been recorded for this episode? "seriesNo":2, // sequential number of this series in the trial list (0-based) "totalBoardsPredicted":3, // The current prediction for the total number of episodes to be played in this series. // During the main subserties, this is simply para.max_boards; during the bonus subseries, this is A+para.clear_how_many, // where A is the number of non-bonus episodes that had been played in this series before bonus was activated. "totalRewardEarned":60}
{"board":{"longId":0,"id":0,"value": [{"buckets":[0],"color":"RED","dropped":1,"id":0,"shape":"STAR","x":3,"y":1}, {"buckets":[0],"color":"RED","dropped":3,"id":0,"shape":"STAR","x":3,"y":5}]}, "bonus":false, // true if this episode is part of a bonus subseries "code":-8, // It is always -8 on /display calls "errmsg":"Display requested", "finishCode":1, // Tells if the episode still continues, has stalemated, // has been finished by clearing the board, or has been given up. // For the semantics of the code and finishCode fields, // see the entry on the MOVE command in the documentation on the Captive Game Server "numMovesMade":3, // the number of move attempts made so far in this episode "totalRewardEarned":0 // the sum total of rewards earned by this player in all episodes so far }
Two more fields added in ver. 2.007:
"trialListId":"trial_list_1", // Which trial list from the current experiment plan has been chosen for this player? "ruleSetName":"TD-03", //doc The name of the rule set contained in the current parameter set. This is a file path name, relative to the main rule set directoryIf the rule set file was specified in the param set as an absolute path, that's what you'll see here as well:
"ruleSetName":"/home/vmenkov/test/rules/rule-a.txt",
As of ver 4.005, there is support for the new incentive scheme, called DOUBLING. The output may look as follows (with the fields relevant for this incentive scheme shown in bold):
{"board":{"id":0,"value":[{"buckets":[0,1],"color":"YELLOW","id":2,"shape":"STAR","x":3,"y":3},{"buckets":[0,1],"color":"BLACK","id":3,"shape":"SQUARE","x":4,"y":3},{"buckets":[0,1],"color":"BLACK","id":4,"shape":"STAR","x":2,"y":4},{"buckets":[0,1],"color":"YELLOW","id":5,"shape":"STAR","x":5,"y":4},{"buckets":[0,1],"color":"BLACK","id":7,"shape":"SQUARE","x":4,"y":6},{"buckets":[0,1],"color":"RED","id":8,"shape":"TRIANGLE","x":6,"y":6},{"buckets":[],"color":"BLUE","dropped":3,"id":0,"shape":"SQUARE","x":4,"y":1},{"buckets":[],"color":"YELLOW","dropped":3,"id":1,"shape":"CIRCLE","x":3,"y":2},{"buckets":[],"color":"RED","dropped":0,"id":6,"shape":"SQUARE","x":3,"y":6}]}, "code":-8, "errmsg":"Display requested\nDEBUG\n*M*[S0][20211222-124942-JRNZOF; FC=1g; m 9/9 $10][20211222-130206-M51PK8; FC=0; m 3/9 $0]\n[S1]\n[S2]\nid=115, curSer=0 b=false, R=$10", "error":false, "explainCounters":"* / 1", "finishCode":0,"numMovesMade":3, "ruleLineNo":1, "rulesSrc":{"orders":[],"rows":["(1,*,*,*,[2, 3])","(1,*,*,*,[0, 1])"]}, "transcript":[{"code":0,"pieceId":0,"pos":4,"time":"2021-12-22T18:06:27.543Z[UTC]","bucketNo":3},{"code":0,"pieceId":6,"pos":33,"time":"2021-12-22T18:06:41.463Z[UTC]","bucketNo":0},{"code":0,"pieceId":1,"pos":9,"time":"2021-12-22T18:06:52.927Z[UTC]","bucketNo":3}], "bonus":false,"bonusEpisodeNo":0,"canActivateBonus":false, "episodeNo":1, "factorAchieved":2, "factorPromised":4, "incentive":"DOUBLING", "justReachedX2":false, "justReachedX4":false, "lastStretch":11, "rewardsAndFactorsPerSeries":[[10,2]], "guessSaved":false, "ruleSetName":"bottom_then_top", "seriesNo":0, "totalBoardsPredicted":5, "totalRewardEarned":10, "trialListId":"broad_clock_shape"}
For a detailed explanation of the incentive-related fields, see The DOUBLING incentive scheme.
Starting in ver. 4.008, this structure has one more field:
"rewardRange":[2,6],The value is the range within which the reward for the current episode could conceivably fall once episode is ended. The first value is the lower bound; this is what the player would earn if it will make an inordinately large number of mistakes before he completes the episode (i.e. clears the board or reaches a stalemate). The second value is the upper bound; this is what the player will earn if he completes the episode without making any more mistakes in addition to those he may have made already. (If the episode has been completed already, this field is still provided, and the upper bound of the range represents the actual reward for the episode). These reward numbers do not include any doubling/quadrupling factor that may attach to this episode (and the series) if the DOUBLING incentive scheme is in effect.
If the finishCode in the display structure has a value other than 0 (thus indicating that the episode has completed), the display structure will also have a transitionMap field. That fields contains a structure whose content is explained under /guess, below.
The response structure is nearly identical to that of /move, except that code contains a value that describes the accept/deny code to this move. For the meaning of the output fields, see the documentation for ExtendedDisplay. A field named foo will be described there under getFoo() or (for boolean values) isFoo().
Sample response
{"board":{"longId":0,"id":0,"value": [{"buckets":[1],"color":"BLUE","id":0,"shape":"TRIANGLE","x":4,"y":2}, {"buckets":[1],"color":"BLUE","dropped":3,"id":0,"shape":"TRIANGLE","x":2,"y":3}]}, // the board after this move "bonus":false, // true if this episode is part of a bonus subseries "code":4, Outcome of this move. For the semantics of the code and finishCode fields, see the entry on the MOVE command in the documentation on the Captive Game Server "errmsg":"...", // either used to describe errors, or contains various status info for debugging. "finishCode":0, Overall state of this episode now. For the semantics of the code and finishCode fields, see the entry on the MOVE command in the documentation on the Captive Game Server "numMovesMade":1, // the number of move attempts made so far in this episode "totalRewardEarned":42 // the sum total of rewards earned by this player in all episodes so far }
The finish code reports the current state: 0=can play (there are pieces on the board, and some can be moved), 1=finish (no pieces left on the board), 2=stalemate (there are pieces on the board, but none of them can be moved anymore).
{"errmsg":"Bonus activated successfully","error":false}
The finish code reports the current state: 0=can play (there are pieces on the board, and some can be moved), 1=finish (no pieces left on the board), 2=stalemate (there are pieces on the board, but none of them can be moved anymore).
{"errmsg":"Gave up series 0","error":false}
Response (on success):
{"error":false,"byteCnt":91,"path":"/opt/tomcat/saved/guesses/qt-01.csv"}
For the meaning of the output fields, see the documentation for GuessWriteReport. A field named foo will be described there under getFoo() or (for boolean values) isFoo().
The response contains a structure, transitionMap, which can be used to create a set of button offering the player possible further actions. The structure describes buttons in terms of key/value pairs, where the key tells where you'll get if you click the button, and the value tells how you can get there. Listed below are all possible key/value pairs, and their semantics.
Key | Value | How the button can be labeled | What the client can if after player clicks the button |
---|---|---|---|
MAIN | DEFAULT | Next episode | Call /newEpisode, which will produce another main-subseries (non-bonus) episode in the same series |
BONUS | DEFAULT | Next episode | Call /newEpisode, which will produce another bonus episode in the same series |
NEXT | DEFAULT | Next series | Call /newEpisode, which will automatically start the first episode of the next series (because no more episodes can be started in the current series) |
END | DEFAULT | Finish | The end of the last series, and of the experiment. Call /newEpisode. It will return error=true, allFinished=true, and completionCode, which the GUI client can then display to the player |
BONUS | ACTIVATE | Activate Bonus | Call /activateBonus, followed by /newEpisode (or simply call /newEpisode with activateBonus=true), which will start the first bonus episode in this series |
NEXT | GIVE_UP | Give up (and proceed to the next series) | Call /giveUp (which will switch series) followed by /newEpisode. (Or simply call /newEpisode with giveUp=true). The first episode of the next series will start |
END | GIVE_UP | Give up (and end the last series, and the experiment) | Call /newEpisode with giveUp=true. It will return error=true, allFinished=true, and completionCode, which the GUI client can then display to the player. |
A button with value="DEFAULT" is produced after every episode, indicating the default action (continuing in the same series, switching to the next series if the current series is done, or finishing one's work after the last series). A button with value="GIVE_UP" is produced after every episode other than the last episode of a series; it indicates the player's ability to end a series prematurely (and then go to the next series, if available). A button with value="ACTIVATE" is produced after main-subseries episodes if the player is eligibile to switch to ("activate") the bonus subseries.
{"RED":[220,20,0], "PINK":[220,100,100], "BLUE":[30,90,210], "errmsg":"No error", "BLACK":[0,0,0], "YELLOW":[250,240,0], "PURPLE":[200,0,200], "error":false, "GREEN":[0,250,0]}The hash table may also contain fields "error" and "errmsg". If an error has happened (e.g. the color map file on server could not be parsed), the error field will be set to true, and errmsg will contain an explanation of sorts.
{"error":false, "values":["cloud-lightning","moon","blank","cloud-off","sun","cloud","cloud-rain","cloud-drizzle","triangle","star","cloud-snow","arrows/arrow-up","arrows/arrow-left","arrows/arrow-down-left","arrows/arrow-down-right","arrows/arrow-up-left","arrows/arrow-right","arrows/arrow-up-right","arrows/arrow-down","square","circle"]}The shape names are given using the same case as in the file names. They are not case-sensitive, though.
{"error":true, "errmsg": "And this is why: ..."}
The "HTML play" can be used to play some episodes in the web browser, without the use of the client GUI app. This allows to test the Game Server functionality separately from that of the client GUI app.
Note: for technical reasons, in the HTML play the game pieces are drawn in black, and their assigned color (slightly "watered down", for better visibility) is indicated by the background of the cell. This is different from how the pieces appear in the GUI client.
For efficiency's sake, the Game Service tends to cache certain data, such as parsed rule sets or object property tables. If you modify a trial list file or a rule set file that already exists under /opt/tomcat/game-data, you may want to force the server to purge its stored tables, to ensure that your updated files will be re-read. If any players are playing at this time, clearing the table may cause a slight delay for them (as all necessary tables are re-read), but no disruption of their games.
You can use the experiment plan validation page to check your experiment plan's experiment control files for errors (syntax errors, references to non-existent files, attempts to generate boards with non-existence colors or shapes, etc). Or if you remember the name of your plan, you can do it right here; just enter the name of your experiment plan and click on "Submit".
In /opt/tomcat/game-data
In /opt/tomcat/saved