This is a documentation for Board Game Arena: play board games online !

BGA Undo policy: Difference between revisions

From Board Game Arena
Jump to navigation Jump to search
No edit summary
(12 intermediate revisions by 5 users not shown)
Line 22: Line 22:
Undo are painful for opponents, in real world and online. Some players may also use Undo to "test" situations while they should just think instead.
Undo are painful for opponents, in real world and online. Some players may also use Undo to "test" situations while they should just think instead.


In many case, proposing an "Undo" means also adding an extra step, which is not good for the game interface.
In many cases, proposing an "Undo" means also adding an extra step, which is not good for the game interface.


Undo may be useful in 2 main situations:
Undo may be useful in 2 main situations:
* To allow players to take back a move following a "misclick" that may ruins their game.
* To allow players to take back a move following a "misclick" that may ruin their game.
* To allow players to take back a very complex series of actions.
* To allow players to take back a very complex series of actions.


== About Undo to avoid "misclick" ==
== About Undo to avoid "misclick" ==


Most of the case, you don't need this: players must pay attention :)
I most cases, you don't need this: players must pay attention :)


However, if several of the following cases correspond to your situation, you may consider adding an Undo for "misclicks":
However, if several of the following cases correspond to your situation, you may consider adding an Undo for "misclicks":


* If the '''zone to click is very small''', or close to other zones, misclicks may happened frequently. Note that if this only happens on mobile, you can propose the Undo to mobile users only.
* If the '''zone to click is very small''', or close to other zones, misclicks may happened frequently. Note that if this only happens on mobile, you can propose the Undo to mobile users only.
* If '''there is already another step''' after the step you want to undo. In this case, adding an "Undo" do not add an extra step.
* If '''there is already another step''' after the step you want to undo. In this case, adding an "Undo" does not add an extra step.
* If you are in the middle of a real "game action". For example, on Chess, this is normal to allow to undo the selection of a piece in order to select another one, as the move hasn't been done already.
* If you are in the middle of a real "game action". For example, on Chess, it is normal to allow to undo the selection of a piece in order to select another one, as the move hasn't been done already.
* The action done by the player is obviously stupid (in this case, please also consider adding a confirmation before the action).
* The action done by the player is obviously a mistake (in this case, please also consider adding a confirmation before the action).


On the contrary, you must be reluctant to add an Undo if:
On the contrary, you must be reluctant to add an Undo if:
* This adds an extra step.
* This adds an extra step.
* The game action is clear and the zone to click is very large.
* The game action is clear and the zone to click is large.


To conclude: we may allow a player to undo a move caused by a "misclick", but we shouldn't allow a player to undo a bad move he plays conscientiously.
To conclude: we may allow a player to undo a move caused by a "misclick", but we shouldn't allow a player to undo a bad move he plays consciously.
 
Note: not all of these cituations require server undo, in chess example above Undo of selection - you don't need to send piece selection to the server you can store it locally (this is "client side undo"), there is no Undo button in this case - user just selects another piece. Similary if you add prompt for clicking on critical buttons like "Pass" you can add extra prompt, but no Undo


== About Undo to take back moves from a complex series of actions ==
== About Undo to take back moves from a complex series of actions ==
Line 51: Line 53:
In most games, you shouldn't provide an undo because players are not allowed to "explore" the different possibilities by playing and undoing their moves.
In most games, you shouldn't provide an undo because players are not allowed to "explore" the different possibilities by playing and undoing their moves.


However, in some games, this is commonly admitted that players are allowed to take back a move. The typical example is a complex strategy game with a long serie of actions during your turn. "Through the Ages" or "Tzolk'in" are 2 good examples.
However, in some games, it is commonly admitted that players are allowed to take back a move. The typical example is a complex strategy game with a long serie of actions during your turn. "Through the Ages" or "Tzolk'in" are 2 good examples.


To know if this is a good idea to propose an undo, ask yourself the question: "when playing in real life, will my opponents authorize me to take back this move?". Depending on your answer, you should propose an Undo, do not propose an Undo, or maybe propose an Undo as an option for the game.
To know if this is a good idea to propose an undo, ask yourself the question: "when playing in real life, will my opponents authorize me to take back this move?". Depending on your answer, you should choose to provide an Undo, to not provide an Undo, or maybe to propose an Undo as an option for the game.
 
Note: in this cituation it is also possible to use client side undo, instead of server side, see "Russian Railroads" for example (implemented using setClientState method).


