# Bot Assist

Updated 2025-01-27 for GS 8.041

This is document provides an overview of the "Bot Assist" feature, which was introduced in GS ver. 8.014. Originally, the entire trial list had to either have bot assist or not, but since ver 8.022, we have parameter-set-level granularity.

## Overview

The Bot Assist functionality is intended to work as follows. After each move (succesful or unsuccessful) by the player, the "bot" of some kind will provide him with a suggestion of what would be the next move to try. The suggestion can be delivered to the player via a "chat box", similar to that used in 2PG coop games.

The Bot Assist feature was originally designed for traditional single-player games (2PG); however, it is now available in two-player games (2PG) as well. In a 2PG, the bot assist capability needs to be configured separately for each player to which you can provide this service.

## Experiment preparation

See also: two-player games with a bot -- Experiment preparation

To enable Bot Assist in a 1PG, or to enable Bot Assist for Player 0 in a 2PG, you need to add to your trial list file a column named `bot_assist`, the value in which should indicate the name of the bot you want to use (typically, a wrapper over some AI algorithm). This should be chosen from the same set of bots that's also available for human-vs-bot games (i.e. adve and coop 2PGs with a bot partner), where the corresponding value is put in the column named `bot`.

The only bot included in GS 8.041 is `pseudo`, which stands for "pseudo learning".

In a 2PG, you can also enable Bot Assist for Player 1; to do that put the name of the bot algorithm into the column named `bot_assist1`.

In a 2PG, Bot Assist service provided to the two players independently from each other; i.e. each of the players to whom you're providing bot assist is served by his own separately configured bot. This means that you can choose to enable the bot for Player 0 only, for Player 1 only, or for both players. (Or for none of them, of course).

Additionally, the trial list file may include columns containing the necessary parameter values for the bot in question. See the "Bot algo parameters" secton below.

For an example, see the plan `vm/colorVshape-bot-assist`.

### Multiple series

In an experiment involving playing multiple series of episodes (typically, with a different rule set in each series), the bot (or bots) are configured independently in each parameter set of your trial list file. (A parameter set in GOHR controls one series of episodes). Thus you can construct, for example, a trial list file for a 2PG, in which in the first series (series 0) only Player 0 has bot assist, and in the next series, only plyayer 1 does.

### Bot algo parameters

As of ver. 8.041, if `bot_assist=pseudo`, the bot uses two optional columns in the parameter set, controlling the behaviour of the bot:

- `pseudo_halftime` (default=8.0): a non-negative real value that specifies how fast the bot pretends to learn, i.e. how fast the simulated error declines. The value `Infinity` is allowed; it is interpreted as positive infinity, i.e. the bot never learning (the error rate never declining from its initial value).
- `pseudo_init_error_rate` (default=0.75): the initial error rate.

(In a 2PG, if `bot_assist1=pseudo`, the bot for Player 1 is controlled by the parameters named `pseudo_halftime1` and `pseudo_init_error_rate1`, whose semantics is analogous to those described above).

At every step the bot computes the simulated error rate