= How to implement Undo =
= How to implement Undo =
There are 2 ways to implement Undo:
* The framework undoSavePoint/undoRestorepoint methods, [[Main_game_logic:_yourgamename.game.php#Undo_moves | documented here]].
* Your own method.
As a rule of thumb, it is way better to use your own method to provide an Undo. The reasons are:
* The framework method is quite heavy to use (it saves the WHOLE situation). To cancel a simple move, it is way more efficient for you to execute the opposite move.
* The framework method is doing a full "reset" of the game interface. It is way more smooth and clear to play the opposite move from a player point of view.
* The framework Undo provides only ONE save point. If you want to provide a complex Undo (ie: the possibility to undo all the previous steps one by one), you must implement it by yourself.
However, the framework is very handy to use (only 2 methods), so this should be your first choice if some moves are very difficult to reverse on client side.
== How does undoSavePoint/undoRestorepoint works? ==
The undoSavePoint method makes a full copy of the game database state.
The undoRestorePoint method:
* restores the game database
* removes all your game HTML + the player panels
* restores the game HTML + the player panels according to their state at the initial game loading
* calls your "setup" method
'''Important''': because undoRestorePoint is calling your "setup" method, you must ensure that your whole game state can be restored from there. In particular, all your member variables must be initialized inside the "setup" method (and not only in the constructor !).
== Adding simple Undo actio to your game ==
=== Step 1: Declare in gameinfos ===
Add  'db_undo_support' => true in gamesinfo.inc.php
Note: if you have game in studio you must reload gameinfos and restart all these games
=== Step 2: Insert savepoint ===
Determine where you want to save the undo state. Must be single active player state
* Begging of new user turn
* Every time new information revealed such as
** dice roll
** card is drawn
** anything else hidden is shown
Insert
$this->undoSavepoint();
=== Step 3: Create undo action in server ===
Add players undo action
in game.action.php:
    public function undo() {
        self::setAjaxMode();
        $this->game->action_undo();
        self::ajaxResponse();
    }
in game.game.php
    function action_undo() {
        $this->undoRestorePoint(); // do not do checkAction - its on purpose - do not need to add undo as possible action to every state
    }
=== Step 4: Create undo action in client ===
in game.js
add this to onUpdateActionButton regardless of state but inside isCurrenPlayerAction
<pre>
this.addActionButton('button_undo', _('Undo'), 'onUndo', undefined, undefined, 'red');
</pre>
and onUndo would be like this
<pre>
    onUndo: function (event) {
      const action = "undo";
      this.ajaxcall(
        "/" + this.game_name + "/" + this.game_name + "/" + action + ".html",
        [],
        this,
        (result) => {},
        (err) => {
          if (err) return;
          this.setMainTitle(_("Undo requested..."));
          dojo.empty("generalactions");
        },
        true
      );
    },
</pre>
=== Step 5: Make it fancy ===
If you want you can add extra styling to the Undo button such as it will appeat at right of all buttons
in game.css
<pre>
#button_undo {
    float: right;
    background-color: red;
    background-image: none;
}
</pre>
[[Category:Studio]]

Revision as of 02:37, 29 January 2023

Introducing "Undo" features in games can lead to bugs and/or bad interface design.

Please read the following carefully before implementing an Undo feature for your game.


What you must NEVER do

When you are restoring a game situation A from a game situation B:

  • The Undo action must never change the active player. In other words, if player X clicks on "Undo", it must NOT make player Y active.
  • If several players did game actions between A and B, you must NEVER provide an undo. In other words, an "Undo" action must NEVER force another player to redo some moves.
  • No hidden (or private) information must have been revealed between A and B.
  • No random event with a visible effect must have been triggered between A and B. This includes cards shuffling, dice roll, elements picking, ...


When to propose to Undo?

As a rule of thumb, on BGA we advise you to not undo moves.

Undo are painful for opponents, in real world and online. Some players may also use Undo to "test" situations while they should just think instead.

In many cases, proposing an "Undo" means also adding an extra step, which is not good for the game interface.

Undo may be useful in 2 main situations:

  • To allow players to take back a move following a "misclick" that may ruin their game.
  • To allow players to take back a very complex series of actions.

About Undo to avoid "misclick"

I most cases, you don't need this: players must pay attention :)

However, if several of the following cases correspond to your situation, you may consider adding an Undo for "misclicks":

  • If the zone to click is very small, or close to other zones, misclicks may happened frequently. Note that if this only happens on mobile, you can propose the Undo to mobile users only.
  • If there is already another step after the step you want to undo. In this case, adding an "Undo" does not add an extra step.
  • If you are in the middle of a real "game action". For example, on Chess, it is normal to allow to undo the selection of a piece in order to select another one, as the move hasn't been done already.
  • The action done by the player is obviously a mistake (in this case, please also consider adding a confirmation before the action).

On the contrary, you must be reluctant to add an Undo if:

  • This adds an extra step.
  • The game action is clear and the zone to click is large.

To conclude: we may allow a player to undo a move caused by a "misclick", but we shouldn't allow a player to undo a bad move he plays consciously.

Note: not all of these cituations require server undo, in chess example above Undo of selection - you don't need to send piece selection to the server you can store it locally (this is "client side undo"), there is no Undo button in this case - user just selects another piece. Similary if you add prompt for clicking on critical buttons like "Pass" you can add extra prompt, but no Undo

About Undo to take back moves from a complex series of actions

Games with a complex serie of actions are an "exception" to BGA Undo policy.

In most games, you shouldn't provide an undo because players are not allowed to "explore" the different possibilities by playing and undoing their moves.

However, in some games, it is commonly admitted that players are allowed to take back a move. The typical example is a complex strategy game with a long serie of actions during your turn. "Through the Ages" or "Tzolk'in" are 2 good examples.

To know if this is a good idea to propose an undo, ask yourself the question: "when playing in real life, will my opponents authorize me to take back this move?". Depending on your answer, you should choose to provide an Undo, to not provide an Undo, or maybe to propose an Undo as an option for the game.

Note: in this cituation it is also possible to use client side undo, instead of server side, see "Russian Railroads" for example (implemented using setClientState method).

How to implement Undo

There are 2 ways to implement Undo:

  • The framework undoSavePoint/undoRestorepoint methods, documented here.
  • Your own method.

As a rule of thumb, it is way better to use your own method to provide an Undo. The reasons are:

  • The framework method is quite heavy to use (it saves the WHOLE situation). To cancel a simple move, it is way more efficient for you to execute the opposite move.
  • The framework method is doing a full "reset" of the game interface. It is way more smooth and clear to play the opposite move from a player point of view.
  • The framework Undo provides only ONE save point. If you want to provide a complex Undo (ie: the possibility to undo all the previous steps one by one), you must implement it by yourself.

However, the framework is very handy to use (only 2 methods), so this should be your first choice if some moves are very difficult to reverse on client side.

How does undoSavePoint/undoRestorepoint works?

The undoSavePoint method makes a full copy of the game database state.

The undoRestorePoint method:

  • restores the game database
  • removes all your game HTML + the player panels
  • restores the game HTML + the player panels according to their state at the initial game loading
  • calls your "setup" method

Important: because undoRestorePoint is calling your "setup" method, you must ensure that your whole game state can be restored from there. In particular, all your member variables must be initialized inside the "setup" method (and not only in the constructor !).

Adding simple Undo actio to your game

Step 1: Declare in gameinfos

Add 'db_undo_support' => true in gamesinfo.inc.php

Note: if you have game in studio you must reload gameinfos and restart all these games

Step 2: Insert savepoint

Determine where you want to save the undo state. Must be single active player state

  • Begging of new user turn
  • Every time new information revealed such as
    • dice roll
    • card is drawn
    • anything else hidden is shown

Insert

$this->undoSavepoint();

Step 3: Create undo action in server

Add players undo action in game.action.php:

   public function undo() {
       self::setAjaxMode();
       $this->game->action_undo();
       self::ajaxResponse();
   }

in game.game.php

   function action_undo() {
       $this->undoRestorePoint(); // do not do checkAction - its on purpose - do not need to add undo as possible action to every state
   }

Step 4: Create undo action in client

in game.js add this to onUpdateActionButton regardless of state but inside isCurrenPlayerAction

			this.addActionButton('button_undo', _('Undo'), 'onUndo', undefined, undefined, 'red');

and onUndo would be like this

    onUndo: function (event) {
      const action = "undo";
      this.ajaxcall(
        "/" + this.game_name + "/" + this.game_name + "/" + action + ".html",
        [],
        this,
        (result) => {},
        (err) => {
          if (err) return;
          this.setMainTitle(_("Undo requested..."));
          dojo.empty("generalactions");
        },
        true
      );
    },

Step 5: Make it fancy

If you want you can add extra styling to the Undo button such as it will appeat at right of all buttons in game.css

#button_undo {
    float: right;
    background-color: red;
    background-image: none;
}