Q = Q0 2-t/h = Q0 exp( - t*log(2)/h),
where t is the number of previous steps (the player's move attempts, excluding successful picks), h is `pseudo_halftime`, and Q0 is `pseudo_init_error_rate`.

Normally, the bot then will propose a bad move with probability Q, and a good move with probability 1-Q. An exception, of course, is a board state when all possible moves are good; in this case, by necessity, a good move will proposed.

Note that with the above formula the bot at some points may have a higher simulated error rate than a random player. For example, consider the rule set

    	    (count:1, bucket:[0,1])
    	    (count:1, bucket:[2,3])

where the player need to put game pieces, alternatingly, into an upper bucket (0 or 1) or into a lower bucket (2 or 3). In this game, a random player's error rate will always be 0.5; but pseudo-learning bot starting with a sufficiently high Q0 will have a higher simulated error rate during its first several proposed moves.

Note also that the same two parameters are used for configuring the bot partner / bot opponent in two-player games pairing a human and a bot.

## Example

Here's an example of an experiment plan in 2PG

    action trial-lists/vm> more adve.colorVshape-bot-assist/colorVshape.csv
    rule_id,max_boards,min_points,max_points,min_objects,max_objects,min_shapes,max_shapes,min_colors,max_colors,b,feedback_switches,stack_memory_depth,stack_memory_show_order,grid_memory_show_order,give_up_at,x2_likelihood,x4_likelihood,pregame,images,bot_assist,pseudo_halftime,pseudo_init_error_rate,bot_assist1,pseudo_halftime1,pseudo_init_error_rate1
    pk/shapeVcolor,2,2,10,4,6,2,4,2,4,1.5,free,10,TRUE,TRUE,0,100,10000,doubling,pk/shapeVcolor/*.png,pseudo,10,0.90,,,
    FDCL/basic/cw,2,2,10,4,6,2,4,2,4,1.5,free,10,TRUE,TRUE,0,100,10000,doubling,,,,,pseudo,10,0.90
    FDCL/basic/ccw,2,2,10,4,6,2,4,2,4,1.5,free,10,TRUE,TRUE,0,100,10000,doubling,,pseudo,10,0.90,pseudo,20,0.40

As you can see, in the first series, only Player 0 receives bot assistance, in the second series, only Player 1 does, and in the thrid series, both do.

## Older algorithm (used before ver 8.025)

The one column that the bot `pseudo` uses is named `pseudo_halftime`; it contains an integer value that specifies how fast the bot pretends to learn. That is, at every step the bot can elect to either choose a perfectly random move (which may or may not be good), or choose some guaranteed good move (picking it from the set of all possible good moves). The probability of the first election is decaying exponetially; thus, when the bot starts with the rule set, it will choose a move randomly; after `pseudo_halftime` moves it's fifty-fifty chance whether the bot will choose your move from the set of all (good or bad) possible move attempts, or from the set of all possible good moves.

Mathematically, let Q be the exponentially decaying value with the initial value 1 and the specified half-life time h: Q = 2^{-t/h) = exp( - t*log(2)/h). Then, at step t, we will make this choice:

- with probability Q, we will carry out a truly random selection of a move, and will return that move.
- with probabiliy 1-Q, we will select a guaranteed good move.

Or, in other words, if for a random player the probability of a good move is p(t), then the probability of a good move by the bot is

p = (1-Q)+ Q*p(t) = 1 - Q *(1-p(t)).

As to the "confidence" value printed by the bot, it is the same as p above, and is computed as follows:

p(t) = goodCnt/allCnt,
where goodCnt is the number of possible good moves and allCnt is the number of all moves (good or bad) that can be chosen.
confidence = 1 - Q * (1-p(t))

## Using a dynamic plan

You can take any 1PG experiment plan (your "base plan") and use it for a 1PG play with bot assist. To do that: go to the launch form, scroll down to Form 2, and enter a P:-type dynamic plan name which will include the name of your "base plan" and the name of a modifier. For example, if your base plan is `FDCL/basic`, and you want to use the modifier `bot-assist/pseudo-10` (whose file is in `/opt/w2020/game-data/modifiers/bot-assist/pseudo-10.csv`, you will enter `P:FDCL/basic:bot-assist/pseudo-10` in the plan name box.

The way P:-type dynamic plan works, the columns from the modifier file (which, in this case, specify the bot type and parameters) are virtually pasted at the end of all lines of the base plan's trial list files, thus adding the bot assist feature to your base plan.

## Issues for the GUI client

This section contains some guidance for implementing support for the "Bot Assist" feature in the GUI Client.

1) The data fields discussed below are available in the Game Server 8.014, currently running on action.rutgers.edu/w2020-dev

2) The experiment plan you can use for testing is vm/colorVshape-bot-assist. It's a normal 1PG plan, but it computes, after each /move or /pick, a "suggestion by bot", which is sent to the player as part of the /move or /pick return structure; the client can then show that suggestion to the player in the chat box.

3) [Note: this section has been updated for ver 8.022] The GUI client may want to find out, at the very beginning of the game, that it's a "bot assist" game, when it starts playing each series of the session (i.e., each new parameter set with a new rule set) whether that parameter set includes a "bot assist" feature, so that it can choose to display the chat panel (which normally is not needed in 1PG games). To do that, the client shall look at the return value of the first /newEpisode call of each session; inside that value, there is a "para" structure; if that structure has a field named "bot_assist" with a non-null value, you now you're in a bot assiste game. For example (quoting from catalina.out):
`20-May-2025 02:03:53.700 INFO [http-nio-1234-exec-191] edu.wisc.game.util.Logging.info NewEpisodeWrapper2(pid=vm-2025-05-18-b): returning: {"mustWait":false,"episodeId":"20250520-020353-BT3DX2", "para":{"max_points":10,"b":1.5,"min_points":2,"max_colors":4,"feedback_switches":"free","pseudo_halftime":10,"min_objects":4,"rule_id":"pk/shapeVcolor","x4_likelihood":10000,"max_objects":6,"grid_memory_show_order":true,"min_shapes":2,"bot_assist":"pseudo","pregame":"doubling","x2_likelihood":100,"give_up_at":0,"stack_memory_show_order":true,"max_shapes":4,"min_colors":2,"stack_memory_depth":10,"max_boards":4}, "alreadyFinished":false,"completionMode":0,"display":{"mover":0,"mustWait":false,"bonus":false,"totalRewardEarned":0,"totalRewardEarnedPartner":0,"seriesNo":0,"displaySeriesNo":0,"episodeNo":0, "displayEpisodeNo":0,"bonusEpisodeNo":0,"canActivateBonus":false,"totalBoardsPredicted":4,"guessSaved":false,"rewardRange":[2,10],"trialListId":"colorVshape","ruleSetName":"pk/shapeVcolor","incentive":"LIKELIHOOD","lastStretch":0,"lastR":0.0,"faces":"TOO_DEEP","facesMine":"TOO_DEEP","rewardsAndFactorsPerSeries":[[0,1]],"justReachedX2":false,"justReachedX4":false,"factorAchieved":1,"factorPromised":0,"finishCode":0,"board":"TOO_DEEP","code":-8,"errmsg":"Display requested....". ...}`

I reckon you may feel that the first /newEpisode call is a tad too late to learn about the peculiarities of the game. In that case, you can pull that info from the response of the /player call as well, since it includes all parameter sets too. You can just look at the first one. For example:
`20-May-2025 02:03:53.589 INFO [http-nio-1234-exec-190] edu.wisc.game.util.Logging.info PlayerResponse(pid=vm-2025-05-18-b, exp=vm/colorVshape-bot-assist), returning: {"newlyRegistered":true,"trialListId":"colorVshape", "trialList":[{"max_points":10,"b":1.5,"min_points":2,"max_colors":4,"feedback_switches":"free","pseudo_halftime":10,"min_objects":4,"rule_id":"pk/shapeVcolor","x4_ likelihood":10000,"max_objects":6,"grid_memory_show_order":true,"min_shapes":2,"bot_assist":"pseudo","pregame":"doubling","x2_likelihood":100,"give_up_at":0,"stack_memory_show_order":true,"max_shapes":4,"min_colors" :2,"stack_memory_depth":10,"max_boards":4},{"max_points":10,"b":1.5,"min_points":2,"max_colors":4,"feedback_switches":"free","pseudo_halftime":10,"min_objects":4,"rule_id":"FDCL/basic/cw","x4_likelihood":10000,"max_ objects":6,"grid_memory_show_order":true,"min_shapes":2,"bot_assist":"pseudo","pregame":"doubling","x2_likelihood":100,"give_up_at":0,"stack_memory_show_order":true,"max_shapes":4,"min_colors":2,"stack_memory_depth" :10,"max_boards":4},{"max_points":10,"b":1.5,"min_points":2,"max_colors":4,"feedback_switches":"free","pseudo_halftime":10,"min_objects":4,"rule_id":"FDCL/basic/ccw","x4_likelihood":10000,"max_objects":6,"grid_memor y_show_order":true,"min_shapes":2,"bot_assist":"pseudo","pregame":"doubling","x2_likelihood":100,"give_up_at":0,"stack_memory_show_order":true,"max_shapes":4,"min_colors":2,"stack_memory_depth":10,"max_boards":4}]," playerId":"vm-2025-05-18-b","alreadyFinished":false,"completionMode":0, "experimentPlan":"vm/colorVshape-bot-assist", "isCoopGame":false,"isAdveGame":false,"needChat":false,"error":false,"errmsg":"Debug:\n(PlayerInfo: id=1503, playerId=vm-2025-05-18-b, pair=0, trialListId=colorVshape, date=Tue May 20 02:03:53 EDT 2025)\n*M*[S0]\n[S1]\n[S2]\nid=1503, curSer=0 b=false, R=$0"}`

In any event, once the client learns that the series to be played is a bot assist game, it should enable the screen to display the chat panel. Since there seems to be no reason why the human player may say anything useful to the bot (my bots are dumb and don't understand any words anyway), I would suggest that unlike in a coop 2PG game, this chat panel should not allow the user to enter any text. (Maybe there should be no entry box at all, to avoid distracting the player).

4) The buckets are numbered clockwise, 0 1 2 3, starting with the top left one. In a bot assist game, some kind of labels indicating that numbering, should be shown at or near the buckets.

5) In the return structures of all calls (/move, /pick, /display) that include a board description, each game piece will have, along with the field "id", also the field "label", whose value will be a string to be displayed somewhere on or near the game piece. For example:
`20-May-2025 02:03:59.755 INFO [http-nio-1234-exec-193] edu.wisc.game.util.Logging.info /display(20250520-020353-BT3DX2) returning: {"mover":0,"mustWait":false,"bonus":false,"totalRewardEarned":0,"totalRewardEarnedPartner":0,"seriesNo":0,"displaySeriesNo":0,"episodeNo":0,"displayEpisodeNo":0,"bonusEpisodeNo":0,"canActivateBonus":false,"totalBoardsPredicted":4,"guessSaved":false,"rewardRange":[2,9],"trialListId":"colorVshape","ruleSetName":"pk/shapeVcolor","incentive":"LIKELIHOOD","lastStretch":0,"lastR":0.0,"faces":[true,false],"facesMine":[true,true],"rewardsAndFactorsPerSeries":[[0,1]],"justReachedX2":false,"justReachedX4":false,"factorAchieved":1,"factorPromised":0,"finishCode":0, "board":{"id":0,"value":[{"id":0,"color":"yellow","shape":"triangle","x":3,"y":3,"image":"pk/shapeVcolor/triangle_yellow.png","label":"@","buckets":[1,2]},{"id":1,"color":"blue","shape":"triangle","x":5,"y":3,"image":"pk/shapeVcolor/triangle_blue.png","label":"A","buckets":[2,3]},{"id":2,"color":"yellow","shape":"square","x":6,"y":3,"image":"pk/shapeVcolor/square_yellow.png","label":"B","buckets":[0,1]},{"id":3,"color":"yellow","shape":"square","x":6,"y":4,"image":"pk/shapeVcolor/square_yellow.png","label":"C","buckets":[0,1]},{"id":4,"color":"blue","shape":"square","x":1,"y":6,"dropped":0,"image":"pk/shapeVcolor/square_blue.png","label":"D","buckets":[]}]}, "code":-8,"errmsg":"Display requested...", ....}`

6) The return value of a /move or /pick call may contain the field named "botAssistChat". If that field is present and has a non-null value, that value should be displayed as a message in the chat box. For example:
`20-May-2025 02:03:58.557 INFO [http-nio-1234-exec-188] edu.wisc.game.util.Logging.info /move(epi=20250520-020353-BT3DX2, (3,3) to (0,0), cnt=1), return {"mover":0,"mustWait":false,"bonus":false,"totalRewardEarned":0,"totalRewardEarnedPartner":0,"seriesNo":0,"displaySeriesNo":0,"episodeNo":0,"displayEpisodeNo":0,"bonusEpisodeNo":0,"canActivateBonus":false,"totalBoardsPredicted":4,"guessSaved":false,"rewardRange":[2,9],"trialListId":"colorVshape","ruleSetName":"pk/shapeVcolor","incentive":"LIKELIHOOD","lastStretch":0,"lastR":0.0,"faces":[true,false],"facesMine":[true,true],"rewardsAndFactorsPerSeries":[[0,1]],"justReachedX2":false,"justReachedX4":false,"factorAchieved":1,"factorPromised":0, "botAssistChat":"I suggest moving piece A to bucket 3", "finishCode":0,"board":{"id":0,"value":[{"id":0,"color":"yellow","shape":"triangle","x":3,"y":3,"image":"pk/shapeVcolor/triangle_yellow.png","label":"@","buckets":[1,2]},{"id":1,"color":"blue","shape":"triangle","x":5,"y":3,"image":"pk/shapeVcolor/triangle_blue.png","label":"A","buckets":[2,3]},{"id":2,"color":"yellow","shape":"square","x":6,"y":3,"image":"pk/shapeVcolor/square_yellow.png","label":"B","buckets":[0,1]},{"id":3,"color":"yellow","shape":"square","x":6,"y":4,"image":"pk/shapeVcolor/square_yellow.png","label":"C","buckets":[0,1]},{"id":4,"color":"blue","shape":"square","x":1,"y":6,"dropped":0,"image":"pk/shapeVcolor/square_blue.png","label":"D","buckets":[]}]},"code":4,"errmsg":"null\nDEBUG\n(PlayerInfo: id=1503, playerId=vm-2025-05-18-b, pair=0, trialListId=colorVshape, date=Tue May 20 02:03:53 EDT 2025)\n*M*[S0][20250520-020353-BT3DX2; FC=0; x0:0) 2/5 $0:0]\n[S1]\n[S2]\nid=1503, curSer=0 b=false, R=$0","error":false,"numMovesMade":2,"transcript":[{"bucketNo":0,"pieceId":4,"pos":31,"code":0,"rValue":2.0,"mover":0},{"bucketNo":3,"pieceId":0,"pos":15,"code":4,"rValue":2.0,"mover":0}],"recentKnowledge":{"15":{"pieceId":0,"pos":15,"knownMovable":true,"knownImmovable":false,"deniedBuckets":[3]}},"recentKnowledge2":{"0":{"pieceId":0,"pos":15,"knownMovable":true,"knownImmovable":false,"deniedBuckets":[3]}},"rulesSrc":{"orders":[],"rows":["(*,SQUARE,*,*,0) (*,CIRCLE,*,*,1) (*,TRIANGLE,*,*,2) (*,STAR,*,*,3) (*,*,RED,*,0) (*,*,YELLOW,*,1) (*,*,GREEN,*,2) (*,*,BLUE,*,3)"]},"explainCounters":"* / *,*,*,*,*,*,*,*","ruleLineNo":0}`

## The pseudo-learning bot

For details on how this bot works, please see the section on the pseudo-learning bot in the document on the 2PG with a bot partner.

## Recording bot's suggestions

When a game with bot assist is played, the game server, in addition to recording the player's moves in the transcript file, also records the bot's suggestions in the bot assist suggestions file. These files are located in the directory `bot-assist` under the main saved data directory (typically, `/opt/w2020/saved`), and have names such as `player-id.0.bot-assist.csv` `player-id.1.bot-assist.csv`. The suffix "0" or "1" indicates which player (Player 0 or Player 1) received suggestions. In a 2PG, of course, the suffix is always 0.

The file format of bot assiste suggestions files is identical to that of the transcript files, describing the proposed move in terms of the game piece ID and the destination bucket ID. Since both the entries in the game's transcript file and those in the bot assist file contain time stamps, it should be possible for an analysis script to match the two, and to figure which bot suggestion was offered before which move of the player.

Note that if in a particular trial list only some parameter sets involve bot assist, the bot assist transcript will be shorter than the main game transcript, because the former will only have sections for the episodes played with bot assist.

Additionally, when at least one parameter set in the trial list has a bot assist feature, the main game transcript file will have an additional column named "followed". In those episodes where bot assist was active, this column will contain 1 or 0, depending on whether the player's move followed the bot's suggestion or not. If some parameter sets in the trial list did not have bot assist, this column will be empty for the moves made when playing that para set.

    bixi-Latitude-E5420:/opt/w2020/saved/transcripts> more vm-2025-06-23-b.transcripts.csv
    #pid,episodeId,moveNo,timestamp,mover,objectId,y,x,by,bx,code,followed
    vm-2025-06-23-b,20250623-235052-9L4794,0,20250623-235347.867,0,5,6,4,7,7,4,
    vm-2025-06-23-b,20250623-235052-9L4794,1,20250623-235350.440,0,5,6,4,,,0,
    vm-2025-06-23-b,20250623-235052-9L4794,2,20250623-235352.555,0,5,6,4,7,0,0,
    vm-2025-06-23-b,20250623-235052-9L4794,3,20250623-235355.439,0,4,5,6,7,7,4,
    vm-2025-06-23-b,20250623-235052-9L4794,4,20250623-235358.072,0,3,3,6,7,7,0,
    vm-2025-06-23-b,20250623-235052-9L4794,5,20250623-235406.218,0,4,5,6,0,7,4,
    vm-2025-06-23-b,20250623-235052-9L4794,6,20250623-235408.877,0,4,5,6,7,0,0,
    vm-2025-06-23-b,20250623-235052-9L4794,7,20250623-235414.255,0,1,1,5,0,7,0,
    vm-2025-06-23-b,20250623-235052-9L4794,8,20250623-235417.574,0,0,1,2,0,0,0,
    vm-2025-06-23-b,20250623-235052-9L4794,9,20250623-235424.960,0,2,3,2,7,7,0,
    vm-2025-06-23-b,20250623-235428-V2CRGY,0,20250623-235432.319,0,5,5,4,7,7,0,
    vm-2025-06-23-b,20250623-235428-V2CRGY,1,20250623-235435.015,0,1,3,4,7,7,0,
    vm-2025-06-23-b,20250623-235428-V2CRGY,2,20250623-235438.005,0,4,5,1,7,7,0,
    vm-2025-06-23-b,20250623-235428-V2CRGY,3,20250623-235441.488,0,3,4,6,0,7,0,
    vm-2025-06-23-b,20250623-235428-V2CRGY,4,20250623-235444.621,0,2,4,3,0,7,0,
    vm-2025-06-23-b,20250623-235428-V2CRGY,5,20250623-235448.059,0,0,2,2,0,7,0,
    vm-2025-06-23-b,20250623-235451-6ZW7I7,0,20250623-235456.199,0,4,4,2,7,0,0,0
    vm-2025-06-23-b,20250623-235451-6ZW7I7,1,20250623-235458.245,0,5,5,5,7,7,0,0
    vm-2025-06-23-b,20250623-235451-6ZW7I7,2,20250623-235500.349,0,3,3,6,0,7,0,0
    vm-2025-06-23-b,20250623-235451-6ZW7I7,3,20250623-235502.617,0,2,2,6,,,0,0
    vm-2025-06-23-b,20250623-235451-6ZW7I7,4,20250623-235506.848,0,0,1,1,0,0,0,0
    vm-2025-06-23-b,20250623-235451-6ZW7I7,5,20250623-235510.909,0,1,2,3,,,0,0
    vm-2025-06-23-b,20250623-235451-6ZW7I7,6,20250623-235516.362,0,1,2,3,7,0,0,0
    vm-2025-06-23-b,20250623-235451-6ZW7I7,7,20250623-235519.691,0,2,2,6,7,7,0,0
    vm-2025-06-23-b,20250623-235522-CLFANT,0,20250623-235524.977,0,0,1,2,0,0,0,0
    vm-2025-06-23-b,20250623-235522-CLFANT,1,20250623-235527.554,0,2,5,2,7,0,0,0
    vm-2025-06-23-b,20250623-235522-CLFANT,2,20250623-235530.451,0,3,5,3,7,7,0,0
    vm-2025-06-23-b,20250623-235537-ZP2QZ2,0,20250623-235540.405,0,2,5,2,7,0,0,
    vm-2025-06-23-b,20250623-235537-ZP2QZ2,1,20250623-235543.160,0,0,2,1,0,0,0,
    vm-2025-06-23-b,20250623-235537-ZP2QZ2,2,20250623-235545.303,0,1,2,6,0,7,0,
    vm-2025-06-23-b,20250623-235537-ZP2QZ2,3,20250623-235548.196,0,3,5,3,7,7,0,
    vm-2025-06-23-b,20250623-235537-ZP2QZ2,4,20250623-235550.693,0,4,6,3,7,0,0,
    vm-2025-06-23-b,20250623-235553-ZOWIQ7,0,20250623-235556.246,0,0,3,6,0,7,0,
    vm-2025-06-23-b,20250623-235553-ZOWIQ7,1,20250623-235558.367,0,3,6,6,7,7,0,
    vm-2025-06-23-b,20250623-235553-ZOWIQ7,2,20250623-235600.790,0,2,6,5,7,0,0,
    vm-2025-06-23-b,20250623-235553-ZOWIQ7,3,20250623-235603.691,0,1,5,6,0,0,0,