<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://en.doc.boardgamearena.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=RichardSPeters</id>
	<title>Board Game Arena - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://en.doc.boardgamearena.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=RichardSPeters"/>
	<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/Special:Contributions/RichardSPeters"/>
	<updated>2026-10-01T12:42:58Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.39.0</generator>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Tutorial_reversi&amp;diff=29211</id>
		<title>Tutorial reversi</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Tutorial_reversi&amp;diff=29211"/>
		<updated>2026-03-30T20:11:17Z</updated>

		<summary type="html">&lt;p&gt;RichardSPeters: removing confusion re PlayerTurn and PlayDisc (php and js)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Using this tutorial, you can build a complete working game on the BGA environment: Reversi.&lt;br /&gt;
&lt;br /&gt;
Before you read this tutorial, you must:&lt;br /&gt;
* Read the overall presentations of the BGA Framework ([[Studio|see here]]).&lt;br /&gt;
* Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi].&lt;br /&gt;
* Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript&lt;br /&gt;
* &#039;&#039;&#039;Setup your development environment&#039;&#039;&#039; [http://en.doc.boardgamearena.com/First_steps_with_BGA_Studio First Steps with BGA Studio]&lt;br /&gt;
&lt;br /&gt;
== Create your first game ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: you should already have created a project following instructions in [[First_steps_with_BGA_Studio#Create_a_new_game_project|Create a new game project]]. While you will find a &#039;&#039;&#039;reversi&#039;&#039;&#039; directory in your SFTP folder, do not use it for this tutorial. Instead, use the project you have created as an (empty) starting point.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
With the initial skeleton of code provided in your project, you can already start a game from the BGA Studio:&lt;br /&gt;
* Go to your [https://studio.boardgamearena.com/controlpanel studio Control panel], then Manage games and select your initial project. &#039;&#039;Note: there are warnings displayed about a missing BGG_ID and presentation text. You can ignore that for now.&#039;&#039;&lt;br /&gt;
* Click the Play link next to your project name. This will open the Play page and offer to create a new table for your project. &#039;&#039;Optional: click the Heart icon to add your project to your favorite games list.&#039;&#039;&lt;br /&gt;
* On the Play page, on the top of the page, make sure that your settings are &amp;quot;Simple game&amp;quot;, &amp;quot;Real time&amp;quot; and &amp;quot;Manual&amp;quot;.&lt;br /&gt;
* Click &amp;quot;Create table&amp;quot; to create a table of your project. &lt;br /&gt;
* For now, we are going to work with one player only, so use the (-) button to set the number of players to 2. Most of the time it is simpler to proceed with only one player during the early phase of development of your game, as it&#039;s easy and fast to start/stop games. By default the game requires 2 players, so press &amp;quot;Express Start&amp;quot;. It will open another tab with another of your players.&lt;br /&gt;
* Reminder: Always use the &amp;quot;Express Start&amp;quot; button to start the game.&lt;br /&gt;
&lt;br /&gt;
Thus, you can start a &amp;quot;Reversi&amp;quot; game, and arrive on a void, empty game. Yeah.&lt;br /&gt;
&lt;br /&gt;
End the game by clicking on the game options icon on the top right, and then on &amp;quot;Express Stop&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Editing the game information (Optional) ==&lt;br /&gt;
&lt;br /&gt;
This step is optional and will fix the warnings on the project page (missing BGG_ID and presentation).&lt;br /&gt;
&lt;br /&gt;
==== Edits to fix Errors ====&lt;br /&gt;
* Edit your local copy of the &amp;lt;code&amp;gt;gameinfos.inc.php&amp;lt;/code&amp;gt; file:&lt;br /&gt;
** Change the &amp;lt;code&amp;gt;bgg_id&amp;lt;/code&amp;gt; value from &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;2389&amp;lt;/code&amp;gt; - that&#039;s around line 26.&lt;br /&gt;
** Add &amp;lt;code&amp;gt;1,&amp;lt;/code&amp;gt; to the players array (so you can start 1-player games while testing) - that&#039;s around line 29.&lt;br /&gt;
* Upload the &amp;lt;code&amp;gt;gameinfos.inc.php&amp;lt;/code&amp;gt; file to the SFTP server (see [[First_steps_with_BGA_Studio#Connect_to_your_SFTP_folder|Connect to your SFTP folder]]).&lt;br /&gt;
&lt;br /&gt;
==== Test your Edits ====&lt;br /&gt;
* Go back to your project page, and in the the local version, click Game page and check if the information match what you changed in the gameinfos file. [[File:Studio local version block.png|frameless]]&lt;br /&gt;
&lt;br /&gt;
===== Not working? =====&lt;br /&gt;
Some changes will require bypassing the cache (adding/changing images). It is often worth doing a hard refresh (Ctrl+F5) to make sure the latest version is used.&lt;br /&gt;
&lt;br /&gt;
== Make sure the IDE is properly configured ==&lt;br /&gt;
Try to add some invalid values in the framework names like this :&lt;br /&gt;
&lt;br /&gt;
[[File:Voluntary reversi tutorial ide error.png]]&lt;br /&gt;
&lt;br /&gt;
The error should be underlined if your IDE is properly configured. If it is not, check [[Setting up BGA Development environment using VSCode]]&lt;br /&gt;
&lt;br /&gt;
In the same fashion, the autocomplete of the IDE should propose you some values after &amp;lt;code&amp;gt;this-&amp;gt;bga-&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
You can also check if the upload works as expected, the game should now work and display the error (Undefined property: Bga\Games\EmptyGame\Game::$bgaTEST ...).&lt;br /&gt;
&lt;br /&gt;
Revert to the correct syntax.&lt;br /&gt;
&lt;br /&gt;
== Make it look like Reversi ==&lt;br /&gt;
&lt;br /&gt;
Let&#039;s start with the board. This will give you a good idea of how things will look and where tokens should go.&lt;br /&gt;
&lt;br /&gt;
Be careful designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case).&lt;br /&gt;
&lt;br /&gt;
For Reversi, it&#039;s useless to have a 750x750px board - much too big, so we chose this one which fit perfectly (536x528):&lt;br /&gt;
&lt;br /&gt;
[[File:Board.jpg]]&lt;br /&gt;
&lt;br /&gt;
Note that we are using a jpg file. Jpg files are lighter than png, so they are faster to load. Later, we are going to use PNGs for tokens because they allow for transparency.&lt;br /&gt;
&lt;br /&gt;
==== Add the board ====&lt;br /&gt;
use lowercase file names&lt;br /&gt;
* upload &amp;lt;code&amp;gt;board.jpg&amp;lt;/code&amp;gt; in your &amp;lt;code&amp;gt;img/&amp;lt;/code&amp;gt; directory. &lt;br /&gt;
* edit &amp;lt;code&amp;gt;modules/js/Game.js&amp;lt;/code&amp;gt; to add the &amp;lt;code&amp;gt;div&amp;lt;/code&amp;gt; for your board at the beginning of the setup function.&lt;br /&gt;
&lt;br /&gt;
Note: If you are building this game by following the tutorial, you will have a different project name than &amp;lt;code&amp;gt;reversi&amp;lt;/code&amp;gt; (i.e. &amp;lt;code&amp;gt;mygame.css&amp;lt;/code&amp;gt;). The file names in your project will be different than shown in this tutorial, replacing &amp;lt;code&amp;gt;reversi&amp;lt;/code&amp;gt; with your project name. Be sure that any code (other than comments) that references &amp;lt;code&amp;gt;reversi&amp;lt;/code&amp;gt; is changed to your actual project name.&lt;br /&gt;
&lt;br /&gt;
Edit the Game.js file and replace the code after &#039;&#039;&#039;// Example to add a div on the game area&#039;&#039;&#039; with the following:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.bga.gameArea.getElement().insertAdjacentHTML(&#039;beforeend&#039;, `&lt;br /&gt;
  &amp;lt;div id=&amp;quot;board&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
`);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: example above use backticks, aka string template literals. This technique used a lot in code of games as it allows to easily create html with strings substitutions, use quotes, double quotes and newlines. &lt;br /&gt;
If you don&#039;t know what it is check here&lt;br /&gt;
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals&lt;br /&gt;
&lt;br /&gt;
*edit your &amp;lt;code&amp;gt;reversi.css&amp;lt;/code&amp;gt; file to transform it into a visible board:&lt;br /&gt;
&lt;br /&gt;
 #board {&lt;br /&gt;
    width: 536px;&lt;br /&gt;
    height: 528px;&lt;br /&gt;
    background-image: url(&#039;img/board.jpg&#039;);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
and remove the code in Game.js in method onEnteringState that refers to adding action buttons and remove the section under &amp;lt;code&amp;gt;// example of adding a div for each player&amp;quot;&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important: refresh your page.&#039;&#039;&#039; Here&#039;s your board:[[File:reversi1.jpg]]&lt;br /&gt;
&lt;br /&gt;
If the board does not appear, refresh the page (always do this when you update the CSS file), and check the image filename. Remember file names are case sensitive!&lt;br /&gt;
&lt;br /&gt;
==== Code the Grid====&lt;br /&gt;
Now, we need to create some invisible HTML elements where squares are. These elements will be used as position references for the white and black tokens. &lt;br /&gt;
&lt;br /&gt;
=====Build the grid of squares=====&lt;br /&gt;
The board is 8 squares by 8 squares. This means we need 64 squares. To avoid writing 64 individual &amp;lt;code&amp;gt;div&amp;lt;/code&amp;gt; elements on our template, we are going to generate the squares on JS &amp;lt;code&amp;gt;setup&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
We&#039;ll do this in our Javascript &amp;lt;code&amp;gt;setup&amp;lt;/code&amp;gt; method under &amp;lt;code&amp;gt;// TODO: Set up your game interface here, according to &amp;quot;gamedatas&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const board = document.getElementById(&#039;board&#039;);&lt;br /&gt;
const hor_scale = 64.8;&lt;br /&gt;
const ver_scale = 64.4;&lt;br /&gt;
for (let x=1; x&amp;lt;=8; x++) {&lt;br /&gt;
    for (let y=1; y&amp;lt;=8; y++) {&lt;br /&gt;
        const left = Math.round((x - 1) * hor_scale + 10);&lt;br /&gt;
        const top = Math.round((y - 1) * ver_scale + 7);&lt;br /&gt;
        // we use afterbegin to make sure squares are placed before discs&lt;br /&gt;
        board.insertAdjacentHTML(`afterbegin`, `&amp;lt;div id=&amp;quot;square_${x}_${y}&amp;quot; class=&amp;quot;square&amp;quot; style=&amp;quot;left: ${left}px; top: ${top}px;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;`);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: as you can see, squares in our &amp;lt;code&amp;gt;board.jpg&amp;lt;/code&amp;gt; files do not have an exact width/height in pixels, and that&#039;s the reason we are using floating point numbers here.&lt;br /&gt;
&lt;br /&gt;
=====Style Those Squares=====&lt;br /&gt;
Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#board {&lt;br /&gt;
    width: 536px;&lt;br /&gt;
    height: 528px;&lt;br /&gt;
    background-image: url(&#039;img/board.jpg&#039;);&lt;br /&gt;
    position: relative;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.square {&lt;br /&gt;
    width: 62px;&lt;br /&gt;
    height: 62px;&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    background-color: red;&lt;br /&gt;
    display: flex;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Explanations:&lt;br /&gt;
*With &amp;quot;&amp;lt;code&amp;gt;position: relative&amp;lt;/code&amp;gt;&amp;quot; on board, we ensure square elements are positioned relatively to board.&lt;br /&gt;
*&amp;lt;code&amp;gt;background-color: red;&amp;lt;/code&amp;gt; is used for testing. This allows us to see the invisible elements. (You could instead do something like &amp;lt;code&amp;gt;outline: 2px solid orange;&amp;lt;/code&amp;gt; have fun and be creative)&lt;br /&gt;
*The display flex with center will place the disc in the center of the square.&lt;br /&gt;
&lt;br /&gt;
Let&#039;s refresh and check our (beautiful) squares:&lt;br /&gt;
&lt;br /&gt;
[[File:reversi2.jpg]]&lt;br /&gt;
&lt;br /&gt;
Now that you know the squares are there, you can remove the test line &amp;lt;code&amp;gt;background-color: red;&amp;lt;/code&amp;gt;  from your &amp;lt;code&amp;gt;.square&amp;lt;/code&amp;gt; class in the CSS stylesheet.&lt;br /&gt;
&lt;br /&gt;
===== Not Working?=====&lt;br /&gt;
If the styled squares do not appear, inspect and check your css (Chrome DevTools: Application &amp;gt; Frames &amp;gt; top &amp;gt; Stylesheets &amp;gt; reversi.css). &lt;br /&gt;
&lt;br /&gt;
==The Tokens==&lt;br /&gt;
&lt;br /&gt;
Now, our board is ready for some tokens!&lt;br /&gt;
&lt;br /&gt;
[Note: Throughout this tutorial, sometimes &amp;quot;tokens&amp;quot; is used, and sometimes &amp;quot;discs&amp;quot; is used. They are often swapped if you&#039;re looking at code in the reversi example project.]&lt;br /&gt;
&lt;br /&gt;
===Build the Token===&lt;br /&gt;
There are quite a few steps before the tokens will appear. You may be used to testing after every change, but that won&#039;t work well here. The token will &#039;&#039;&#039;not&#039;&#039;&#039; show until you have add styles to the css, utility method in the js,  adjusted the php file, and added the token to the board in the js file.&lt;br /&gt;
&lt;br /&gt;
====Add Token to img directory ====&lt;br /&gt;
Here&#039;s a new piece of art with the tokens. We need transparency here so we are using a png file:&lt;br /&gt;
&lt;br /&gt;
[[File:tokens.png]]&lt;br /&gt;
&lt;br /&gt;
Upload this image file &amp;lt;code&amp;gt;tokens.png&amp;lt;/code&amp;gt; in your &amp;lt;code&amp;gt;img/&amp;lt;/code&amp;gt; directory.  NB: may need to manually upload as have found that the automatic SFTP does&#039;t seem to pick up images.&lt;br /&gt;
&lt;br /&gt;
Important Fun Fact: we are using ONE file for both tokens. It is really important to use a minimum number of graphic files for your game. This is called the &amp;quot;CSS sprite&amp;quot; technique, because it makes the game load faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites].&lt;br /&gt;
&lt;br /&gt;
====Style the Tokens in .css file ====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.disc {&lt;br /&gt;
    position: relative;&lt;br /&gt;
    width: 54px;&lt;br /&gt;
    height: 54px;&lt;br /&gt;
    z-index: 3;&lt;br /&gt;
    transform-style: preserve-3d;&lt;br /&gt;
}&lt;br /&gt;
.disc .disc-faces { &lt;br /&gt;
    transform-style: preserve-3d;&lt;br /&gt;
}&lt;br /&gt;
.disc[data-color=&amp;quot;000000&amp;quot;] .disc-faces { &lt;br /&gt;
    transform: rotateY(180deg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.disc-face {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    width: 54px;&lt;br /&gt;
    height: 54px;&lt;br /&gt;
    background-image: url(&#039;img/tokens.png&#039;);&lt;br /&gt;
    background-size: auto 100%;&lt;br /&gt;
    backface-visibility: hidden;&lt;br /&gt;
}&lt;br /&gt;
.disc-face[data-side=&amp;quot;white&amp;quot;] { &lt;br /&gt;
    background-position-x: 0%; &lt;br /&gt;
    transform: rotateY(0deg);&lt;br /&gt;
}&lt;br /&gt;
.disc-face[data-side=&amp;quot;black&amp;quot;] { &lt;br /&gt;
    background-position-x: 100%; &lt;br /&gt;
    transform: rotateY(180deg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With this CSS code, we can set and change the token color by changing the &amp;lt;code&amp;gt;data-color&amp;lt;/code&amp;gt; attribute. Using data instead of a class ensures it can be only one of them (the disc cannot be black and white at the same time).&lt;br /&gt;
We will create a disc-faces div in the disc containing 2 disc-face divs, to represent each face of the disc. It will allow us to have a nice flip animation!&lt;br /&gt;
&lt;br /&gt;
====Add Token Utility Method in .js file====&lt;br /&gt;
Now, let&#039;s make the first token appear on our board. Tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, using a template string&lt;br /&gt;
&lt;br /&gt;
Let&#039;s create a method in our Javascript code (in the &amp;lt;code&amp;gt;modules/js/Game.js&amp;lt;/code&amp;gt; file) that will make a token appear on the board, using this template. Add under the section &amp;lt;code&amp;gt;//// Utility methods&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        async addDiscOnBoard( x, y, playerId, animate = true )&lt;br /&gt;
        {&lt;br /&gt;
            const color = this.gamedatas.players[ playerId ].color;&lt;br /&gt;
            const discId = `disc_${x}_${y}`;&lt;br /&gt;
&lt;br /&gt;
            document.getElementById(`square_${x}_${y}`).insertAdjacentHTML(&#039;beforeend&#039;, `&lt;br /&gt;
                &amp;lt;div class=&amp;quot;disc&amp;quot; data-color=&amp;quot;${color}&amp;quot; id=&amp;quot;${discId}&amp;quot;&amp;gt;&lt;br /&gt;
                    &amp;lt;div class=&amp;quot;disc-faces&amp;quot;&amp;gt;&lt;br /&gt;
                        &amp;lt;div class=&amp;quot;disc-face&amp;quot; data-side=&amp;quot;white&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
                        &amp;lt;div class=&amp;quot;disc-face&amp;quot; data-side=&amp;quot;black&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
                    &amp;lt;/div&amp;gt;&lt;br /&gt;
                &amp;lt;/div&amp;gt;&lt;br /&gt;
            `);&lt;br /&gt;
&lt;br /&gt;
            if (animate) {&lt;br /&gt;
                const element = document.getElementById(discId);&lt;br /&gt;
                await this.animationManager.fadeIn(element, document.getElementById(`overall_player_board_${playerId}`));&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For this to work, you will also need to initialize the animation manager. Include the function in your .js file by updating as outlined here: [[BgaAnimations]]. Then add this in the setup function:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            this.animationManager = new BgaAnimations.Manager({&lt;br /&gt;
                animationsActive: () =&amp;gt; this.bga.gameui.bgaAnimationsActive(),&lt;br /&gt;
            });&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Utility Method Explanation=====&lt;br /&gt;
*with &amp;lt;code&amp;gt;element.insertAdjacentHTML&amp;lt;/code&amp;gt; method, we create a HTML piece of code and insert it as a new child of the square div element.&lt;br /&gt;
*Immediately after, using &amp;lt;code&amp;gt;this.animationManager.fadeIn&amp;lt;/code&amp;gt; BGA method, we make the disc slide in (with a fade in animation) from the player panel.&lt;br /&gt;
&lt;br /&gt;
Also note the trailing comma - this is needed because there may be other functions defined after this one, making &amp;lt;code&amp;gt;addDiscOnBoard&amp;lt;/code&amp;gt; just one element in an array of functions.&lt;br /&gt;
&lt;br /&gt;
====Set Token Colors in setupNewGame in modules/php/Game.php file====&lt;br /&gt;
Before we can show a token, we need to set the player colors in the &amp;lt;code&amp;gt;setupNewGame&amp;lt;/code&amp;gt; function in  &amp;lt;code&amp;gt;modules/php/Game.php&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
Replace &amp;lt;code&amp;gt;$default_colors = $gameinfos[&#039;player_colors&#039;];&amp;lt;/code&amp;gt; with the following line:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$default_colors = [&amp;quot;ffffff&amp;quot;, &amp;quot;000000&amp;quot;];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: A few lines below, you may have to remove the line &amp;lt;code&amp;gt;this-&amp;gt;reattributeColorsBasedOnPreferences( $players, $gameinfos[&#039;player_colors&#039;] );&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Test the Token===&lt;br /&gt;
Now, to test if everything works fine we will call the &amp;lt;code&amp;gt;addTokenOnBoard()&amp;lt;/code&amp;gt;  in .js file to Test&lt;br /&gt;
&lt;br /&gt;
In &amp;lt;code&amp;gt;modules/js/Game.js&amp;lt;/code&amp;gt;, in the &amp;lt;code&amp;gt;setup&amp;lt;/code&amp;gt; function, under the code we added to generate the squares. &lt;br /&gt;
 this.addDiscOnBoard(2, 2, this.bga.players.getCurrentPlayerId(), false);&lt;br /&gt;
Now restart the game.&lt;br /&gt;
&lt;br /&gt;
A token should appear and slide immediately to its position, like this:&lt;br /&gt;
&lt;br /&gt;
[[File:reversi3.jpg]]&lt;br /&gt;
&lt;br /&gt;
==The database==&lt;br /&gt;
&lt;br /&gt;
We did most of the client-side programming, so let&#039;s have a look on the other side now. To design the database model of our game, you will need to access the database. You won&#039;t need to do anything in database UI, yet.&lt;br /&gt;
&lt;br /&gt;
===Accessing the Database===&lt;br /&gt;
To access the database, start a game, then click &amp;quot;Go to game database&amp;quot; link at the bottom of our game, to access the database directly with a PhpMyAdmin instance.&lt;br /&gt;
&lt;br /&gt;
After the first time you&#039;ve access the database, you could skip opening a game and instead, go to https://studio.boardgamearena.com/db/ . Your PhpMyAdmin username/password is in your welcome email.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: do not remove existing tables&lt;br /&gt;
&lt;br /&gt;
===Create Table in .sql file===&lt;br /&gt;
Now, you are able to create the table(s) you need for your game, and report every SQL command used in your &amp;lt;code&amp;gt;dbmodel.sql&amp;lt;/code&amp;gt; file. &lt;br /&gt;
&lt;br /&gt;
The database model of Reversi is short: just one table with the squares of the board. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE TABLE IF NOT EXISTS `board` (&lt;br /&gt;
  `board_x` smallint unsigned NOT NULL,&lt;br /&gt;
  `board_y` smallint unsigned NOT NULL,&lt;br /&gt;
  `board_player` int unsigned DEFAULT NULL,&lt;br /&gt;
  PRIMARY KEY (`board_x`,`board_y`)&lt;br /&gt;
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Add the above SQL to &amp;lt;code&amp;gt;dbmodel.sql&amp;lt;/code&amp;gt;. Pay special attention to the backtick &amp;lt;code&amp;gt;`&amp;lt;/code&amp;gt; character vs. the single quote &amp;lt;code&amp;gt;&#039;&amp;lt;/code&amp;gt; when working with SQL.&lt;br /&gt;
&lt;br /&gt;
===Test the Table===&lt;br /&gt;
Now, a new database with a &amp;lt;code&amp;gt;board&amp;lt;/code&amp;gt; table will be created each time we start a Reversi game. This is why after modifying our &amp;lt;code&amp;gt;dbmodel.sql&amp;lt;/code&amp;gt; it&#039;s a good time to stop your current game &amp;amp; start a new game.&lt;br /&gt;
&lt;br /&gt;
Start a new game and verify a table is created : Click the &amp;quot;Go to game database&amp;quot;, use the login and password your received by email, then you should see &amp;lt;code&amp;gt;board&amp;lt;/code&amp;gt; in the list of tables (the db of the current table is automatically selected when clicking on this button).&lt;br /&gt;
&lt;br /&gt;
== Setup the initial game position ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: From now on, you must launch the game with &#039;&#039;&#039;two players&#039;&#039;&#039; to get two &amp;lt;code&amp;gt;player_id&amp;lt;/code&amp;gt;s within the database. Otherwise, the game will crash.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;setupNewGame&amp;lt;/code&amp;gt; method of our &amp;lt;code&amp;gt;Game.php&amp;lt;/code&amp;gt; is called during initial setup. This initializes our data and places the starting tokens on the board. At the beginning of the game, there should be 4 tokens on the board.&lt;br /&gt;
&lt;br /&gt;
===Initialize the Board in modules/php/Game.php file===&lt;br /&gt;
Under &amp;lt;code&amp;gt;// TODO: setup the initial game situation here&amp;lt;/code&amp;gt;, initialize the board&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
list( $blackplayer_id, $whiteplayer_id ) = array_keys( $players );&lt;br /&gt;
// Init the board&lt;br /&gt;
$this-&amp;gt;boardManager-&amp;gt;initializeBoard(8, (int) $blackplayer_id, (int) $whiteplayer_id);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wait, is that all?&lt;br /&gt;
&lt;br /&gt;
No, the boardManager doesn&#039;t exist, we&#039;ll create it so it will handle all the functions related the tokens on the board. Splitting the code in multiple files will help us have easier to read classes, even more if each class handle a specific responsability.&lt;br /&gt;
&lt;br /&gt;
Create a BoardManager.php file next to Game.php, and put this code in it:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
&lt;br /&gt;
namespace Bga\Games\Reversi;&lt;br /&gt;
&lt;br /&gt;
class BoardManager&lt;br /&gt;
{&lt;br /&gt;
    public function __construct(private Game $game)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function initializeBoard(int $boardSize, int $blackPlayerId, int $whitePlayerId): void&lt;br /&gt;
    {&lt;br /&gt;
        $sqlValues = [];&lt;br /&gt;
        $boardSizeHalf = (int) floor($boardSize / 2);&lt;br /&gt;
&lt;br /&gt;
        for ($x = 1; $x &amp;lt;= $boardSize; $x++) {&lt;br /&gt;
            for ($y = 1; $y &amp;lt;= $boardSize; $y++) {&lt;br /&gt;
                $discValue = &#039;NULL&#039;;&lt;br /&gt;
                if (($x === $boardSizeHalf &amp;amp;&amp;amp; $y === $boardSizeHalf) || ($x === ($boardSizeHalf + 1) &amp;amp;&amp;amp; $y === ($boardSizeHalf + 1))) {&lt;br /&gt;
                    $discValue = (string) $whitePlayerId;&lt;br /&gt;
                } elseif (($x === $boardSizeHalf &amp;amp;&amp;amp; $y === ($boardSizeHalf + 1)) || ($x === ($boardSizeHalf + 1) &amp;amp;&amp;amp; $y === $boardSizeHalf)) {&lt;br /&gt;
                    $discValue = (string) $blackPlayerId;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                $sqlValues[] = &amp;quot;($x,$y,$discValue)&amp;quot;;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $sql = &amp;quot;INSERT INTO `board` (`board_x`,`board_y`,`board_player`) VALUES &amp;quot; . implode(&#039;,&#039;, $sqlValues);&lt;br /&gt;
        $this-&amp;gt;game-&amp;gt;DbQuery($sql);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Once the php file is created edit the namespace and change \Reversi to be the name of your project&lt;br /&gt;
&lt;br /&gt;
Note: the file name match the class name, and the namespace will now match the one from Game.php (so we are following PSR-4 specification), allowing BGA to autoload the class when we will need it. Let&#039;s initialize it from the Game.php file!&lt;br /&gt;
&lt;br /&gt;
Before the Game __construct, add&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  public BoardManager $boardManager;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
At the end of the Game __construct, add&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $this-&amp;gt;boardManager = new BoardManager($this);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Passing the game object to the constructor allows us to use its db functions.&lt;br /&gt;
&lt;br /&gt;
If we were managing cards, we would have probably used the [[Deck]] component, and ideally we would create a CardManager that holds the Deck and all game specific logic about managing the cards.&lt;br /&gt;
&lt;br /&gt;
==== Board Initialization Explanation ====&lt;br /&gt;
&lt;br /&gt;
*We create one table entry for each square, with a &amp;lt;code&amp;gt;NULL&amp;lt;/code&amp;gt; value which means &amp;quot;empty square&amp;quot;&lt;br /&gt;
* On 4 of the squares, we place an initial token.&lt;br /&gt;
&lt;br /&gt;
After this, we set &amp;lt;code&amp;gt;activeNextPlayer&amp;lt;/code&amp;gt; to make the first player active at the beginning of the game (this line is already present in the default code template).&lt;br /&gt;
&lt;br /&gt;
If you didn&#039;t do it earlier, you need to remove the call to &amp;lt;code&amp;gt;this-&amp;gt;reattributeColorsBasedOnPreferences()&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;SetupNewGame()&amp;lt;/code&amp;gt;. If you don&#039;t, player color preferences will try (and fail) to override the two colors supported here.&lt;br /&gt;
&lt;br /&gt;
===Show the Initial Token Setup ===&lt;br /&gt;
Now, we need to make these tokens appear on the client side. The first step is to return the token positions with our &amp;lt;code&amp;gt;getAllDatas()&amp;lt;/code&amp;gt; PHP method. &amp;lt;code&amp;gt;getAllDatas()&amp;lt;/code&amp;gt; is called during each page reload.&lt;br /&gt;
&lt;br /&gt;
In the &amp;lt;code&amp;gt;getAllDatas()&amp;lt;/code&amp;gt; method, after &amp;lt;code&amp;gt;// TODO: Gather all information about current game situation (visible by player $current_player_id)&amp;lt;/code&amp;gt;, add the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// Get reversi board token&lt;br /&gt;
$result[&#039;board&#039;] = $this-&amp;gt;boardManager-&amp;gt;getOccupiedDiscs();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
And in the board manager, add:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function getOccupiedDiscs(): array&lt;br /&gt;
    {&lt;br /&gt;
        return $this-&amp;gt;game-&amp;gt;getObjectListFromDB(&lt;br /&gt;
            &amp;quot;SELECT `board_x` `x`, `board_y` `y`, `board_player` `player`&lt;br /&gt;
             FROM `board`&lt;br /&gt;
             WHERE `board_player` IS NOT NULL&amp;quot;&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Next, you will modify the query that gets player information to also get the player&#039;s colors. Above the lines you just inserted in &amp;lt;code&amp;gt;getAllDatas()&amp;lt;/code&amp;gt; notice how we&#039;ve added &amp;lt;code&amp;gt;player_color color&amp;lt;/code&amp;gt; to the sql query in the code below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result[&amp;quot;players&amp;quot;] = $this-&amp;gt;getCollectionFromDb(&lt;br /&gt;
    &amp;quot;SELECT `player_id` `id`, `player_score` `score`, `player_color` `color` FROM `player`&amp;quot;&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We are using the BGA framework&#039;s &amp;lt;code&amp;gt;getObjectListFromDB()&amp;lt;/code&amp;gt; that formats the result of this SQL query in a PHP array with x, y and player attributes. We add it to the result associative array with the key &amp;lt;code&amp;gt;board&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Last, we process this array client side. Let&#039;s place a token on the board for each array item. We&#039;ll do this in our Javascript &amp;lt;code&amp;gt;setup&amp;lt;/code&amp;gt; method under the code we added to generate the squares. &lt;br /&gt;
&lt;br /&gt;
This will result in a removal or edit of the previously added line &amp;lt;code&amp;gt;this.addDiscOnBoard(2, 2, this.bga.players.getCurrentPlayerId());&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for( var i in gamedatas.board ) {&lt;br /&gt;
    const square = gamedatas.board[i];&lt;br /&gt;
    &lt;br /&gt;
    if( square.player !== null ) {&lt;br /&gt;
        this.addDiscOnBoard( square.x, square.y, square.player );&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;board&amp;lt;/code&amp;gt; entry created in &amp;lt;code&amp;gt;getAllDatas()&amp;lt;/code&amp;gt; is used here as &amp;lt;code&amp;gt;gamedatas.board&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Test the Game Start===&lt;br /&gt;
Reload... and here we are:&lt;br /&gt;
&lt;br /&gt;
[[File:reversi5.jpg]]&lt;br /&gt;
&lt;br /&gt;
It starts to feel like Reversi here...&lt;br /&gt;
&lt;br /&gt;
==State Machine==&lt;br /&gt;
&lt;br /&gt;
Stop your game, again. You&#039;re about to start the core game logic.&lt;br /&gt;
&lt;br /&gt;
You already read [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine], so you know that this is the heart of your game logic. For reversi, it&#039;s relatively simple. Here&#039;s a diagram of our game state machine:&lt;br /&gt;
&lt;br /&gt;
[[File:reversi6.jpg]]&lt;br /&gt;
&lt;br /&gt;
===Build your States===&lt;br /&gt;
And here are the classes we need to create on the &amp;lt;code&amp;gt;module/php/States&amp;lt;/code&amp;gt; folder, according to this diagram (note - PlayerTurn is replaced by PlayDisc):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PlayDisc.php:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
&lt;br /&gt;
namespace Bga\Games\Reversi\States;&lt;br /&gt;
&lt;br /&gt;
use Bga\GameFramework\StateType;&lt;br /&gt;
use Bga\Games\Reversi\Game;&lt;br /&gt;
&lt;br /&gt;
class PlayDisc extends \Bga\GameFramework\States\GameState&lt;br /&gt;
{&lt;br /&gt;
    public function __construct(protected Game $game) {&lt;br /&gt;
        parent::__construct($game, &lt;br /&gt;
            id: 10, &lt;br /&gt;
            type: StateType::ACTIVE_PLAYER,&lt;br /&gt;
&lt;br /&gt;
            description: clienttranslate(&#039;${actplayer} must play a disc&#039;),&lt;br /&gt;
            descriptionMyTurn: clienttranslate(&#039;${you} must play a disc&#039;),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function zombie(int $playerId) {&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(The &#039;&#039;declare(strict_types=1)&#039;&#039; is optional but recommended for new games). The &#039;&#039;&amp;lt;?php&#039;&#039; at the beginning is necessary for your IDE to understand the php syntax. ) &lt;br /&gt;
&lt;br /&gt;
In PlayDisc.js remove the code in onEnteringState (which sets the title top you/activealayer must play a counter &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;NextPlayer.php:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
&lt;br /&gt;
namespace Bga\Games\Reversi\States;&lt;br /&gt;
&lt;br /&gt;
use Bga\GameFramework\StateType;&lt;br /&gt;
use Bga\Games\Reversi\Game;&lt;br /&gt;
&lt;br /&gt;
class NextPlayer extends \Bga\GameFramework\States\GameState&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
    public function __construct(protected Game $game) {&lt;br /&gt;
        parent::__construct($game, &lt;br /&gt;
            id: 90, &lt;br /&gt;
            type: StateType::GAME,&lt;br /&gt;
&lt;br /&gt;
            updateGameProgression: true,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note: state ids 1 and 99 are reserved by the framework for gameSetup and gameEnd. We don&#039;t need to describe those states as the framework will take care of that for us.&lt;br /&gt;
&lt;br /&gt;
In setupNewGame in Game.php, make sure the return is &amp;lt;code&amp;gt;return PlayDisc::class;&amp;lt;/code&amp;gt; so the framework knows which game state is the initial one.&lt;br /&gt;
&lt;br /&gt;
===Test Your States===&lt;br /&gt;
... and start a new Reversi game.&lt;br /&gt;
&lt;br /&gt;
As you can see on the screen capture below, the BGA framework makes the game jump to our first game state &amp;lt;code&amp;gt;PlayDisc&amp;lt;/code&amp;gt; right after the initial setup. That&#039;s why the status bar contains the description of &amp;lt;code&amp;gt;PlayDisc&amp;lt;/code&amp;gt; state (&amp;quot;XXXX must play a disc&amp;quot;):&lt;br /&gt;
&lt;br /&gt;
[[File:reversi7.jpg]]&lt;br /&gt;
&lt;br /&gt;
==The rules==&lt;br /&gt;
&lt;br /&gt;
We will use the &amp;lt;code&amp;gt;getPossibleMoves&amp;lt;/code&amp;gt; PHP method to: &lt;br /&gt;
*Indicate to the current player where she is allowed to play by returning a list of coordinates&lt;br /&gt;
*Check if the player has the right to play in the spot they choose&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
Example of &amp;lt;code&amp;gt;getPossibleMoves&amp;lt;/code&amp;gt; here https://gist.github.com/leocaseiro/a8bc2851bd0caddd06685b5035937d15&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is pure PHP programming here, and there are no special things from the BGA framework that can be used. This is why we won&#039;t go into details here. The overall idea is: &lt;br /&gt;
* Create a &amp;lt;code&amp;gt;getTurnedOverDiscs(x,y)&amp;lt;/code&amp;gt; method that returns coordinates of discs that would be turned over if a token would be played at &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;,&amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Loop through all free squares of the board and call the &amp;lt;code&amp;gt;getTurnedOverDiscs&amp;lt;/code&amp;gt; method on each of them. If at least 1 token is turned over, this is a valid move.&lt;br /&gt;
&lt;br /&gt;
IMPORTANT: Making a database query is slow! Please don&#039;t load the entire game board with a SQL query multiple times. In our implementation, we load the entire board once at the beginning of &amp;lt;code&amp;gt;getPossibleMoves&amp;lt;/code&amp;gt;, and then pass the board as an argument to all methods.&lt;br /&gt;
&lt;br /&gt;
If you want to look into details, please look at the &amp;quot;utility method&amp;quot; sections of &amp;lt;code&amp;gt;Game.php&amp;lt;/code&amp;gt;. If building the tutorial yourself, copy the functions under &amp;quot;Utility functions&amp;quot; comment from the Reversi tutorial.&lt;br /&gt;
&lt;br /&gt;
==Display allowed moves==&lt;br /&gt;
&lt;br /&gt;
Now we want to highlight the squares where the player can place a disc.&lt;br /&gt;
&lt;br /&gt;
To do this, we add a &amp;lt;code&amp;gt;getArgs&amp;lt;/code&amp;gt; method in &amp;lt;code&amp;gt;modules/php/States/PlayDisc.php&amp;lt;/code&amp;gt;. This method is called on the server each time we enter into &amp;lt;code&amp;gt;PlayDisc&amp;lt;/code&amp;gt; game state, and its result is transferred automatically to the client-side:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function getArgs(int $activePlayerId): array&lt;br /&gt;
{&lt;br /&gt;
    return [&lt;br /&gt;
        &#039;possibleMoves&#039; =&amp;gt; $this-&amp;gt;game-&amp;gt;boardManager-&amp;gt;getPossibleMoves($activePlayerId)&lt;br /&gt;
    ];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we need to handle it on the JS side.&lt;br /&gt;
Replace the state classes definition in the Game constructor by this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // Declare the State classes&lt;br /&gt;
        this.playDisc = new PlayDisc(this, bga);&lt;br /&gt;
        this.bga.states.register(&#039;PlayDisc&#039;, this.playDisc);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
So it matches the state we described in PHP. The state name is the PHP class name, that we use in register to link them.&lt;br /&gt;
&lt;br /&gt;
Replace the class PlayerTurn in &amp;lt;code&amp;gt;modules/js/Game.js&amp;lt;/code&amp;gt; by this one:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class PlayDisc {&lt;br /&gt;
    constructor(game, bga) {&lt;br /&gt;
        this.game = game;&lt;br /&gt;
        this.bga = bga;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    onEnteringState(args, isCurrentPlayerActive) {&lt;br /&gt;
        this.bga.statusBar.setTitle(isCurrentPlayerActive ? &lt;br /&gt;
            _(&#039;${you} must play a disc&#039;) :&lt;br /&gt;
            _(&#039;${actplayer} must play a disc&#039;)&lt;br /&gt;
        );&lt;br /&gt;
      &lt;br /&gt;
        if (isCurrentPlayerActive) {            &lt;br /&gt;
            this.updatePossibleMoves(args.possibleMoves);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Note:&amp;lt;/b&amp;gt; We set the title when entering the state, so you can now remove description/descriptionMyTurn in the PHP class that was just here for test purpose.&lt;br /&gt;
&lt;br /&gt;
We use the &amp;lt;code&amp;gt;getPossibleMoves&amp;lt;/code&amp;gt; method we just developed.&lt;br /&gt;
&lt;br /&gt;
Each time we enter into a new game state, we use the &amp;lt;code&amp;gt;onEnteringState&amp;lt;/code&amp;gt; Javascript method of the JS State class. This lets us use the data returned by the method above on the client side.&lt;br /&gt;
&lt;br /&gt;
So, when we enter into &amp;lt;code&amp;gt;PlayDisc&amp;lt;/code&amp;gt; game state, we call our &amp;lt;code&amp;gt;updatePossibleMoves&amp;lt;/code&amp;gt; method (under the &amp;quot;Utility methods&amp;quot; section). This method is added to PlayDisc class and looks like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
updatePossibleMoves(possibleMoves) {&lt;br /&gt;
    // Remove current possible moves&lt;br /&gt;
    document.querySelectorAll(&#039;.possibleMove&#039;).forEach(div =&amp;gt; div.classList.remove(&#039;possibleMove&#039;));&lt;br /&gt;
&lt;br /&gt;
    for( let x in possibleMoves ) {&lt;br /&gt;
        for( let y in possibleMoves[ x ] ) {&lt;br /&gt;
            // x,y is a possible move&lt;br /&gt;
            document.getElementById(`square_${x}_${y}`).classList.add(&#039;possibleMove&#039;);&lt;br /&gt;
        }            &lt;br /&gt;
    }&lt;br /&gt;
                &lt;br /&gt;
    this.bga.gameui.addTooltipToClass( &#039;possibleMove&#039;, &#039;&#039;, _(&#039;Place a disc here&#039;) );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Here&#039;s what this does. At first, it removes all &amp;lt;code&amp;gt;possibleMove&amp;lt;/code&amp;gt; classes currently applied with the very useful &amp;lt;code&amp;gt;document.querySelectorAll&amp;lt;/code&amp;gt; method.&lt;br /&gt;
&lt;br /&gt;
Then it loops through all possible moves our PHP &amp;lt;code&amp;gt;updatePossibleMoves&amp;lt;/code&amp;gt; function created for us, and adds the &amp;lt;code&amp;gt;possibleMove&amp;lt;/code&amp;gt; class to each corresponding square.&lt;br /&gt;
&lt;br /&gt;
Finally, it uses the BGA framework &amp;lt;code&amp;gt;addTooltipToClass&amp;lt;/code&amp;gt; method to associate a tooltip to all those highlighted squares so that players can understand their meaning.&lt;br /&gt;
&lt;br /&gt;
To see the possible moves we need to create a CSS class (&amp;lt;code&amp;gt;possibleMove&amp;lt;/code&amp;gt;) that can be applied to a &amp;lt;code&amp;gt;square&amp;lt;/code&amp;gt; element to highlight it:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.possibleMove {&lt;br /&gt;
    background-color: white;&lt;br /&gt;
    opacity: 0.2; &lt;br /&gt;
    cursor: pointer;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
And here we are:&lt;br /&gt;
&lt;br /&gt;
[[File:reversi8.jpg.jpg]]&lt;br /&gt;
&lt;br /&gt;
==Let&#039;s play ==&lt;br /&gt;
&lt;br /&gt;
From now, it&#039;s better to restart a game with 2 players, because we are going to implement a complete Reversi turn. The summary of what we are going to do is:&lt;br /&gt;
* When we click on a square, check if it is a &amp;lt;code&amp;gt;possibleMove&amp;lt;/code&amp;gt; and if so, send the move to the server.&lt;br /&gt;
*Server side, check the move is correct, apply Reversi rules and jump to next player.&lt;br /&gt;
*Client side, change the token position to reflect the move.&lt;br /&gt;
&lt;br /&gt;
First we associate each click on a square to one of our methods using our Javascript &amp;lt;code&amp;gt;setup&amp;lt;/code&amp;gt; method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
document.querySelectorAll(&#039;.square&#039;).forEach(square =&amp;gt; square.addEventListener(&#039;click&#039;, e =&amp;gt; this.onPlayDisc(e)));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, our &amp;quot;onPlayDisc&amp;quot; method is called each time someone clicks on a square.&lt;br /&gt;
&lt;br /&gt;
Here&#039;s our &amp;quot;onPlayDisc&amp;quot; method below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
onPlayDisc(evt) {&lt;br /&gt;
    // Stop this event propagation&lt;br /&gt;
    evt.preventDefault();&lt;br /&gt;
    evt.stopPropagation();&lt;br /&gt;
&lt;br /&gt;
    // Get the cliqued square x and y&lt;br /&gt;
    // Note: square id format is &amp;quot;square_X_Y&amp;quot;&lt;br /&gt;
    var coords = evt.currentTarget.id.split(&#039;_&#039;);&lt;br /&gt;
    var x = coords[1];&lt;br /&gt;
    var y = coords[2];&lt;br /&gt;
&lt;br /&gt;
    if(!document.getElementById(`square_${x}_${y}`).classList.contains(&#039;possibleMove&#039;)) {&lt;br /&gt;
        // This is not a possible move =&amp;gt; the click does nothing&lt;br /&gt;
        return ;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    this.bga.actions.performAction(&amp;quot;actPlayDisc&amp;quot;, {&lt;br /&gt;
        x:x,&lt;br /&gt;
        y:y&lt;br /&gt;
    });&lt;br /&gt;
},&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
What we do here is:&lt;br /&gt;
*We stop the propagation of the Javascript &amp;lt;code&amp;gt;onclick&amp;lt;/code&amp;gt; event. Otherwise, it can lead to random behavior so it&#039;s always a good idea.&lt;br /&gt;
*We get the x/y coordinates of the square by using &amp;lt;code&amp;gt;evt.currentTarget.id&amp;lt;/code&amp;gt;&lt;br /&gt;
*We check that clicked square has the &amp;lt;code&amp;gt;possibleMove&amp;lt;/code&amp;gt; class, otherwise we know for sure that we can&#039;t play there.&lt;br /&gt;
*Finally, we make a call to the server using BGA &amp;lt;code&amp;gt;bgaPerformAction&amp;lt;/code&amp;gt; method with argument x and y. This call will check that &amp;lt;code&amp;gt;actPlayDisc&amp;lt;/code&amp;gt; action is possible, according to current game state (we will add the &amp;lt;code&amp;gt;PossibleAction&amp;lt;/code&amp;gt; attribute on &amp;lt;code&amp;gt;actPlayDisc&amp;lt;/code&amp;gt; just after). This check is important to avoid issues if a player double clicks on a square.&lt;br /&gt;
&lt;br /&gt;
Now, we have to manage this &amp;lt;code&amp;gt;actPlayDisc&amp;lt;/code&amp;gt; action on the server side. Add a corresponding &amp;lt;code&amp;gt;actPlayDisc&amp;lt;/code&amp;gt; method in our &amp;lt;code&amp;gt;PlayDisc&amp;lt;/code&amp;gt; state class (&amp;lt;code&amp;gt;PlayDisc.php&amp;lt;/code&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#[PossibleAction]&lt;br /&gt;
function actPlayDisc( int $x, int $y, int $activePlayerId )&lt;br /&gt;
{&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
And add &amp;lt;code&amp;gt;use Bga\GameFramework\States\PossibleAction;&amp;lt;/code&amp;gt; at the top of the file.&lt;br /&gt;
&lt;br /&gt;
(The function will be called when the front-side action is triggered using the Autowire mechanism, if you want to see how it works in details check [[Main game logic: Game.php|https://en.doc.boardgamearena.com/Main_game_logic:_Game.php#Actions_%28autowired%29]] )&lt;br /&gt;
&lt;br /&gt;
now, we are using the &amp;lt;code&amp;gt;getTurnedOverDiscs&amp;lt;/code&amp;gt; method again to check that this move is possible:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // Now, check if this is a possible move&lt;br /&gt;
        $board = $this-&amp;gt;game-&amp;gt;boardManager-&amp;gt;getBoard();&lt;br /&gt;
        $turnedOverDiscs = $this-&amp;gt;game-&amp;gt;getTurnedOverDiscs( $x, $y, $activePlayerId, $board );&lt;br /&gt;
        &lt;br /&gt;
        if( count( $turnedOverDiscs ) === 0 ) {&lt;br /&gt;
            throw new SystemException(&amp;quot;Impossible move&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
we update the database to change the color of all turned over disc + the disc we just placed:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            // Let&#039;s place a disc at x,y and return all &amp;quot;$returned&amp;quot; discs to the active player&lt;br /&gt;
            &lt;br /&gt;
            $this-&amp;gt;game-&amp;gt;boardManager-&amp;gt;applyMove($activePlayerId, $x, $y, $turnedOverDiscs);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We add this part to the BoardManager before going back to our act function:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function applyMove(int $playerId, int $x, int $y, array $turnedOverDiscs): void&lt;br /&gt;
    {&lt;br /&gt;
        $positions = [];&lt;br /&gt;
        foreach ($turnedOverDiscs as $turnedOver) {&lt;br /&gt;
            $turnedX = (int) $turnedOver[&#039;x&#039;];&lt;br /&gt;
            $turnedY = (int) $turnedOver[&#039;y&#039;];&lt;br /&gt;
            $positions[] = &amp;quot;($turnedX,$turnedY)&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        $positions[] = &amp;quot;($x,$y)&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        $sql = &amp;quot;UPDATE `board` SET `board_player` = $playerId WHERE (`board_x`, `board_y`) IN (&amp;quot; . implode(&#039;,&#039;, $positions) . &amp;quot;)&amp;quot;;&lt;br /&gt;
        $this-&amp;gt;game-&amp;gt;DbQuery($sql);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
define what game statistics we want to record (for more details read [[Game statistics: stats.json]]), For this tutorial we need to edit the stats.json file and set it to the following:&lt;br /&gt;
 {&lt;br /&gt;
   &amp;quot;table&amp;quot;: [],&lt;br /&gt;
   &amp;quot;player&amp;quot;: {&lt;br /&gt;
     &amp;quot;discPlayedOnCorner&amp;quot;: {&lt;br /&gt;
       &amp;quot;id&amp;quot;: 10,&lt;br /&gt;
       &amp;quot;name&amp;quot;: &amp;quot;Discs played on a corner&amp;quot;,&lt;br /&gt;
       &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;&lt;br /&gt;
     },&lt;br /&gt;
     &amp;quot;discPlayedOnBorder&amp;quot;: {&lt;br /&gt;
       &amp;quot;id&amp;quot;: 11,&lt;br /&gt;
       &amp;quot;name&amp;quot;: &amp;quot;Discs played on a border&amp;quot;,&lt;br /&gt;
       &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;&lt;br /&gt;
     },&lt;br /&gt;
     &amp;quot;discPlayedOnCenter&amp;quot;: {&lt;br /&gt;
       &amp;quot;id&amp;quot;: 12,&lt;br /&gt;
       &amp;quot;name&amp;quot;: &amp;quot;Discs played on board center part&amp;quot;,&lt;br /&gt;
       &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;&lt;br /&gt;
     },&lt;br /&gt;
     &amp;quot;turnedOver&amp;quot;: {&lt;br /&gt;
       &amp;quot;id&amp;quot;: 13,&lt;br /&gt;
       &amp;quot;name&amp;quot;: &amp;quot;Number of discs turned over&amp;quot;,&lt;br /&gt;
       &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
we manage game statistics:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $this-&amp;gt;bga-&amp;gt;playerStats-&amp;gt;inc(&#039;turnedOver&#039;, $disc_count, $activePlayerId);&lt;br /&gt;
        $updatedStat = &#039;discPlayedOnCenter&#039;;&lt;br /&gt;
        if( ($x==1 &amp;amp;&amp;amp; $y==1) || ($x==$board_size &amp;amp;&amp;amp; $y==1) || ($x==1 &amp;amp;&amp;amp; $y==$board_size) || ($x==$board_size &amp;amp;&amp;amp; $y==$board_size) ) {&lt;br /&gt;
            $updatedStat = &#039;discPlayedOnCorner&#039;;&lt;br /&gt;
        } else if( $x==1 || $x==$board_size || $y==1 || $y==$board_size ) {&lt;br /&gt;
            $updatedStat = &#039;discPlayedOnBorder&#039;;&lt;br /&gt;
        }&lt;br /&gt;
        $this-&amp;gt;bga-&amp;gt;playerStats-&amp;gt;inc($updatedStat, 1, $activePlayerId);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notify the turned discs:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            // Notify&lt;br /&gt;
            $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all( &amp;quot;playDisc&amp;quot;, clienttranslate( &#039;${player_name} plays a disc and turns over ${returned_nbr} disc(s)&#039; ), array(&lt;br /&gt;
                &#039;player_id&#039; =&amp;gt; $activePlayerId,&lt;br /&gt;
                &#039;player_name&#039; =&amp;gt; $this-&amp;gt;game-&amp;gt;getPlayerNameById($activePlayerId),&lt;br /&gt;
                &#039;returned_nbr&#039; =&amp;gt; count( $turnedOverDiscs ),&lt;br /&gt;
                &#039;x&#039; =&amp;gt; $x,&lt;br /&gt;
                &#039;y&#039; =&amp;gt; $y&lt;br /&gt;
            ) );&lt;br /&gt;
&lt;br /&gt;
            $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all( &amp;quot;turnOverDiscs&amp;quot;, &#039;&#039;, array(&lt;br /&gt;
                &#039;player_id&#039; =&amp;gt; $activePlayerId,&lt;br /&gt;
                &#039;turnedOver&#039; =&amp;gt; $turnedOverDiscs&lt;br /&gt;
            ) );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then we update the scores accordingly&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $playerIds = array_keys($this-&amp;gt;game-&amp;gt;loadPlayersBasicInfos());&lt;br /&gt;
        foreach ($playerIds as $playerId) {&lt;br /&gt;
            $tokens = $this-&amp;gt;game-&amp;gt;boardManager-&amp;gt;countDiscsForPlayer((int) $playerId);&lt;br /&gt;
            $this-&amp;gt;bga-&amp;gt;playerScore-&amp;gt;set($playerId, $tokens); // this will update the JS counter automatically&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In BoardManager, add:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function countDiscsForPlayer(int $playerId): int&lt;br /&gt;
    {&lt;br /&gt;
        return (int) $this-&amp;gt;game-&amp;gt;getUniqueValueFromDB(&amp;quot;SELECT COUNT(*) FROM `board` WHERE `board_player` = $playerId&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We are using for that 2 notifications (&amp;lt;code&amp;gt;playDisc&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;turnOverDiscs&amp;lt;/code&amp;gt;  that we are going to implement on client side later). Note that the description of the &amp;lt;code&amp;gt;playDisc&amp;lt;/code&amp;gt; notification will be logged in the game log.&lt;br /&gt;
&lt;br /&gt;
Finally, we jump to the next game state.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // Then, go to the next state&lt;br /&gt;
        return NextPlayer::class;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To make the statistics work, we have to initialize them in &amp;lt;code&amp;gt;stats.json&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;player&amp;quot;: {&lt;br /&gt;
    &amp;quot;discPlayedOnCorner&amp;quot;: {&lt;br /&gt;
      &amp;quot;id&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;Discs played on a corner&amp;quot;,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;discPlayedOnBorder&amp;quot;: {&lt;br /&gt;
      &amp;quot;id&amp;quot;: 11,&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;Discs played on a border&amp;quot;,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;discPlayedOnCenter&amp;quot;: {&lt;br /&gt;
      &amp;quot;id&amp;quot;: 12,&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;Discs played on board center part&amp;quot;,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;turnedOver&amp;quot;: {&lt;br /&gt;
      &amp;quot;id&amp;quot;: 13,&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;Number of discs turned over&amp;quot;,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A last thing to do on the server side is to activate the next player when we enter the &amp;lt;code&amp;gt;NextPlayer&amp;lt;/code&amp;gt; game state (in the &amp;lt;code&amp;gt;modules/php/States/NextPlayer.php&amp;lt;/code&amp;gt; file):&lt;br /&gt;
&lt;br /&gt;
Add `const ST_END_GAME = 99;` at the top of the file, then add:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function onEnteringState(): void&lt;br /&gt;
    {&lt;br /&gt;
        // Active next player&lt;br /&gt;
        $player_id = intval($this-&amp;gt;game-&amp;gt;activeNextPlayer());&lt;br /&gt;
&lt;br /&gt;
        // Check if both player has at least 1 discs, and if there are free squares to play&lt;br /&gt;
        $player_to_discs = $this-&amp;gt;game-&amp;gt;getCollectionFromDb( &amp;quot;SELECT board_player, COUNT( board_x )&lt;br /&gt;
                                                       FROM board&lt;br /&gt;
                                                       GROUP BY board_player&amp;quot;, true );&lt;br /&gt;
&lt;br /&gt;
        if( ! isset( $player_to_discs[ null ] ) )&lt;br /&gt;
        {&lt;br /&gt;
            // Index 0 has not been set =&amp;gt; there&#039;s no more free place on the board !&lt;br /&gt;
            // =&amp;gt; end of the game&lt;br /&gt;
            return ST_END_GAME;&lt;br /&gt;
        }&lt;br /&gt;
        else if( ! isset( $player_to_discs[ $player_id ] ) )&lt;br /&gt;
        {&lt;br /&gt;
            // Active player has no more disc on the board =&amp;gt; he looses immediately&lt;br /&gt;
            return ST_END_GAME;&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        // Can this player play?&lt;br /&gt;
&lt;br /&gt;
        $possibleMoves = $this-&amp;gt;game-&amp;gt;getPossibleMoves( $player_id );&lt;br /&gt;
        if( count( $possibleMoves ) == 0 )&lt;br /&gt;
        {&lt;br /&gt;
&lt;br /&gt;
            // This player can&#039;t play&lt;br /&gt;
            // Can his opponent play ?&lt;br /&gt;
            $opponent_id = (int)$this-&amp;gt;game-&amp;gt;getUniqueValueFromDb( &amp;quot;SELECT player_id FROM player WHERE player_id!=&#039;$player_id&#039; &amp;quot; );&lt;br /&gt;
            if( count( $this-&amp;gt;game-&amp;gt;getPossibleMoves( $opponent_id ) ) == 0 )&lt;br /&gt;
            {&lt;br /&gt;
                // Nobody can move =&amp;gt; end of the game&lt;br /&gt;
                return ST_END_GAME;&lt;br /&gt;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {            &lt;br /&gt;
                // =&amp;gt; pass his turn&lt;br /&gt;
                return NextPlayer::class;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            // This player can play. Give him some extra time&lt;br /&gt;
            $this-&amp;gt;game-&amp;gt;giveExtraTime( $player_id );&lt;br /&gt;
            return PlayDisc::class;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, when we play a token, the rules are checked and the token appears in the database.&lt;br /&gt;
&lt;br /&gt;
[[File:reversi9.jpg]]&lt;br /&gt;
&lt;br /&gt;
Of course, as we don&#039;t manage notifications on client side, we need to press F5 after each move to see the changes on the board.&lt;br /&gt;
&lt;br /&gt;
==Make the move appear automatically==&lt;br /&gt;
&lt;br /&gt;
Now, what we have to do is process the notifications sent by the server and make the move appear on the interface.&lt;br /&gt;
&lt;br /&gt;
In our &amp;lt;code&amp;gt;setupNotifications&amp;lt;/code&amp;gt; method in &amp;lt;code&amp;gt;modules/js/Game.js&amp;lt;/code&amp;gt;, we tell BGA to listen to notifications we will add later (&amp;lt;code&amp;gt;playDisc&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;turnOverDiscs&amp;lt;/code&amp;gt;) using promise-based notifications:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        setupNotifications() {&lt;br /&gt;
            console.log( &#039;notifications subscriptions setup&#039; );&lt;br /&gt;
&lt;br /&gt;
            // automatically listen to the notifications, based on the `notif_xxx` function on this class.&lt;br /&gt;
            this.bga.notifications.setupPromiseNotifications();&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We will associate each of our 3 notifications with a method prefixed with &amp;lt;code&amp;gt;notif_&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Let&#039;s have a look now on the &amp;lt;code&amp;gt;playDisc&amp;lt;/code&amp;gt; notification handler method:&amp;lt;pre&amp;gt;&lt;br /&gt;
        async notif_playDisc(args) {&lt;br /&gt;
            // Remove current possible moves (makes the board more clear)&lt;br /&gt;
            document.querySelectorAll(&#039;.possibleMove&#039;).forEach(div =&amp;gt; div.classList.remove(&#039;possibleMove&#039;));&lt;br /&gt;
        &lt;br /&gt;
            await this.addDiscOnBoard( args.x, args.y, args.player_id );&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
No surprise here, we re-used some existing stuff to: &lt;br /&gt;
*Remove the highlighted squares.&lt;br /&gt;
* Add a new disc on board, coming from player panel. That&#039;s where the async await part of &amp;lt;code&amp;gt;addDiscOnBoard&amp;lt;/code&amp;gt; becomes useful.&lt;br /&gt;
&lt;br /&gt;
Now, here&#039;s the method that handles the &amp;lt;code&amp;gt;turnOverDiscs&amp;lt;/code&amp;gt; notification:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        async animateTurnOverDisc(disc, targetColor) {&lt;br /&gt;
            const squareDiv = document.getElementById(`square_${disc.x}_${disc.y}`);&lt;br /&gt;
            const discDiv = document.getElementById(`disc_${disc.x}_${disc.y}`);&lt;br /&gt;
            &lt;br /&gt;
            squareDiv.classList.add(&#039;flip-animation&#039;);&lt;br /&gt;
            await this.bga.gameui.wait(500); // for the flip animation to finish&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            discDiv.dataset.color = targetColor;&lt;br /&gt;
&lt;br /&gt;
            const parallelAnimations = [{&lt;br /&gt;
                keyframes: [ // flip the disc&lt;br /&gt;
                    { transform: `rotateY(180deg)` },&lt;br /&gt;
                    { transform: `rotateY(0deg)` },&lt;br /&gt;
                ]&lt;br /&gt;
            }, {&lt;br /&gt;
                keyframes: [ // lift the disc&lt;br /&gt;
                    { transform: `translate(0, -12px) scale(1.2)`, offset: 0.5 },&lt;br /&gt;
                ]&lt;br /&gt;
            }];&lt;br /&gt;
&lt;br /&gt;
            await this.animationManager.slideAndAttach(discDiv, squareDiv, { duration: 1000, parallelAnimations });&lt;br /&gt;
            &lt;br /&gt;
            squareDiv.classList.remove(&#039;flip-animation&#039;);&lt;br /&gt;
            await this.bga.gameui.wait(500); // for the flip animation removal to finish&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        async notif_turnOverDiscs(args) {&lt;br /&gt;
            // Get the color of the player who is returning the discs&lt;br /&gt;
            const targetColor = this.gamedatas.players[ args.player_id ].color;&lt;br /&gt;
&lt;br /&gt;
            // wait for the animations of all turned discs to be over before considering the notif done&lt;br /&gt;
            await Promise.all(&lt;br /&gt;
                args.turnedOver.map(disc =&amp;gt; &lt;br /&gt;
                    this.animateTurnOverDisc(disc, targetColor)&lt;br /&gt;
                )&lt;br /&gt;
            );&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The list of the discs to be turned over has been made available by our server side code in &amp;lt;code&amp;gt;args.turnedOver&amp;lt;/code&amp;gt; (see previous paragraph). We loop through all these discs, and trigger a single disc animation using &amp;lt;code&amp;gt;animateTurnOverDisc&amp;lt;/code&amp;gt;. This function creates a complex animation using [[BgaAnimations]] for the disc in parameter.&lt;br /&gt;
&lt;br /&gt;
We create a chain of 3 animations using await to make the square highlight, then flip the token, then remove the square highlight.&lt;br /&gt;
&lt;br /&gt;
==Implement the zombie mode ==&lt;br /&gt;
When a player leave the table, a Zombie (bot) will play for the leaver, so the table can continue. For that, we write code in the zombieTurn function.&lt;br /&gt;
&lt;br /&gt;
to learn more about the Zombie mode, read [[Zombie Mode]]&lt;br /&gt;
&lt;br /&gt;
Add this at the end of your PlayDisc.php file :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function zombie(int $playerId) {&lt;br /&gt;
        // Zombie level 1&lt;br /&gt;
        $possibleMoves = $this-&amp;gt;game-&amp;gt;boardManager-&amp;gt;getPossibleMoves($playerId);&lt;br /&gt;
        // transform the 2 dimensional array into a flat array of possible [$x, $y]&lt;br /&gt;
        $possibleMovesArray = [];&lt;br /&gt;
        foreach($possibleMoves as $x =&amp;gt; $ys) {&lt;br /&gt;
            foreach($ys as $y =&amp;gt; $valid) {&lt;br /&gt;
                $possibleMovesArray[] = [$x, $y];&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $zombieChoice = $this-&amp;gt;getRandomZombieChoice($possibleMovesArray);&lt;br /&gt;
        return $this-&amp;gt;actPlayDisc($zombieChoice[0], $zombieChoice[1], $playerId);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And that&#039;s it ! Everytime a leaver should be playing, the Zombie will random play one of the possible moves, and the remaining player will be able to play the next move.&lt;br /&gt;
&lt;br /&gt;
== Add a debug function to help up test the code==&lt;br /&gt;
Add this at the end of your Game.php file :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    function debug_playAutomatically(int $moves = 50) {&lt;br /&gt;
        $this-&amp;gt;bga-&amp;gt;debug-&amp;gt;playUntil(fn(int $count) =&amp;gt; $count === $moves);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function debug_playToEndGame() {&lt;br /&gt;
        $this-&amp;gt;debug_playAutomatically(64); // reversi max moves is under 64 for the standard size board&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This function plays automatically until we reach the end of the game. The functions starting with &amp;quot;debug_&amp;quot; can be triggered in the Studio with a special menu.&lt;br /&gt;
It uses the tool &amp;lt;code&amp;gt;$this-&amp;gt;debug-&amp;gt;playUntil&amp;lt;/code&amp;gt; that will trigger zombie actions, but you could put any code in the debug function for the tests you need.&lt;br /&gt;
&lt;br /&gt;
Start a new game, click on the Bug icon on the top left then &amp;quot;playToEndGame&amp;quot;. You should see the game randomly playing until it reaches the end game, so it helps you check the animations, and you can see if the final scoring is also working as expected. This example will allow us to easily test the Zombie code, as it will play as a Zombie for all players until the end of the game. It&#039;s also an easy way to see if end score computation is working as expected!&lt;br /&gt;
&lt;br /&gt;
You can create as many debug function as you like!&lt;br /&gt;
&lt;br /&gt;
==Optional: add unit tests==&lt;br /&gt;
Follow the instruction on https://en.doc.boardgamearena.com/Testing_by_developer#PHP to setup phpunit and create the first Reversi unit tests that are given as examples there.&lt;br /&gt;
&lt;br /&gt;
Running &amp;lt;code&amp;gt;phpunit&amp;lt;/code&amp;gt;, it should be 4/4 OK! And we have a good hint the case of the full board should be handled OK without actually completing a game :)&lt;br /&gt;
&lt;br /&gt;
==After the tutorial==&lt;br /&gt;
You might want to check another tutorial, or start working on your first real project !&lt;br /&gt;
&lt;br /&gt;
[[Create a game in BGA Studio: Complete Walkthrough]]&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>RichardSPeters</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Tutorial_reversi&amp;diff=29173</id>
		<title>Tutorial reversi</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Tutorial_reversi&amp;diff=29173"/>
		<updated>2026-03-26T22:27:55Z</updated>

		<summary type="html">&lt;p&gt;RichardSPeters: added notes re stats.json&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Using this tutorial, you can build a complete working game on the BGA environment: Reversi.&lt;br /&gt;
&lt;br /&gt;
Before you read this tutorial, you must:&lt;br /&gt;
* Read the overall presentations of the BGA Framework ([[Studio|see here]]).&lt;br /&gt;
* Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi].&lt;br /&gt;
* Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript&lt;br /&gt;
* &#039;&#039;&#039;Setup your development environment&#039;&#039;&#039; [http://en.doc.boardgamearena.com/First_steps_with_BGA_Studio First Steps with BGA Studio]&lt;br /&gt;
&lt;br /&gt;
== Create your first game ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: you should already have created a project following instructions in [[First_steps_with_BGA_Studio#Create_a_new_game_project|Create a new game project]]. While you will find a &#039;&#039;&#039;reversi&#039;&#039;&#039; directory in your SFTP folder, do not use it for this tutorial. Instead, use the project you have created as an (empty) starting point.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
With the initial skeleton of code provided in your project, you can already start a game from the BGA Studio:&lt;br /&gt;
* Go to your [https://studio.boardgamearena.com/controlpanel studio Control panel], then Manage games and select your initial project. &#039;&#039;Note: there are warnings displayed about a missing BGG_ID and presentation text. You can ignore that for now.&#039;&#039;&lt;br /&gt;
* Click the Play link next to your project name. This will open the Play page and offer to create a new table for your project. &#039;&#039;Optional: click the Heart icon to add your project to your favorite games list.&#039;&#039;&lt;br /&gt;
* On the Play page, on the top of the page, make sure that your settings are &amp;quot;Simple game&amp;quot;, &amp;quot;Real time&amp;quot; and &amp;quot;Manual&amp;quot;.&lt;br /&gt;
* Click &amp;quot;Create table&amp;quot; to create a table of your project. &lt;br /&gt;
* For now, we are going to work with one player only, so use the (-) button to set the number of players to 2. Most of the time it is simpler to proceed with only one player during the early phase of development of your game, as it&#039;s easy and fast to start/stop games. By default the game requires 2 players, so press &amp;quot;Express Start&amp;quot;. It will open another tab with another of your players.&lt;br /&gt;
* Reminder: Always use the &amp;quot;Express Start&amp;quot; button to start the game.&lt;br /&gt;
&lt;br /&gt;
Thus, you can start a &amp;quot;Reversi&amp;quot; game, and arrive on a void, empty game. Yeah.&lt;br /&gt;
&lt;br /&gt;
End the game by clicking on the game options icon on the top right, and then on &amp;quot;Express Stop&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Editing the game information (Optional) ==&lt;br /&gt;
&lt;br /&gt;
This step is optional and will fix the warnings on the project page (missing BGG_ID and presentation).&lt;br /&gt;
&lt;br /&gt;
==== Edits to fix Errors ====&lt;br /&gt;
* Edit your local copy of the &amp;lt;code&amp;gt;gameinfos.inc.php&amp;lt;/code&amp;gt; file:&lt;br /&gt;
** Change the &amp;lt;code&amp;gt;bgg_id&amp;lt;/code&amp;gt; value from &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;2389&amp;lt;/code&amp;gt; - that&#039;s around line 26.&lt;br /&gt;
** Add &amp;lt;code&amp;gt;1,&amp;lt;/code&amp;gt; to the players array (so you can start 1-player games while testing) - that&#039;s around line 29.&lt;br /&gt;
* Upload the &amp;lt;code&amp;gt;gameinfos.inc.php&amp;lt;/code&amp;gt; file to the SFTP server (see [[First_steps_with_BGA_Studio#Connect_to_your_SFTP_folder|Connect to your SFTP folder]]).&lt;br /&gt;
&lt;br /&gt;
==== Test your Edits ====&lt;br /&gt;
* Go back to your project page, and in the the local version, click Game page and check if the information match what you changed in the gameinfos file. [[File:Studio local version block.png|frameless]]&lt;br /&gt;
&lt;br /&gt;
===== Not working? =====&lt;br /&gt;
Some changes will require bypassing the cache (adding/changing images). It is often worth doing a hard refresh (Ctrl+F5) to make sure the latest version is used.&lt;br /&gt;
&lt;br /&gt;
== Make sure the IDE is properly configured ==&lt;br /&gt;
Try to add some invalid values in the framework names like this :&lt;br /&gt;
&lt;br /&gt;
[[File:Voluntary reversi tutorial ide error.png]]&lt;br /&gt;
&lt;br /&gt;
The error should be underlined if your IDE is properly configured. If it is not, check [[Setting up BGA Development environment using VSCode]]&lt;br /&gt;
&lt;br /&gt;
In the same fashion, the autocomplete of the IDE should propose you some values after &amp;lt;code&amp;gt;this-&amp;gt;bga-&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
You can also check if the upload works as expected, the game should now work and display the error (Undefined property: Bga\Games\EmptyGame\Game::$bgaTEST ...).&lt;br /&gt;
&lt;br /&gt;
Revert to the correct syntax.&lt;br /&gt;
&lt;br /&gt;
== Make it look like Reversi ==&lt;br /&gt;
&lt;br /&gt;
Let&#039;s start with the board. This will give you a good idea of how things will look and where tokens should go.&lt;br /&gt;
&lt;br /&gt;
Be careful designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case).&lt;br /&gt;
&lt;br /&gt;
For Reversi, it&#039;s useless to have a 750x750px board - much too big, so we chose this one which fit perfectly (536x528):&lt;br /&gt;
&lt;br /&gt;
[[File:Board.jpg]]&lt;br /&gt;
&lt;br /&gt;
Note that we are using a jpg file. Jpg files are lighter than png, so they are faster to load. Later, we are going to use PNGs for tokens because they allow for transparency.&lt;br /&gt;
&lt;br /&gt;
==== Add the board ====&lt;br /&gt;
use lowercase file names&lt;br /&gt;
* upload &amp;lt;code&amp;gt;board.jpg&amp;lt;/code&amp;gt; in your &amp;lt;code&amp;gt;img/&amp;lt;/code&amp;gt; directory. &lt;br /&gt;
* edit &amp;lt;code&amp;gt;modules/js/Game.js&amp;lt;/code&amp;gt; to add the &amp;lt;code&amp;gt;div&amp;lt;/code&amp;gt; for your board at the beginning of the setup function.&lt;br /&gt;
&lt;br /&gt;
Note: If you are building this game by following the tutorial, you will have a different project name than &amp;lt;code&amp;gt;reversi&amp;lt;/code&amp;gt; (i.e. &amp;lt;code&amp;gt;mygame.css&amp;lt;/code&amp;gt;). The file names in your project will be different than shown in this tutorial, replacing &amp;lt;code&amp;gt;reversi&amp;lt;/code&amp;gt; with your project name. Be sure that any code (other than comments) that references &amp;lt;code&amp;gt;reversi&amp;lt;/code&amp;gt; is changed to your actual project name.&lt;br /&gt;
&lt;br /&gt;
Edit the Game.js file and replace the code after &#039;&#039;&#039;// Example to add a div on the game area&#039;&#039;&#039; with the following:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.bga.gameArea.getElement().insertAdjacentHTML(&#039;beforeend&#039;, `&lt;br /&gt;
  &amp;lt;div id=&amp;quot;board&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
`);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: example above use backticks, aka string template literals. This technique used a lot in code of games as it allows to easily create html with strings substitutions, use quotes, double quotes and newlines. &lt;br /&gt;
If you don&#039;t know what it is check here&lt;br /&gt;
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals&lt;br /&gt;
&lt;br /&gt;
*edit your &amp;lt;code&amp;gt;reversi.css&amp;lt;/code&amp;gt; file to transform it into a visible board:&lt;br /&gt;
&lt;br /&gt;
 #board {&lt;br /&gt;
    width: 536px;&lt;br /&gt;
    height: 528px;&lt;br /&gt;
    background-image: url(&#039;img/board.jpg&#039;);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
and remove the code in Game.js in method onEnteringState that refers to adding action buttons and remove the section under &amp;lt;code&amp;gt;// example of adding a div for each player&amp;quot;&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important: refresh your page.&#039;&#039;&#039; Here&#039;s your board:[[File:reversi1.jpg]]&lt;br /&gt;
&lt;br /&gt;
If the board does not appear, refresh the page (always do this when you update the CSS file), and check the image filename. Remember file names are case sensitive!&lt;br /&gt;
&lt;br /&gt;
==== Code the Grid====&lt;br /&gt;
Now, we need to create some invisible HTML elements where squares are. These elements will be used as position references for the white and black tokens. &lt;br /&gt;
&lt;br /&gt;
=====Build the grid of squares=====&lt;br /&gt;
The board is 8 squares by 8 squares. This means we need 64 squares. To avoid writing 64 individual &amp;lt;code&amp;gt;div&amp;lt;/code&amp;gt; elements on our template, we are going to generate the squares on JS &amp;lt;code&amp;gt;setup&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
We&#039;ll do this in our Javascript &amp;lt;code&amp;gt;setup&amp;lt;/code&amp;gt; method under &amp;lt;code&amp;gt;// TODO: Set up your game interface here, according to &amp;quot;gamedatas&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const board = document.getElementById(&#039;board&#039;);&lt;br /&gt;
const hor_scale = 64.8;&lt;br /&gt;
const ver_scale = 64.4;&lt;br /&gt;
for (let x=1; x&amp;lt;=8; x++) {&lt;br /&gt;
    for (let y=1; y&amp;lt;=8; y++) {&lt;br /&gt;
        const left = Math.round((x - 1) * hor_scale + 10);&lt;br /&gt;
        const top = Math.round((y - 1) * ver_scale + 7);&lt;br /&gt;
        // we use afterbegin to make sure squares are placed before discs&lt;br /&gt;
        board.insertAdjacentHTML(`afterbegin`, `&amp;lt;div id=&amp;quot;square_${x}_${y}&amp;quot; class=&amp;quot;square&amp;quot; style=&amp;quot;left: ${left}px; top: ${top}px;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;`);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: as you can see, squares in our &amp;lt;code&amp;gt;board.jpg&amp;lt;/code&amp;gt; files do not have an exact width/height in pixels, and that&#039;s the reason we are using floating point numbers here.&lt;br /&gt;
&lt;br /&gt;
=====Style Those Squares=====&lt;br /&gt;
Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#board {&lt;br /&gt;
    width: 536px;&lt;br /&gt;
    height: 528px;&lt;br /&gt;
    background-image: url(&#039;img/board.jpg&#039;);&lt;br /&gt;
    position: relative;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.square {&lt;br /&gt;
    width: 62px;&lt;br /&gt;
    height: 62px;&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    background-color: red;&lt;br /&gt;
    display: flex;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Explanations:&lt;br /&gt;
*With &amp;quot;&amp;lt;code&amp;gt;position: relative&amp;lt;/code&amp;gt;&amp;quot; on board, we ensure square elements are positioned relatively to board.&lt;br /&gt;
*&amp;lt;code&amp;gt;background-color: red;&amp;lt;/code&amp;gt; is used for testing. This allows us to see the invisible elements. (You could instead do something like &amp;lt;code&amp;gt;outline: 2px solid orange;&amp;lt;/code&amp;gt; have fun and be creative)&lt;br /&gt;
*The display flex with center will place the disc in the center of the square.&lt;br /&gt;
&lt;br /&gt;
Let&#039;s refresh and check our (beautiful) squares:&lt;br /&gt;
&lt;br /&gt;
[[File:reversi2.jpg]]&lt;br /&gt;
&lt;br /&gt;
Now that you know the squares are there, you can remove the test line &amp;lt;code&amp;gt;background-color: red;&amp;lt;/code&amp;gt;  from your &amp;lt;code&amp;gt;.square&amp;lt;/code&amp;gt; class in the CSS stylesheet.&lt;br /&gt;
&lt;br /&gt;
===== Not Working?=====&lt;br /&gt;
If the styled squares do not appear, inspect and check your css (Chrome DevTools: Application &amp;gt; Frames &amp;gt; top &amp;gt; Stylesheets &amp;gt; reversi.css). &lt;br /&gt;
&lt;br /&gt;
==The Tokens==&lt;br /&gt;
&lt;br /&gt;
Now, our board is ready for some tokens!&lt;br /&gt;
&lt;br /&gt;
[Note: Throughout this tutorial, sometimes &amp;quot;tokens&amp;quot; is used, and sometimes &amp;quot;discs&amp;quot; is used. They are often swapped if you&#039;re looking at code in the reversi example project.]&lt;br /&gt;
&lt;br /&gt;
===Build the Token===&lt;br /&gt;
There are quite a few steps before the tokens will appear. You may be used to testing after every change, but that won&#039;t work well here. The token will &#039;&#039;&#039;not&#039;&#039;&#039; show until you have add styles to the css, utility method in the js,  adjusted the php file, and added the token to the board in the js file.&lt;br /&gt;
&lt;br /&gt;
====Add Token to img directory ====&lt;br /&gt;
Here&#039;s a new piece of art with the tokens. We need transparency here so we are using a png file:&lt;br /&gt;
&lt;br /&gt;
[[File:tokens.png]]&lt;br /&gt;
&lt;br /&gt;
Upload this image file &amp;lt;code&amp;gt;tokens.png&amp;lt;/code&amp;gt; in your &amp;lt;code&amp;gt;img/&amp;lt;/code&amp;gt; directory.  NB: may need to manually upload as have found that the automatic SFTP does&#039;t seem to pick up images.&lt;br /&gt;
&lt;br /&gt;
Important Fun Fact: we are using ONE file for both tokens. It is really important to use a minimum number of graphic files for your game. This is called the &amp;quot;CSS sprite&amp;quot; technique, because it makes the game load faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites].&lt;br /&gt;
&lt;br /&gt;
====Style the Tokens in .css file ====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.disc {&lt;br /&gt;
    position: relative;&lt;br /&gt;
    width: 54px;&lt;br /&gt;
    height: 54px;&lt;br /&gt;
    z-index: 3;&lt;br /&gt;
    transform-style: preserve-3d;&lt;br /&gt;
}&lt;br /&gt;
.disc .disc-faces { &lt;br /&gt;
    transform-style: preserve-3d;&lt;br /&gt;
}&lt;br /&gt;
.disc[data-color=&amp;quot;000000&amp;quot;] .disc-faces { &lt;br /&gt;
    transform: rotateY(180deg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.disc-face {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    width: 54px;&lt;br /&gt;
    height: 54px;&lt;br /&gt;
    background-image: url(&#039;img/tokens.png&#039;);&lt;br /&gt;
    background-size: auto 100%;&lt;br /&gt;
    backface-visibility: hidden;&lt;br /&gt;
}&lt;br /&gt;
.disc-face[data-side=&amp;quot;white&amp;quot;] { &lt;br /&gt;
    background-position-x: 0%; &lt;br /&gt;
    transform: rotateY(0deg);&lt;br /&gt;
}&lt;br /&gt;
.disc-face[data-side=&amp;quot;black&amp;quot;] { &lt;br /&gt;
    background-position-x: 100%; &lt;br /&gt;
    transform: rotateY(180deg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With this CSS code, we can set and change the token color by changing the &amp;lt;code&amp;gt;data-color&amp;lt;/code&amp;gt; attribute. Using data instead of a class ensures it can be only one of them (the disc cannot be black and white at the same time).&lt;br /&gt;
We will create a disc-faces div in the disc containing 2 disc-face divs, to represent each face of the disc. It will allow us to have a nice flip animation!&lt;br /&gt;
&lt;br /&gt;
====Add Token Utility Method in .js file====&lt;br /&gt;
Now, let&#039;s make the first token appear on our board. Tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, using a template string&lt;br /&gt;
&lt;br /&gt;
Let&#039;s create a method in our Javascript code (in the &amp;lt;code&amp;gt;modules/js/Game.js&amp;lt;/code&amp;gt; file) that will make a token appear on the board, using this template. Add under the section &amp;lt;code&amp;gt;//// Utility methods&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        async addDiscOnBoard( x, y, playerId, animate = true )&lt;br /&gt;
        {&lt;br /&gt;
            const color = this.gamedatas.players[ playerId ].color;&lt;br /&gt;
            const discId = `disc_${x}_${y}`;&lt;br /&gt;
&lt;br /&gt;
            document.getElementById(`square_${x}_${y}`).insertAdjacentHTML(&#039;beforeend&#039;, `&lt;br /&gt;
                &amp;lt;div class=&amp;quot;disc&amp;quot; data-color=&amp;quot;${color}&amp;quot; id=&amp;quot;${discId}&amp;quot;&amp;gt;&lt;br /&gt;
                    &amp;lt;div class=&amp;quot;disc-faces&amp;quot;&amp;gt;&lt;br /&gt;
                        &amp;lt;div class=&amp;quot;disc-face&amp;quot; data-side=&amp;quot;white&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
                        &amp;lt;div class=&amp;quot;disc-face&amp;quot; data-side=&amp;quot;black&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
                    &amp;lt;/div&amp;gt;&lt;br /&gt;
                &amp;lt;/div&amp;gt;&lt;br /&gt;
            `);&lt;br /&gt;
&lt;br /&gt;
            if (animate) {&lt;br /&gt;
                const element = document.getElementById(discId);&lt;br /&gt;
                await this.animationManager.fadeIn(element, document.getElementById(`overall_player_board_${playerId}`));&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For this to work, you will also need to initialize the animation manager. Include the function in your .js file by updating as outlined here: [[BgaAnimations]]. Then add this in the setup function:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            this.animationManager = new BgaAnimations.Manager({&lt;br /&gt;
                animationsActive: () =&amp;gt; this.bga.gameui.bgaAnimationsActive(),&lt;br /&gt;
            });&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Utility Method Explanation=====&lt;br /&gt;
*with &amp;lt;code&amp;gt;element.insertAdjacentHTML&amp;lt;/code&amp;gt; method, we create a HTML piece of code and insert it as a new child of the square div element.&lt;br /&gt;
*Immediately after, using &amp;lt;code&amp;gt;this.animationManager.fadeIn&amp;lt;/code&amp;gt; BGA method, we make the disc slide in (with a fade in animation) from the player panel.&lt;br /&gt;
&lt;br /&gt;
Also note the trailing comma - this is needed because there may be other functions defined after this one, making &amp;lt;code&amp;gt;addDiscOnBoard&amp;lt;/code&amp;gt; just one element in an array of functions.&lt;br /&gt;
&lt;br /&gt;
====Set Token Colors in setupNewGame in modules/php/Game.php file====&lt;br /&gt;
Before we can show a token, we need to set the player colors in the &amp;lt;code&amp;gt;setupNewGame&amp;lt;/code&amp;gt; function in  &amp;lt;code&amp;gt;modules/php/Game.php&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
Replace &amp;lt;code&amp;gt;$default_colors = $gameinfos[&#039;player_colors&#039;];&amp;lt;/code&amp;gt; with the following line:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$default_colors = [&amp;quot;ffffff&amp;quot;, &amp;quot;000000&amp;quot;];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: A few lines below, you may have to remove the line &amp;lt;code&amp;gt;this-&amp;gt;reattributeColorsBasedOnPreferences( $players, $gameinfos[&#039;player_colors&#039;] );&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Test the Token===&lt;br /&gt;
Now, to test if everything works fine we will call the &amp;lt;code&amp;gt;addTokenOnBoard()&amp;lt;/code&amp;gt;  in .js file to Test&lt;br /&gt;
&lt;br /&gt;
In &amp;lt;code&amp;gt;modules/js/Game.js&amp;lt;/code&amp;gt;, in the &amp;lt;code&amp;gt;setup&amp;lt;/code&amp;gt; function, under the code we added to generate the squares. &lt;br /&gt;
 this.addDiscOnBoard(2, 2, this.bga.players.getCurrentPlayerId(), false);&lt;br /&gt;
Now restart the game.&lt;br /&gt;
&lt;br /&gt;
A token should appear and slide immediately to its position, like this:&lt;br /&gt;
&lt;br /&gt;
[[File:reversi3.jpg]]&lt;br /&gt;
&lt;br /&gt;
==The database==&lt;br /&gt;
&lt;br /&gt;
We did most of the client-side programming, so let&#039;s have a look on the other side now. To design the database model of our game, you will need to access the database. You won&#039;t need to do anything in database UI, yet.&lt;br /&gt;
&lt;br /&gt;
===Accessing the Database===&lt;br /&gt;
To access the database, start a game, then click &amp;quot;Go to game database&amp;quot; link at the bottom of our game, to access the database directly with a PhpMyAdmin instance.&lt;br /&gt;
&lt;br /&gt;
After the first time you&#039;ve access the database, you could skip opening a game and instead, go to https://studio.boardgamearena.com/db/ . Your PhpMyAdmin username/password is in your welcome email.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: do not remove existing tables&lt;br /&gt;
&lt;br /&gt;
===Create Table in .sql file===&lt;br /&gt;
Now, you are able to create the table(s) you need for your game, and report every SQL command used in your &amp;lt;code&amp;gt;dbmodel.sql&amp;lt;/code&amp;gt; file. &lt;br /&gt;
&lt;br /&gt;
The database model of Reversi is short: just one table with the squares of the board. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE TABLE IF NOT EXISTS `board` (&lt;br /&gt;
  `board_x` smallint unsigned NOT NULL,&lt;br /&gt;
  `board_y` smallint unsigned NOT NULL,&lt;br /&gt;
  `board_player` int unsigned DEFAULT NULL,&lt;br /&gt;
  PRIMARY KEY (`board_x`,`board_y`)&lt;br /&gt;
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Add the above SQL to &amp;lt;code&amp;gt;dbmodel.sql&amp;lt;/code&amp;gt;. Pay special attention to the backtick &amp;lt;code&amp;gt;`&amp;lt;/code&amp;gt; character vs. the single quote &amp;lt;code&amp;gt;&#039;&amp;lt;/code&amp;gt; when working with SQL.&lt;br /&gt;
&lt;br /&gt;
===Test the Table===&lt;br /&gt;
Now, a new database with a &amp;lt;code&amp;gt;board&amp;lt;/code&amp;gt; table will be created each time we start a Reversi game. This is why after modifying our &amp;lt;code&amp;gt;dbmodel.sql&amp;lt;/code&amp;gt; it&#039;s a good time to stop your current game &amp;amp; start a new game.&lt;br /&gt;
&lt;br /&gt;
Start a new game and verify a table is created : Click the &amp;quot;Go to game database&amp;quot;, use the login and password your received by email, then you should see &amp;lt;code&amp;gt;board&amp;lt;/code&amp;gt; in the list of tables (the db of the current table is automatically selected when clicking on this button).&lt;br /&gt;
&lt;br /&gt;
== Setup the initial game position ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: From now on, you must launch the game with &#039;&#039;&#039;two players&#039;&#039;&#039; to get two &amp;lt;code&amp;gt;player_id&amp;lt;/code&amp;gt;s within the database. Otherwise, the game will crash.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;setupNewGame&amp;lt;/code&amp;gt; method of our &amp;lt;code&amp;gt;Game.php&amp;lt;/code&amp;gt; is called during initial setup. This initializes our data and places the starting tokens on the board. At the beginning of the game, there should be 4 tokens on the board.&lt;br /&gt;
&lt;br /&gt;
===Initialize the Board in modules/php/Game.php file===&lt;br /&gt;
Under &amp;lt;code&amp;gt;// TODO: setup the initial game situation here&amp;lt;/code&amp;gt;, initialize the board&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
list( $blackplayer_id, $whiteplayer_id ) = array_keys( $players );&lt;br /&gt;
// Init the board&lt;br /&gt;
$this-&amp;gt;boardManager-&amp;gt;initializeBoard(8, (int) $blackplayer_id, (int) $whiteplayer_id);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wait, is that all?&lt;br /&gt;
&lt;br /&gt;
No, the boardManager doesn&#039;t exist, we&#039;ll create it so it will handle all the functions related the tokens on the board. Splitting the code in multiple files will help us have easier to read classes, even more if each class handle a specific responsability.&lt;br /&gt;
&lt;br /&gt;
Create a BoardManager.php file next to Game.php, and put this code in it:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
&lt;br /&gt;
namespace Bga\Games\Reversi;&lt;br /&gt;
&lt;br /&gt;
class BoardManager&lt;br /&gt;
{&lt;br /&gt;
    public function __construct(private Game $game)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function initializeBoard(int $boardSize, int $blackPlayerId, int $whitePlayerId): void&lt;br /&gt;
    {&lt;br /&gt;
        $sqlValues = [];&lt;br /&gt;
        $boardSizeHalf = (int) floor($boardSize / 2);&lt;br /&gt;
&lt;br /&gt;
        for ($x = 1; $x &amp;lt;= $boardSize; $x++) {&lt;br /&gt;
            for ($y = 1; $y &amp;lt;= $boardSize; $y++) {&lt;br /&gt;
                $discValue = &#039;NULL&#039;;&lt;br /&gt;
                if (($x === $boardSizeHalf &amp;amp;&amp;amp; $y === $boardSizeHalf) || ($x === ($boardSizeHalf + 1) &amp;amp;&amp;amp; $y === ($boardSizeHalf + 1))) {&lt;br /&gt;
                    $discValue = (string) $whitePlayerId;&lt;br /&gt;
                } elseif (($x === $boardSizeHalf &amp;amp;&amp;amp; $y === ($boardSizeHalf + 1)) || ($x === ($boardSizeHalf + 1) &amp;amp;&amp;amp; $y === $boardSizeHalf)) {&lt;br /&gt;
                    $discValue = (string) $blackPlayerId;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                $sqlValues[] = &amp;quot;($x,$y,$discValue)&amp;quot;;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $sql = &amp;quot;INSERT INTO `board` (`board_x`,`board_y`,`board_player`) VALUES &amp;quot; . implode(&#039;,&#039;, $sqlValues);&lt;br /&gt;
        $this-&amp;gt;game-&amp;gt;DbQuery($sql);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Once the php file is created edit the namespace and change \Reversi to be the name of your project&lt;br /&gt;
&lt;br /&gt;
Note: the file name match the class name, and the namespace will now match the one from Game.php (so we are following PSR-4 specification), allowing BGA to autoload the class when we will need it. Let&#039;s initialize it from the Game.php file!&lt;br /&gt;
&lt;br /&gt;
Before the Game __construct, add&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  public BoardManager $boardManager;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
At the end of the Game __construct, add&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $this-&amp;gt;boardManager = new BoardManager($this);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Passing the game object to the constructor allows us to use its db functions.&lt;br /&gt;
&lt;br /&gt;
If we were managing cards, we would have probably used the [[Deck]] component, and ideally we would create a CardManager that holds the Deck and all game specific logic about managing the cards.&lt;br /&gt;
&lt;br /&gt;
==== Board Initialization Explanation ====&lt;br /&gt;
&lt;br /&gt;
*We create one table entry for each square, with a &amp;lt;code&amp;gt;NULL&amp;lt;/code&amp;gt; value which means &amp;quot;empty square&amp;quot;&lt;br /&gt;
* On 4 of the squares, we place an initial token.&lt;br /&gt;
&lt;br /&gt;
After this, we set &amp;lt;code&amp;gt;activeNextPlayer&amp;lt;/code&amp;gt; to make the first player active at the beginning of the game (this line is already present in the default code template).&lt;br /&gt;
&lt;br /&gt;
If you didn&#039;t do it earlier, you need to remove the call to &amp;lt;code&amp;gt;this-&amp;gt;reattributeColorsBasedOnPreferences()&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;SetupNewGame()&amp;lt;/code&amp;gt;. If you don&#039;t, player color preferences will try (and fail) to override the two colors supported here.&lt;br /&gt;
&lt;br /&gt;
===Show the Initial Token Setup ===&lt;br /&gt;
Now, we need to make these tokens appear on the client side. The first step is to return the token positions with our &amp;lt;code&amp;gt;getAllDatas()&amp;lt;/code&amp;gt; PHP method. &amp;lt;code&amp;gt;getAllDatas()&amp;lt;/code&amp;gt; is called during each page reload.&lt;br /&gt;
&lt;br /&gt;
In the &amp;lt;code&amp;gt;getAllDatas()&amp;lt;/code&amp;gt; method, after &amp;lt;code&amp;gt;// TODO: Gather all information about current game situation (visible by player $current_player_id)&amp;lt;/code&amp;gt;, add the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// Get reversi board token&lt;br /&gt;
$result[&#039;board&#039;] = $this-&amp;gt;boardManager-&amp;gt;getOccupiedDiscs();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
And in the board manager, add:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function getOccupiedDiscs(): array&lt;br /&gt;
    {&lt;br /&gt;
        return $this-&amp;gt;game-&amp;gt;getObjectListFromDB(&lt;br /&gt;
            &amp;quot;SELECT `board_x` `x`, `board_y` `y`, `board_player` `player`&lt;br /&gt;
             FROM `board`&lt;br /&gt;
             WHERE `board_player` IS NOT NULL&amp;quot;&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Next, you will modify the query that gets player information to also get the player&#039;s colors. Above the lines you just inserted in &amp;lt;code&amp;gt;getAllDatas()&amp;lt;/code&amp;gt; notice how we&#039;ve added &amp;lt;code&amp;gt;player_color color&amp;lt;/code&amp;gt; to the sql query in the code below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result[&amp;quot;players&amp;quot;] = $this-&amp;gt;getCollectionFromDb(&lt;br /&gt;
    &amp;quot;SELECT `player_id` `id`, `player_score` `score`, `player_color` `color` FROM `player`&amp;quot;&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We are using the BGA framework&#039;s &amp;lt;code&amp;gt;getObjectListFromDB()&amp;lt;/code&amp;gt; that formats the result of this SQL query in a PHP array with x, y and player attributes. We add it to the result associative array with the key &amp;lt;code&amp;gt;board&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Last, we process this array client side. Let&#039;s place a token on the board for each array item. We&#039;ll do this in our Javascript &amp;lt;code&amp;gt;setup&amp;lt;/code&amp;gt; method under the code we added to generate the squares. &lt;br /&gt;
&lt;br /&gt;
This will result in a removal or edit of the previously added line &amp;lt;code&amp;gt;this.addDiscOnBoard(2, 2, this.bga.players.getCurrentPlayerId());&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for( var i in gamedatas.board ) {&lt;br /&gt;
    const square = gamedatas.board[i];&lt;br /&gt;
    &lt;br /&gt;
    if( square.player !== null ) {&lt;br /&gt;
        this.addDiscOnBoard( square.x, square.y, square.player );&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;board&amp;lt;/code&amp;gt; entry created in &amp;lt;code&amp;gt;getAllDatas()&amp;lt;/code&amp;gt; is used here as &amp;lt;code&amp;gt;gamedatas.board&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Test the Game Start===&lt;br /&gt;
Reload... and here we are:&lt;br /&gt;
&lt;br /&gt;
[[File:reversi5.jpg]]&lt;br /&gt;
&lt;br /&gt;
It starts to feel like Reversi here...&lt;br /&gt;
&lt;br /&gt;
==State Machine==&lt;br /&gt;
&lt;br /&gt;
Stop your game, again. You&#039;re about to start the core game logic.&lt;br /&gt;
&lt;br /&gt;
You already read [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine], so you know that this is the heart of your game logic. For reversi, it&#039;s relatively simple. Here&#039;s a diagram of our game state machine:&lt;br /&gt;
&lt;br /&gt;
[[File:reversi6.jpg]]&lt;br /&gt;
&lt;br /&gt;
===Build your States===&lt;br /&gt;
And here are the classes we need to create on the &amp;lt;code&amp;gt;module/php/States&amp;lt;/code&amp;gt; folder, according to this diagram:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PlayerTurn.php:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
&lt;br /&gt;
namespace Bga\Games\Reversi\States;&lt;br /&gt;
&lt;br /&gt;
use Bga\GameFramework\StateType;&lt;br /&gt;
use Bga\Games\Reversi\Game;&lt;br /&gt;
&lt;br /&gt;
class PlayerTurn extends \Bga\GameFramework\States\GameState&lt;br /&gt;
{&lt;br /&gt;
    public function __construct(protected Game $game) {&lt;br /&gt;
        parent::__construct($game, &lt;br /&gt;
            id: 10, &lt;br /&gt;
            type: StateType::ACTIVE_PLAYER,&lt;br /&gt;
&lt;br /&gt;
            description: clienttranslate(&#039;${actplayer} must play a disc&#039;),&lt;br /&gt;
            descriptionMyTurn: clienttranslate(&#039;${you} must play a disc&#039;),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function zombie(int $playerId) {&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(The &#039;&#039;declare(strict_types=1)&#039;&#039; is optional but recommended for new games). The &#039;&#039;&amp;lt;?php&#039;&#039; at the beginning is necessary for your IDE to understand the php syntax. ) &lt;br /&gt;
&lt;br /&gt;
In PlayerTurn.js remove the code in onEnteringState (which sets the title top you/activealayer must play a counter &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;NextPlayer.php:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
&lt;br /&gt;
namespace Bga\Games\Reversi\States;&lt;br /&gt;
&lt;br /&gt;
use Bga\GameFramework\StateType;&lt;br /&gt;
use Bga\Games\Reversi\Game;&lt;br /&gt;
&lt;br /&gt;
class NextPlayer extends \Bga\GameFramework\States\GameState&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
    public function __construct(protected Game $game) {&lt;br /&gt;
        parent::__construct($game, &lt;br /&gt;
            id: 90, &lt;br /&gt;
            type: StateType::GAME,&lt;br /&gt;
&lt;br /&gt;
            updateGameProgression: true,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note: state ids 1 and 99 are reserved by the framework for gameSetup and gameEnd. We don&#039;t need to describe those states as the framework will take care of that for us.&lt;br /&gt;
&lt;br /&gt;
In setupNewGame in Game.php, make sure the return is &amp;lt;code&amp;gt;return PlayerTurn::class;&amp;lt;/code&amp;gt; so the framework knows which game state is the initial one.&lt;br /&gt;
&lt;br /&gt;
===Test Your States===&lt;br /&gt;
... and start a new Reversi game.&lt;br /&gt;
&lt;br /&gt;
As you can see on the screen capture below, the BGA framework makes the game jump to our first game state &amp;lt;code&amp;gt;PlayerTurn&amp;lt;/code&amp;gt; right after the initial setup. That&#039;s why the status bar contains the description of &amp;lt;code&amp;gt;PlayDisc&amp;lt;/code&amp;gt; state (&amp;quot;XXXX must play a disc&amp;quot;):&lt;br /&gt;
&lt;br /&gt;
[[File:reversi7.jpg]]&lt;br /&gt;
&lt;br /&gt;
==The rules==&lt;br /&gt;
&lt;br /&gt;
We will use the &amp;lt;code&amp;gt;getPossibleMoves&amp;lt;/code&amp;gt; PHP method to: &lt;br /&gt;
*Indicate to the current player where she is allowed to play by returning a list of coordinates&lt;br /&gt;
*Check if the player has the right to play in the spot they choose&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
Example of &amp;lt;code&amp;gt;getPossibleMoves&amp;lt;/code&amp;gt; here https://gist.github.com/leocaseiro/a8bc2851bd0caddd06685b5035937d15&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is pure PHP programming here, and there are no special things from the BGA framework that can be used. This is why we won&#039;t go into details here. The overall idea is: &lt;br /&gt;
* Create a &amp;lt;code&amp;gt;getTurnedOverDiscs(x,y)&amp;lt;/code&amp;gt; method that returns coordinates of discs that would be turned over if a token would be played at &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;,&amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Loop through all free squares of the board and call the &amp;lt;code&amp;gt;getTurnedOverDiscs&amp;lt;/code&amp;gt; method on each of them. If at least 1 token is turned over, this is a valid move.&lt;br /&gt;
&lt;br /&gt;
IMPORTANT: Making a database query is slow! Please don&#039;t load the entire game board with a SQL query multiple times. In our implementation, we load the entire board once at the beginning of &amp;lt;code&amp;gt;getPossibleMoves&amp;lt;/code&amp;gt;, and then pass the board as an argument to all methods.&lt;br /&gt;
&lt;br /&gt;
If you want to look into details, please look at the &amp;quot;utility method&amp;quot; sections of &amp;lt;code&amp;gt;Game.php&amp;lt;/code&amp;gt;. If building the tutorial yourself, copy the functions under &amp;quot;Utility functions&amp;quot; comment from the Reversi tutorial.&lt;br /&gt;
&lt;br /&gt;
==Display allowed moves==&lt;br /&gt;
&lt;br /&gt;
Now we want to highlight the squares where the player can place a disc.&lt;br /&gt;
&lt;br /&gt;
To do this, we add a &amp;lt;code&amp;gt;getArgs&amp;lt;/code&amp;gt; method in &amp;lt;code&amp;gt;modules/php/States/PlayDisc.php&amp;lt;/code&amp;gt;. This method is called on the server each time we enter into &amp;lt;code&amp;gt;PlayDisc&amp;lt;/code&amp;gt; game state, and its result is transferred automatically to the client-side:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function getArgs(int $activePlayerId): array&lt;br /&gt;
{&lt;br /&gt;
    return [&lt;br /&gt;
        &#039;possibleMoves&#039; =&amp;gt; $this-&amp;gt;game-&amp;gt;boardManager-&amp;gt;getPossibleMoves($activePlayerId)&lt;br /&gt;
    ];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we need to handle it on the JS side.&lt;br /&gt;
Replace the state classes definition in the Game constructor by this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // Declare the State classes&lt;br /&gt;
        this.playDisc = new PlayDisc(this, bga);&lt;br /&gt;
        this.bga.states.register(&#039;PlayDisc&#039;, this.playDisc);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
So it matches the state we described in PHP. The state name is the PHP class name, that we use in register to link them.&lt;br /&gt;
&lt;br /&gt;
Replace the class PlayerTurn in &amp;lt;code&amp;gt;modules/js/Game.js&amp;lt;/code&amp;gt; by this one:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class PlayDisc {&lt;br /&gt;
    constructor(game, bga) {&lt;br /&gt;
        this.game = game;&lt;br /&gt;
        this.bga = bga;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    onEnteringState(args, isCurrentPlayerActive) {&lt;br /&gt;
        this.bga.statusBar.setTitle(isCurrentPlayerActive ? &lt;br /&gt;
            _(&#039;${you} must play a disc&#039;) :&lt;br /&gt;
            _(&#039;${actplayer} must play a disc&#039;)&lt;br /&gt;
        );&lt;br /&gt;
      &lt;br /&gt;
        if (isCurrentPlayerActive) {            &lt;br /&gt;
            this.updatePossibleMoves(args.possibleMoves);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Note:&amp;lt;/b&amp;gt; We set the title when entering the state, so you can now remove description/descriptionMyTurn in the PHP class that was just here for test purpose.&lt;br /&gt;
&lt;br /&gt;
We use the &amp;lt;code&amp;gt;getPossibleMoves&amp;lt;/code&amp;gt; method we just developed.&lt;br /&gt;
&lt;br /&gt;
Each time we enter into a new game state, we use the &amp;lt;code&amp;gt;onEnteringState&amp;lt;/code&amp;gt; Javascript method of the JS State class. This lets us use the data returned by the method above on the client side.&lt;br /&gt;
&lt;br /&gt;
So, when we enter into &amp;lt;code&amp;gt;PlayDisc&amp;lt;/code&amp;gt; game state, we call our &amp;lt;code&amp;gt;updatePossibleMoves&amp;lt;/code&amp;gt; method (under the &amp;quot;Utility methods&amp;quot; section). This method is added to PlayDisc class and looks like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
updatePossibleMoves(possibleMoves) {&lt;br /&gt;
    // Remove current possible moves&lt;br /&gt;
    document.querySelectorAll(&#039;.possibleMove&#039;).forEach(div =&amp;gt; div.classList.remove(&#039;possibleMove&#039;));&lt;br /&gt;
&lt;br /&gt;
    for( let x in possibleMoves ) {&lt;br /&gt;
        for( let y in possibleMoves[ x ] ) {&lt;br /&gt;
            // x,y is a possible move&lt;br /&gt;
            document.getElementById(`square_${x}_${y}`).classList.add(&#039;possibleMove&#039;);&lt;br /&gt;
        }            &lt;br /&gt;
    }&lt;br /&gt;
                &lt;br /&gt;
    this.bga.gameui.addTooltipToClass( &#039;possibleMove&#039;, &#039;&#039;, _(&#039;Place a disc here&#039;) );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Here&#039;s what this does. At first, it removes all &amp;lt;code&amp;gt;possibleMove&amp;lt;/code&amp;gt; classes currently applied with the very useful &amp;lt;code&amp;gt;document.querySelectorAll&amp;lt;/code&amp;gt; method.&lt;br /&gt;
&lt;br /&gt;
Then it loops through all possible moves our PHP &amp;lt;code&amp;gt;updatePossibleMoves&amp;lt;/code&amp;gt; function created for us, and adds the &amp;lt;code&amp;gt;possibleMove&amp;lt;/code&amp;gt; class to each corresponding square.&lt;br /&gt;
&lt;br /&gt;
Finally, it uses the BGA framework &amp;lt;code&amp;gt;addTooltipToClass&amp;lt;/code&amp;gt; method to associate a tooltip to all those highlighted squares so that players can understand their meaning.&lt;br /&gt;
&lt;br /&gt;
To see the possible moves we need to create a CSS class (&amp;lt;code&amp;gt;possibleMove&amp;lt;/code&amp;gt;) that can be applied to a &amp;lt;code&amp;gt;square&amp;lt;/code&amp;gt; element to highlight it:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.possibleMove {&lt;br /&gt;
    background-color: white;&lt;br /&gt;
    opacity: 0.2; &lt;br /&gt;
    cursor: pointer;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
And here we are:&lt;br /&gt;
&lt;br /&gt;
[[File:reversi8.jpg.jpg]]&lt;br /&gt;
&lt;br /&gt;
==Let&#039;s play ==&lt;br /&gt;
&lt;br /&gt;
From now, it&#039;s better to restart a game with 2 players, because we are going to implement a complete Reversi turn. The summary of what we are going to do is:&lt;br /&gt;
* When we click on a square, check if it is a &amp;lt;code&amp;gt;possibleMove&amp;lt;/code&amp;gt; and if so, send the move to the server.&lt;br /&gt;
*Server side, check the move is correct, apply Reversi rules and jump to next player.&lt;br /&gt;
*Client side, change the token position to reflect the move.&lt;br /&gt;
&lt;br /&gt;
First we associate each click on a square to one of our methods using our Javascript &amp;lt;code&amp;gt;setup&amp;lt;/code&amp;gt; method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
document.querySelectorAll(&#039;.square&#039;).forEach(square =&amp;gt; square.addEventListener(&#039;click&#039;, e =&amp;gt; this.onPlayDisc(e)));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, our &amp;quot;onPlayDisc&amp;quot; method is called each time someone clicks on a square.&lt;br /&gt;
&lt;br /&gt;
Here&#039;s our &amp;quot;onPlayDisc&amp;quot; method below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
onPlayDisc(evt) {&lt;br /&gt;
    // Stop this event propagation&lt;br /&gt;
    evt.preventDefault();&lt;br /&gt;
    evt.stopPropagation();&lt;br /&gt;
&lt;br /&gt;
    // Get the cliqued square x and y&lt;br /&gt;
    // Note: square id format is &amp;quot;square_X_Y&amp;quot;&lt;br /&gt;
    var coords = evt.currentTarget.id.split(&#039;_&#039;);&lt;br /&gt;
    var x = coords[1];&lt;br /&gt;
    var y = coords[2];&lt;br /&gt;
&lt;br /&gt;
    if(!document.getElementById(`square_${x}_${y}`).classList.contains(&#039;possibleMove&#039;)) {&lt;br /&gt;
        // This is not a possible move =&amp;gt; the click does nothing&lt;br /&gt;
        return ;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    this.bga.actions.performAction(&amp;quot;actPlayDisc&amp;quot;, {&lt;br /&gt;
        x:x,&lt;br /&gt;
        y:y&lt;br /&gt;
    });&lt;br /&gt;
},&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
What we do here is:&lt;br /&gt;
*We stop the propagation of the Javascript &amp;lt;code&amp;gt;onclick&amp;lt;/code&amp;gt; event. Otherwise, it can lead to random behavior so it&#039;s always a good idea.&lt;br /&gt;
*We get the x/y coordinates of the square by using &amp;lt;code&amp;gt;evt.currentTarget.id&amp;lt;/code&amp;gt;&lt;br /&gt;
*We check that clicked square has the &amp;lt;code&amp;gt;possibleMove&amp;lt;/code&amp;gt; class, otherwise we know for sure that we can&#039;t play there.&lt;br /&gt;
*Finally, we make a call to the server using BGA &amp;lt;code&amp;gt;bgaPerformAction&amp;lt;/code&amp;gt; method with argument x and y. This call will check that &amp;lt;code&amp;gt;actPlayDisc&amp;lt;/code&amp;gt; action is possible, according to current game state (we will add the &amp;lt;code&amp;gt;PossibleAction&amp;lt;/code&amp;gt; attribute on &amp;lt;code&amp;gt;actPlayDisc&amp;lt;/code&amp;gt; just after). This check is important to avoid issues if a player double clicks on a square.&lt;br /&gt;
&lt;br /&gt;
Now, we have to manage this &amp;lt;code&amp;gt;actPlayDisc&amp;lt;/code&amp;gt; action on the server side. Add a corresponding &amp;lt;code&amp;gt;actPlayDisc&amp;lt;/code&amp;gt; method in our &amp;lt;code&amp;gt;PlayDisc&amp;lt;/code&amp;gt; state class (&amp;lt;code&amp;gt;PlayDisc.php&amp;lt;/code&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#[PossibleAction]&lt;br /&gt;
function actPlayDisc( int $x, int $y, int $activePlayerId )&lt;br /&gt;
{&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
And add &amp;lt;code&amp;gt;use Bga\GameFramework\States\PossibleAction;&amp;lt;/code&amp;gt; at the top of the file.&lt;br /&gt;
&lt;br /&gt;
(The function will be called when the front-side action is triggered using the Autowire mechanism, if you want to see how it works in details check [[Main game logic: Game.php|https://en.doc.boardgamearena.com/Main_game_logic:_Game.php#Actions_%28autowired%29]] )&lt;br /&gt;
&lt;br /&gt;
now, we are using the &amp;lt;code&amp;gt;getTurnedOverDiscs&amp;lt;/code&amp;gt; method again to check that this move is possible:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // Now, check if this is a possible move&lt;br /&gt;
        $board = $this-&amp;gt;game-&amp;gt;boardManager-&amp;gt;getBoard();&lt;br /&gt;
        $turnedOverDiscs = $this-&amp;gt;game-&amp;gt;getTurnedOverDiscs( $x, $y, $activePlayerId, $board );&lt;br /&gt;
        &lt;br /&gt;
        if( count( $turnedOverDiscs ) === 0 ) {&lt;br /&gt;
            throw new SystemException(&amp;quot;Impossible move&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
we update the database to change the color of all turned over disc + the disc we just placed:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            // Let&#039;s place a disc at x,y and return all &amp;quot;$returned&amp;quot; discs to the active player&lt;br /&gt;
            &lt;br /&gt;
            $this-&amp;gt;game-&amp;gt;boardManager-&amp;gt;applyMove($activePlayerId, $x, $y, $turnedOverDiscs);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We add this part to the BoardManager before going back to our act function:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function applyMove(int $playerId, int $x, int $y, array $turnedOverDiscs): void&lt;br /&gt;
    {&lt;br /&gt;
        $positions = [];&lt;br /&gt;
        foreach ($turnedOverDiscs as $turnedOver) {&lt;br /&gt;
            $turnedX = (int) $turnedOver[&#039;x&#039;];&lt;br /&gt;
            $turnedY = (int) $turnedOver[&#039;y&#039;];&lt;br /&gt;
            $positions[] = &amp;quot;($turnedX,$turnedY)&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        $positions[] = &amp;quot;($x,$y)&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        $sql = &amp;quot;UPDATE `board` SET `board_player` = $playerId WHERE (`board_x`, `board_y`) IN (&amp;quot; . implode(&#039;,&#039;, $positions) . &amp;quot;)&amp;quot;;&lt;br /&gt;
        $this-&amp;gt;game-&amp;gt;DbQuery($sql);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
define what game statistics we want to record (for more details read [[Game statistics: stats.json]]), For this tutorial we need to edit the stats.json file and set it to the following:&lt;br /&gt;
 {&lt;br /&gt;
   &amp;quot;table&amp;quot;: [],&lt;br /&gt;
   &amp;quot;player&amp;quot;: {&lt;br /&gt;
     &amp;quot;discPlayedOnCorner&amp;quot;: {&lt;br /&gt;
       &amp;quot;id&amp;quot;: 10,&lt;br /&gt;
       &amp;quot;name&amp;quot;: &amp;quot;Discs played on a corner&amp;quot;,&lt;br /&gt;
       &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;&lt;br /&gt;
     },&lt;br /&gt;
     &amp;quot;discPlayedOnBorder&amp;quot;: {&lt;br /&gt;
       &amp;quot;id&amp;quot;: 11,&lt;br /&gt;
       &amp;quot;name&amp;quot;: &amp;quot;Discs played on a border&amp;quot;,&lt;br /&gt;
       &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;&lt;br /&gt;
     },&lt;br /&gt;
     &amp;quot;discPlayedOnCenter&amp;quot;: {&lt;br /&gt;
       &amp;quot;id&amp;quot;: 12,&lt;br /&gt;
       &amp;quot;name&amp;quot;: &amp;quot;Discs played on board center part&amp;quot;,&lt;br /&gt;
       &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;&lt;br /&gt;
     },&lt;br /&gt;
     &amp;quot;turnedOver&amp;quot;: {&lt;br /&gt;
       &amp;quot;id&amp;quot;: 13,&lt;br /&gt;
       &amp;quot;name&amp;quot;: &amp;quot;Number of discs turned over&amp;quot;,&lt;br /&gt;
       &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
we manage game statistics:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $this-&amp;gt;bga-&amp;gt;playerStats-&amp;gt;inc(&#039;turnedOver&#039;, $disc_count, $activePlayerId);&lt;br /&gt;
        $updatedStat = &#039;discPlayedOnCenter&#039;;&lt;br /&gt;
        if( ($x==1 &amp;amp;&amp;amp; $y==1) || ($x==$board_size &amp;amp;&amp;amp; $y==1) || ($x==1 &amp;amp;&amp;amp; $y==$board_size) || ($x==$board_size &amp;amp;&amp;amp; $y==$board_size) ) {&lt;br /&gt;
            $updatedStat = &#039;discPlayedOnCorner&#039;;&lt;br /&gt;
        } else if( $x==1 || $x==$board_size || $y==1 || $y==$board_size ) {&lt;br /&gt;
            $updatedStat = &#039;discPlayedOnBorder&#039;;&lt;br /&gt;
        }&lt;br /&gt;
        $this-&amp;gt;bga-&amp;gt;playerStats-&amp;gt;inc($updatedStat, 1, $activePlayerId);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notify the turned discs:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            // Notify&lt;br /&gt;
            $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all( &amp;quot;playDisc&amp;quot;, clienttranslate( &#039;${player_name} plays a disc and turns over ${returned_nbr} disc(s)&#039; ), array(&lt;br /&gt;
                &#039;player_id&#039; =&amp;gt; $activePlayerId,&lt;br /&gt;
                &#039;player_name&#039; =&amp;gt; $this-&amp;gt;game-&amp;gt;getPlayerNameById($activePlayerId),&lt;br /&gt;
                &#039;returned_nbr&#039; =&amp;gt; count( $turnedOverDiscs ),&lt;br /&gt;
                &#039;x&#039; =&amp;gt; $x,&lt;br /&gt;
                &#039;y&#039; =&amp;gt; $y&lt;br /&gt;
            ) );&lt;br /&gt;
&lt;br /&gt;
            $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all( &amp;quot;turnOverDiscs&amp;quot;, &#039;&#039;, array(&lt;br /&gt;
                &#039;player_id&#039; =&amp;gt; $activePlayerId,&lt;br /&gt;
                &#039;turnedOver&#039; =&amp;gt; $turnedOverDiscs&lt;br /&gt;
            ) );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then we update the scores accordingly&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $playerIds = array_keys($this-&amp;gt;game-&amp;gt;loadPlayersBasicInfos());&lt;br /&gt;
        foreach ($playerIds as $playerId) {&lt;br /&gt;
            $tokens = $this-&amp;gt;game-&amp;gt;boardManager-&amp;gt;countDiscsForPlayer((int) $playerId);&lt;br /&gt;
            $this-&amp;gt;bga-&amp;gt;playerScore-&amp;gt;set($playerId, $tokens); // this will update the JS counter automatically&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In BoardManager, add:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function countDiscsForPlayer(int $playerId): int&lt;br /&gt;
    {&lt;br /&gt;
        return (int) $this-&amp;gt;game-&amp;gt;getUniqueValueFromDB(&amp;quot;SELECT COUNT(*) FROM `board` WHERE `board_player` = $playerId&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We are using for that 2 notifications (&amp;lt;code&amp;gt;playDisc&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;turnOverDiscs&amp;lt;/code&amp;gt;  that we are going to implement on client side later). Note that the description of the &amp;lt;code&amp;gt;playDisc&amp;lt;/code&amp;gt; notification will be logged in the game log.&lt;br /&gt;
&lt;br /&gt;
Finally, we jump to the next game state.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // Then, go to the next state&lt;br /&gt;
        return NextPlayer::class;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To make the statistics work, we have to initialize them in &amp;lt;code&amp;gt;stats.json&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;player&amp;quot;: {&lt;br /&gt;
    &amp;quot;discPlayedOnCorner&amp;quot;: {&lt;br /&gt;
      &amp;quot;id&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;Discs played on a corner&amp;quot;,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;discPlayedOnBorder&amp;quot;: {&lt;br /&gt;
      &amp;quot;id&amp;quot;: 11,&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;Discs played on a border&amp;quot;,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;discPlayedOnCenter&amp;quot;: {&lt;br /&gt;
      &amp;quot;id&amp;quot;: 12,&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;Discs played on board center part&amp;quot;,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;turnedOver&amp;quot;: {&lt;br /&gt;
      &amp;quot;id&amp;quot;: 13,&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;Number of discs turned over&amp;quot;,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A last thing to do on the server side is to activate the next player when we enter the &amp;lt;code&amp;gt;NextPlayer&amp;lt;/code&amp;gt; game state (in the &amp;lt;code&amp;gt;modules/php/States/NextPlayer.php&amp;lt;/code&amp;gt; file):&lt;br /&gt;
&lt;br /&gt;
Add `const ST_END_GAME = 99;` at the top of the file, then add:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function onEnteringState(): void&lt;br /&gt;
    {&lt;br /&gt;
        // Active next player&lt;br /&gt;
        $player_id = intval($this-&amp;gt;game-&amp;gt;activeNextPlayer());&lt;br /&gt;
&lt;br /&gt;
        // Check if both player has at least 1 discs, and if there are free squares to play&lt;br /&gt;
        $player_to_discs = $this-&amp;gt;game-&amp;gt;getCollectionFromDb( &amp;quot;SELECT board_player, COUNT( board_x )&lt;br /&gt;
                                                       FROM board&lt;br /&gt;
                                                       GROUP BY board_player&amp;quot;, true );&lt;br /&gt;
&lt;br /&gt;
        if( ! isset( $player_to_discs[ null ] ) )&lt;br /&gt;
        {&lt;br /&gt;
            // Index 0 has not been set =&amp;gt; there&#039;s no more free place on the board !&lt;br /&gt;
            // =&amp;gt; end of the game&lt;br /&gt;
            return ST_END_GAME;&lt;br /&gt;
        }&lt;br /&gt;
        else if( ! isset( $player_to_discs[ $player_id ] ) )&lt;br /&gt;
        {&lt;br /&gt;
            // Active player has no more disc on the board =&amp;gt; he looses immediately&lt;br /&gt;
            return ST_END_GAME;&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        // Can this player play?&lt;br /&gt;
&lt;br /&gt;
        $possibleMoves = $this-&amp;gt;game-&amp;gt;getPossibleMoves( $player_id );&lt;br /&gt;
        if( count( $possibleMoves ) == 0 )&lt;br /&gt;
        {&lt;br /&gt;
&lt;br /&gt;
            // This player can&#039;t play&lt;br /&gt;
            // Can his opponent play ?&lt;br /&gt;
            $opponent_id = (int)$this-&amp;gt;game-&amp;gt;getUniqueValueFromDb( &amp;quot;SELECT player_id FROM player WHERE player_id!=&#039;$player_id&#039; &amp;quot; );&lt;br /&gt;
            if( count( $this-&amp;gt;game-&amp;gt;getPossibleMoves( $opponent_id ) ) == 0 )&lt;br /&gt;
            {&lt;br /&gt;
                // Nobody can move =&amp;gt; end of the game&lt;br /&gt;
                return ST_END_GAME;&lt;br /&gt;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {            &lt;br /&gt;
                // =&amp;gt; pass his turn&lt;br /&gt;
                return NextPlayer::class;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            // This player can play. Give him some extra time&lt;br /&gt;
            $this-&amp;gt;game-&amp;gt;giveExtraTime( $player_id );&lt;br /&gt;
            return PlayDisc::class;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, when we play a token, the rules are checked and the token appears in the database.&lt;br /&gt;
&lt;br /&gt;
[[File:reversi9.jpg]]&lt;br /&gt;
&lt;br /&gt;
Of course, as we don&#039;t manage notifications on client side, we need to press F5 after each move to see the changes on the board.&lt;br /&gt;
&lt;br /&gt;
==Make the move appear automatically==&lt;br /&gt;
&lt;br /&gt;
Now, what we have to do is process the notifications sent by the server and make the move appear on the interface.&lt;br /&gt;
&lt;br /&gt;
In our &amp;lt;code&amp;gt;setupNotifications&amp;lt;/code&amp;gt; method in &amp;lt;code&amp;gt;modules/js/Game.js&amp;lt;/code&amp;gt;, we tell BGA to listen to notifications we will add later (&amp;lt;code&amp;gt;playDisc&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;turnOverDiscs&amp;lt;/code&amp;gt;) using promise-based notifications:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        setupNotifications() {&lt;br /&gt;
            console.log( &#039;notifications subscriptions setup&#039; );&lt;br /&gt;
&lt;br /&gt;
            // automatically listen to the notifications, based on the `notif_xxx` function on this class.&lt;br /&gt;
            this.bga.notifications.setupPromiseNotifications();&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We will associate each of our 3 notifications with a method prefixed with &amp;lt;code&amp;gt;notif_&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Let&#039;s have a look now on the &amp;lt;code&amp;gt;playDisc&amp;lt;/code&amp;gt; notification handler method:&amp;lt;pre&amp;gt;&lt;br /&gt;
        async notif_playDisc(args) {&lt;br /&gt;
            // Remove current possible moves (makes the board more clear)&lt;br /&gt;
            document.querySelectorAll(&#039;.possibleMove&#039;).forEach(div =&amp;gt; div.classList.remove(&#039;possibleMove&#039;));&lt;br /&gt;
        &lt;br /&gt;
            await this.addDiscOnBoard( args.x, args.y, args.player_id );&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
No surprise here, we re-used some existing stuff to: &lt;br /&gt;
*Remove the highlighted squares.&lt;br /&gt;
* Add a new disc on board, coming from player panel. That&#039;s where the async await part of &amp;lt;code&amp;gt;addDiscOnBoard&amp;lt;/code&amp;gt; becomes useful.&lt;br /&gt;
&lt;br /&gt;
Now, here&#039;s the method that handles the &amp;lt;code&amp;gt;turnOverDiscs&amp;lt;/code&amp;gt; notification:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        async animateTurnOverDisc(disc, targetColor) {&lt;br /&gt;
            const squareDiv = document.getElementById(`square_${disc.x}_${disc.y}`);&lt;br /&gt;
            const discDiv = document.getElementById(`disc_${disc.x}_${disc.y}`);&lt;br /&gt;
            &lt;br /&gt;
            squareDiv.classList.add(&#039;flip-animation&#039;);&lt;br /&gt;
            await this.bga.gameui.wait(500); // for the flip animation to finish&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            discDiv.dataset.color = targetColor;&lt;br /&gt;
&lt;br /&gt;
            const parallelAnimations = [{&lt;br /&gt;
                keyframes: [ // flip the disc&lt;br /&gt;
                    { transform: `rotateY(180deg)` },&lt;br /&gt;
                    { transform: `rotateY(0deg)` },&lt;br /&gt;
                ]&lt;br /&gt;
            }, {&lt;br /&gt;
                keyframes: [ // lift the disc&lt;br /&gt;
                    { transform: `translate(0, -12px) scale(1.2)`, offset: 0.5 },&lt;br /&gt;
                ]&lt;br /&gt;
            }];&lt;br /&gt;
&lt;br /&gt;
            await this.animationManager.slideAndAttach(discDiv, squareDiv, { duration: 1000, parallelAnimations });&lt;br /&gt;
            &lt;br /&gt;
            squareDiv.classList.remove(&#039;flip-animation&#039;);&lt;br /&gt;
            await this.bga.gameui.wait(500); // for the flip animation removal to finish&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        async notif_turnOverDiscs(args) {&lt;br /&gt;
            // Get the color of the player who is returning the discs&lt;br /&gt;
            const targetColor = this.gamedatas.players[ args.player_id ].color;&lt;br /&gt;
&lt;br /&gt;
            // wait for the animations of all turned discs to be over before considering the notif done&lt;br /&gt;
            await Promise.all(&lt;br /&gt;
                args.turnedOver.map(disc =&amp;gt; &lt;br /&gt;
                    this.animateTurnOverDisc(disc, targetColor)&lt;br /&gt;
                )&lt;br /&gt;
            );&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The list of the discs to be turned over has been made available by our server side code in &amp;lt;code&amp;gt;args.turnedOver&amp;lt;/code&amp;gt; (see previous paragraph). We loop through all these discs, and trigger a single disc animation using &amp;lt;code&amp;gt;animateTurnOverDisc&amp;lt;/code&amp;gt;. This function creates a complex animation using [[BgaAnimations]] for the disc in parameter.&lt;br /&gt;
&lt;br /&gt;
We create a chain of 3 animations using await to make the square highlight, then flip the token, then remove the square highlight.&lt;br /&gt;
&lt;br /&gt;
==Implement the zombie mode ==&lt;br /&gt;
When a player leave the table, a Zombie (bot) will play for the leaver, so the table can continue. For that, we write code in the zombieTurn function.&lt;br /&gt;
&lt;br /&gt;
to learn more about the Zombie mode, read [[Zombie Mode]]&lt;br /&gt;
&lt;br /&gt;
Add this at the end of your PlayDisc.php file :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function zombie(int $playerId) {&lt;br /&gt;
        // Zombie level 1&lt;br /&gt;
        $possibleMoves = $this-&amp;gt;game-&amp;gt;boardManager-&amp;gt;getPossibleMoves($playerId);&lt;br /&gt;
        // transform the 2 dimensional array into a flat array of possible [$x, $y]&lt;br /&gt;
        $possibleMovesArray = [];&lt;br /&gt;
        foreach($possibleMoves as $x =&amp;gt; $ys) {&lt;br /&gt;
            foreach($ys as $y =&amp;gt; $valid) {&lt;br /&gt;
                $possibleMovesArray[] = [$x, $y];&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $zombieChoice = $this-&amp;gt;getRandomZombieChoice($possibleMovesArray);&lt;br /&gt;
        return $this-&amp;gt;actPlayDisc($zombieChoice[0], $zombieChoice[1], $playerId);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And that&#039;s it ! Everytime a leaver should be playing, the Zombie will random play one of the possible moves, and the remaining player will be able to play the next move.&lt;br /&gt;
&lt;br /&gt;
== Add a debug function to help up test the code==&lt;br /&gt;
Add this at the end of your Game.php file :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    function debug_playAutomatically(int $moves = 50) {&lt;br /&gt;
        $this-&amp;gt;bga-&amp;gt;debug-&amp;gt;playUntil(fn(int $count) =&amp;gt; $count === $moves);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function debug_playToEndGame() {&lt;br /&gt;
        $this-&amp;gt;debug_playAutomatically(64); // reversi max moves is under 64 for the standard size board&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This function plays automatically until we reach the end of the game. The functions starting with &amp;quot;debug_&amp;quot; can be triggered in the Studio with a special menu.&lt;br /&gt;
It uses the tool &amp;lt;code&amp;gt;$this-&amp;gt;debug-&amp;gt;playUntil&amp;lt;/code&amp;gt; that will trigger zombie actions, but you could put any code in the debug function for the tests you need.&lt;br /&gt;
&lt;br /&gt;
Start a new game, click on the Bug icon on the top left then &amp;quot;playToEndGame&amp;quot;. You should see the game randomly playing until it reaches the end game, so it helps you check the animations, and you can see if the final scoring is also working as expected. This example will allow us to easily test the Zombie code, as it will play as a Zombie for all players until the end of the game. It&#039;s also an easy way to see if end score computation is working as expected!&lt;br /&gt;
&lt;br /&gt;
You can create as many debug function as you like!&lt;br /&gt;
&lt;br /&gt;
==Optional: add unit tests==&lt;br /&gt;
Follow the instruction on https://en.doc.boardgamearena.com/Testing_by_developer#PHP to setup phpunit and create the first Reversi unit tests that are given as examples there.&lt;br /&gt;
&lt;br /&gt;
Running &amp;lt;code&amp;gt;phpunit&amp;lt;/code&amp;gt;, it should be 4/4 OK! And we have a good hint the case of the full board should be handled OK without actually completing a game :)&lt;br /&gt;
&lt;br /&gt;
==After the tutorial==&lt;br /&gt;
You might want to check another tutorial, or start working on your first real project !&lt;br /&gt;
&lt;br /&gt;
[[Create a game in BGA Studio: Complete Walkthrough]]&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>RichardSPeters</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Tutorial_reversi&amp;diff=29172</id>
		<title>Tutorial reversi</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Tutorial_reversi&amp;diff=29172"/>
		<updated>2026-03-26T21:23:28Z</updated>

		<summary type="html">&lt;p&gt;RichardSPeters: in the State Machine section remov confusion  re PlayDisc and PlayerTurn&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Using this tutorial, you can build a complete working game on the BGA environment: Reversi.&lt;br /&gt;
&lt;br /&gt;
Before you read this tutorial, you must:&lt;br /&gt;
* Read the overall presentations of the BGA Framework ([[Studio|see here]]).&lt;br /&gt;
* Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi].&lt;br /&gt;
* Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript&lt;br /&gt;
* &#039;&#039;&#039;Setup your development environment&#039;&#039;&#039; [http://en.doc.boardgamearena.com/First_steps_with_BGA_Studio First Steps with BGA Studio]&lt;br /&gt;
&lt;br /&gt;
== Create your first game ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: you should already have created a project following instructions in [[First_steps_with_BGA_Studio#Create_a_new_game_project|Create a new game project]]. While you will find a &#039;&#039;&#039;reversi&#039;&#039;&#039; directory in your SFTP folder, do not use it for this tutorial. Instead, use the project you have created as an (empty) starting point.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
With the initial skeleton of code provided in your project, you can already start a game from the BGA Studio:&lt;br /&gt;
* Go to your [https://studio.boardgamearena.com/controlpanel studio Control panel], then Manage games and select your initial project. &#039;&#039;Note: there are warnings displayed about a missing BGG_ID and presentation text. You can ignore that for now.&#039;&#039;&lt;br /&gt;
* Click the Play link next to your project name. This will open the Play page and offer to create a new table for your project. &#039;&#039;Optional: click the Heart icon to add your project to your favorite games list.&#039;&#039;&lt;br /&gt;
* On the Play page, on the top of the page, make sure that your settings are &amp;quot;Simple game&amp;quot;, &amp;quot;Real time&amp;quot; and &amp;quot;Manual&amp;quot;.&lt;br /&gt;
* Click &amp;quot;Create table&amp;quot; to create a table of your project. &lt;br /&gt;
* For now, we are going to work with one player only, so use the (-) button to set the number of players to 2. Most of the time it is simpler to proceed with only one player during the early phase of development of your game, as it&#039;s easy and fast to start/stop games. By default the game requires 2 players, so press &amp;quot;Express Start&amp;quot;. It will open another tab with another of your players.&lt;br /&gt;
* Reminder: Always use the &amp;quot;Express Start&amp;quot; button to start the game.&lt;br /&gt;
&lt;br /&gt;
Thus, you can start a &amp;quot;Reversi&amp;quot; game, and arrive on a void, empty game. Yeah.&lt;br /&gt;
&lt;br /&gt;
End the game by clicking on the game options icon on the top right, and then on &amp;quot;Express Stop&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Editing the game information (Optional) ==&lt;br /&gt;
&lt;br /&gt;
This step is optional and will fix the warnings on the project page (missing BGG_ID and presentation).&lt;br /&gt;
&lt;br /&gt;
==== Edits to fix Errors ====&lt;br /&gt;
* Edit your local copy of the &amp;lt;code&amp;gt;gameinfos.inc.php&amp;lt;/code&amp;gt; file:&lt;br /&gt;
** Change the &amp;lt;code&amp;gt;bgg_id&amp;lt;/code&amp;gt; value from &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;2389&amp;lt;/code&amp;gt; - that&#039;s around line 26.&lt;br /&gt;
** Add &amp;lt;code&amp;gt;1,&amp;lt;/code&amp;gt; to the players array (so you can start 1-player games while testing) - that&#039;s around line 29.&lt;br /&gt;
* Upload the &amp;lt;code&amp;gt;gameinfos.inc.php&amp;lt;/code&amp;gt; file to the SFTP server (see [[First_steps_with_BGA_Studio#Connect_to_your_SFTP_folder|Connect to your SFTP folder]]).&lt;br /&gt;
&lt;br /&gt;
==== Test your Edits ====&lt;br /&gt;
* Go back to your project page, and in the the local version, click Game page and check if the information match what you changed in the gameinfos file. [[File:Studio local version block.png|frameless]]&lt;br /&gt;
&lt;br /&gt;
===== Not working? =====&lt;br /&gt;
Some changes will require bypassing the cache (adding/changing images). It is often worth doing a hard refresh (Ctrl+F5) to make sure the latest version is used.&lt;br /&gt;
&lt;br /&gt;
== Make sure the IDE is properly configured ==&lt;br /&gt;
Try to add some invalid values in the framework names like this :&lt;br /&gt;
&lt;br /&gt;
[[File:Voluntary reversi tutorial ide error.png]]&lt;br /&gt;
&lt;br /&gt;
The error should be underlined if your IDE is properly configured. If it is not, check [[Setting up BGA Development environment using VSCode]]&lt;br /&gt;
&lt;br /&gt;
In the same fashion, the autocomplete of the IDE should propose you some values after &amp;lt;code&amp;gt;this-&amp;gt;bga-&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
You can also check if the upload works as expected, the game should now work and display the error (Undefined property: Bga\Games\EmptyGame\Game::$bgaTEST ...).&lt;br /&gt;
&lt;br /&gt;
Revert to the correct syntax.&lt;br /&gt;
&lt;br /&gt;
== Make it look like Reversi ==&lt;br /&gt;
&lt;br /&gt;
Let&#039;s start with the board. This will give you a good idea of how things will look and where tokens should go.&lt;br /&gt;
&lt;br /&gt;
Be careful designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case).&lt;br /&gt;
&lt;br /&gt;
For Reversi, it&#039;s useless to have a 750x750px board - much too big, so we chose this one which fit perfectly (536x528):&lt;br /&gt;
&lt;br /&gt;
[[File:Board.jpg]]&lt;br /&gt;
&lt;br /&gt;
Note that we are using a jpg file. Jpg files are lighter than png, so they are faster to load. Later, we are going to use PNGs for tokens because they allow for transparency.&lt;br /&gt;
&lt;br /&gt;
==== Add the board ====&lt;br /&gt;
use lowercase file names&lt;br /&gt;
* upload &amp;lt;code&amp;gt;board.jpg&amp;lt;/code&amp;gt; in your &amp;lt;code&amp;gt;img/&amp;lt;/code&amp;gt; directory. &lt;br /&gt;
* edit &amp;lt;code&amp;gt;modules/js/Game.js&amp;lt;/code&amp;gt; to add the &amp;lt;code&amp;gt;div&amp;lt;/code&amp;gt; for your board at the beginning of the setup function.&lt;br /&gt;
&lt;br /&gt;
Note: If you are building this game by following the tutorial, you will have a different project name than &amp;lt;code&amp;gt;reversi&amp;lt;/code&amp;gt; (i.e. &amp;lt;code&amp;gt;mygame.css&amp;lt;/code&amp;gt;). The file names in your project will be different than shown in this tutorial, replacing &amp;lt;code&amp;gt;reversi&amp;lt;/code&amp;gt; with your project name. Be sure that any code (other than comments) that references &amp;lt;code&amp;gt;reversi&amp;lt;/code&amp;gt; is changed to your actual project name.&lt;br /&gt;
&lt;br /&gt;
Edit the Game.js file and replace the code after &#039;&#039;&#039;// Example to add a div on the game area&#039;&#039;&#039; with the following:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.bga.gameArea.getElement().insertAdjacentHTML(&#039;beforeend&#039;, `&lt;br /&gt;
  &amp;lt;div id=&amp;quot;board&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
`);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: example above use backticks, aka string template literals. This technique used a lot in code of games as it allows to easily create html with strings substitutions, use quotes, double quotes and newlines. &lt;br /&gt;
If you don&#039;t know what it is check here&lt;br /&gt;
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals&lt;br /&gt;
&lt;br /&gt;
*edit your &amp;lt;code&amp;gt;reversi.css&amp;lt;/code&amp;gt; file to transform it into a visible board:&lt;br /&gt;
&lt;br /&gt;
 #board {&lt;br /&gt;
    width: 536px;&lt;br /&gt;
    height: 528px;&lt;br /&gt;
    background-image: url(&#039;img/board.jpg&#039;);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
and remove the code in Game.js in method onEnteringState that refers to adding action buttons and remove the section under &amp;lt;code&amp;gt;// example of adding a div for each player&amp;quot;&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important: refresh your page.&#039;&#039;&#039; Here&#039;s your board:[[File:reversi1.jpg]]&lt;br /&gt;
&lt;br /&gt;
If the board does not appear, refresh the page (always do this when you update the CSS file), and check the image filename. Remember file names are case sensitive!&lt;br /&gt;
&lt;br /&gt;
==== Code the Grid====&lt;br /&gt;
Now, we need to create some invisible HTML elements where squares are. These elements will be used as position references for the white and black tokens. &lt;br /&gt;
&lt;br /&gt;
=====Build the grid of squares=====&lt;br /&gt;
The board is 8 squares by 8 squares. This means we need 64 squares. To avoid writing 64 individual &amp;lt;code&amp;gt;div&amp;lt;/code&amp;gt; elements on our template, we are going to generate the squares on JS &amp;lt;code&amp;gt;setup&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
We&#039;ll do this in our Javascript &amp;lt;code&amp;gt;setup&amp;lt;/code&amp;gt; method under &amp;lt;code&amp;gt;// TODO: Set up your game interface here, according to &amp;quot;gamedatas&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const board = document.getElementById(&#039;board&#039;);&lt;br /&gt;
const hor_scale = 64.8;&lt;br /&gt;
const ver_scale = 64.4;&lt;br /&gt;
for (let x=1; x&amp;lt;=8; x++) {&lt;br /&gt;
    for (let y=1; y&amp;lt;=8; y++) {&lt;br /&gt;
        const left = Math.round((x - 1) * hor_scale + 10);&lt;br /&gt;
        const top = Math.round((y - 1) * ver_scale + 7);&lt;br /&gt;
        // we use afterbegin to make sure squares are placed before discs&lt;br /&gt;
        board.insertAdjacentHTML(`afterbegin`, `&amp;lt;div id=&amp;quot;square_${x}_${y}&amp;quot; class=&amp;quot;square&amp;quot; style=&amp;quot;left: ${left}px; top: ${top}px;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;`);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: as you can see, squares in our &amp;lt;code&amp;gt;board.jpg&amp;lt;/code&amp;gt; files do not have an exact width/height in pixels, and that&#039;s the reason we are using floating point numbers here.&lt;br /&gt;
&lt;br /&gt;
=====Style Those Squares=====&lt;br /&gt;
Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#board {&lt;br /&gt;
    width: 536px;&lt;br /&gt;
    height: 528px;&lt;br /&gt;
    background-image: url(&#039;img/board.jpg&#039;);&lt;br /&gt;
    position: relative;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.square {&lt;br /&gt;
    width: 62px;&lt;br /&gt;
    height: 62px;&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    background-color: red;&lt;br /&gt;
    display: flex;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Explanations:&lt;br /&gt;
*With &amp;quot;&amp;lt;code&amp;gt;position: relative&amp;lt;/code&amp;gt;&amp;quot; on board, we ensure square elements are positioned relatively to board.&lt;br /&gt;
*&amp;lt;code&amp;gt;background-color: red;&amp;lt;/code&amp;gt; is used for testing. This allows us to see the invisible elements. (You could instead do something like &amp;lt;code&amp;gt;outline: 2px solid orange;&amp;lt;/code&amp;gt; have fun and be creative)&lt;br /&gt;
*The display flex with center will place the disc in the center of the square.&lt;br /&gt;
&lt;br /&gt;
Let&#039;s refresh and check our (beautiful) squares:&lt;br /&gt;
&lt;br /&gt;
[[File:reversi2.jpg]]&lt;br /&gt;
&lt;br /&gt;
Now that you know the squares are there, you can remove the test line &amp;lt;code&amp;gt;background-color: red;&amp;lt;/code&amp;gt;  from your &amp;lt;code&amp;gt;.square&amp;lt;/code&amp;gt; class in the CSS stylesheet.&lt;br /&gt;
&lt;br /&gt;
===== Not Working?=====&lt;br /&gt;
If the styled squares do not appear, inspect and check your css (Chrome DevTools: Application &amp;gt; Frames &amp;gt; top &amp;gt; Stylesheets &amp;gt; reversi.css). &lt;br /&gt;
&lt;br /&gt;
==The Tokens==&lt;br /&gt;
&lt;br /&gt;
Now, our board is ready for some tokens!&lt;br /&gt;
&lt;br /&gt;
[Note: Throughout this tutorial, sometimes &amp;quot;tokens&amp;quot; is used, and sometimes &amp;quot;discs&amp;quot; is used. They are often swapped if you&#039;re looking at code in the reversi example project.]&lt;br /&gt;
&lt;br /&gt;
===Build the Token===&lt;br /&gt;
There are quite a few steps before the tokens will appear. You may be used to testing after every change, but that won&#039;t work well here. The token will &#039;&#039;&#039;not&#039;&#039;&#039; show until you have add styles to the css, utility method in the js,  adjusted the php file, and added the token to the board in the js file.&lt;br /&gt;
&lt;br /&gt;
====Add Token to img directory ====&lt;br /&gt;
Here&#039;s a new piece of art with the tokens. We need transparency here so we are using a png file:&lt;br /&gt;
&lt;br /&gt;
[[File:tokens.png]]&lt;br /&gt;
&lt;br /&gt;
Upload this image file &amp;lt;code&amp;gt;tokens.png&amp;lt;/code&amp;gt; in your &amp;lt;code&amp;gt;img/&amp;lt;/code&amp;gt; directory.  NB: may need to manually upload as have found that the automatic SFTP does&#039;t seem to pick up images.&lt;br /&gt;
&lt;br /&gt;
Important Fun Fact: we are using ONE file for both tokens. It is really important to use a minimum number of graphic files for your game. This is called the &amp;quot;CSS sprite&amp;quot; technique, because it makes the game load faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites].&lt;br /&gt;
&lt;br /&gt;
====Style the Tokens in .css file ====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.disc {&lt;br /&gt;
    position: relative;&lt;br /&gt;
    width: 54px;&lt;br /&gt;
    height: 54px;&lt;br /&gt;
    z-index: 3;&lt;br /&gt;
    transform-style: preserve-3d;&lt;br /&gt;
}&lt;br /&gt;
.disc .disc-faces { &lt;br /&gt;
    transform-style: preserve-3d;&lt;br /&gt;
}&lt;br /&gt;
.disc[data-color=&amp;quot;000000&amp;quot;] .disc-faces { &lt;br /&gt;
    transform: rotateY(180deg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.disc-face {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    width: 54px;&lt;br /&gt;
    height: 54px;&lt;br /&gt;
    background-image: url(&#039;img/tokens.png&#039;);&lt;br /&gt;
    background-size: auto 100%;&lt;br /&gt;
    backface-visibility: hidden;&lt;br /&gt;
}&lt;br /&gt;
.disc-face[data-side=&amp;quot;white&amp;quot;] { &lt;br /&gt;
    background-position-x: 0%; &lt;br /&gt;
    transform: rotateY(0deg);&lt;br /&gt;
}&lt;br /&gt;
.disc-face[data-side=&amp;quot;black&amp;quot;] { &lt;br /&gt;
    background-position-x: 100%; &lt;br /&gt;
    transform: rotateY(180deg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With this CSS code, we can set and change the token color by changing the &amp;lt;code&amp;gt;data-color&amp;lt;/code&amp;gt; attribute. Using data instead of a class ensures it can be only one of them (the disc cannot be black and white at the same time).&lt;br /&gt;
We will create a disc-faces div in the disc containing 2 disc-face divs, to represent each face of the disc. It will allow us to have a nice flip animation!&lt;br /&gt;
&lt;br /&gt;
====Add Token Utility Method in .js file====&lt;br /&gt;
Now, let&#039;s make the first token appear on our board. Tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, using a template string&lt;br /&gt;
&lt;br /&gt;
Let&#039;s create a method in our Javascript code (in the &amp;lt;code&amp;gt;modules/js/Game.js&amp;lt;/code&amp;gt; file) that will make a token appear on the board, using this template. Add under the section &amp;lt;code&amp;gt;//// Utility methods&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        async addDiscOnBoard( x, y, playerId, animate = true )&lt;br /&gt;
        {&lt;br /&gt;
            const color = this.gamedatas.players[ playerId ].color;&lt;br /&gt;
            const discId = `disc_${x}_${y}`;&lt;br /&gt;
&lt;br /&gt;
            document.getElementById(`square_${x}_${y}`).insertAdjacentHTML(&#039;beforeend&#039;, `&lt;br /&gt;
                &amp;lt;div class=&amp;quot;disc&amp;quot; data-color=&amp;quot;${color}&amp;quot; id=&amp;quot;${discId}&amp;quot;&amp;gt;&lt;br /&gt;
                    &amp;lt;div class=&amp;quot;disc-faces&amp;quot;&amp;gt;&lt;br /&gt;
                        &amp;lt;div class=&amp;quot;disc-face&amp;quot; data-side=&amp;quot;white&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
                        &amp;lt;div class=&amp;quot;disc-face&amp;quot; data-side=&amp;quot;black&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
                    &amp;lt;/div&amp;gt;&lt;br /&gt;
                &amp;lt;/div&amp;gt;&lt;br /&gt;
            `);&lt;br /&gt;
&lt;br /&gt;
            if (animate) {&lt;br /&gt;
                const element = document.getElementById(discId);&lt;br /&gt;
                await this.animationManager.fadeIn(element, document.getElementById(`overall_player_board_${playerId}`));&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For this to work, you will also need to initialize the animation manager. Include the function in your .js file by updating as outlined here: [[BgaAnimations]]. Then add this in the setup function:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            this.animationManager = new BgaAnimations.Manager({&lt;br /&gt;
                animationsActive: () =&amp;gt; this.bga.gameui.bgaAnimationsActive(),&lt;br /&gt;
            });&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Utility Method Explanation=====&lt;br /&gt;
*with &amp;lt;code&amp;gt;element.insertAdjacentHTML&amp;lt;/code&amp;gt; method, we create a HTML piece of code and insert it as a new child of the square div element.&lt;br /&gt;
*Immediately after, using &amp;lt;code&amp;gt;this.animationManager.fadeIn&amp;lt;/code&amp;gt; BGA method, we make the disc slide in (with a fade in animation) from the player panel.&lt;br /&gt;
&lt;br /&gt;
Also note the trailing comma - this is needed because there may be other functions defined after this one, making &amp;lt;code&amp;gt;addDiscOnBoard&amp;lt;/code&amp;gt; just one element in an array of functions.&lt;br /&gt;
&lt;br /&gt;
====Set Token Colors in setupNewGame in modules/php/Game.php file====&lt;br /&gt;
Before we can show a token, we need to set the player colors in the &amp;lt;code&amp;gt;setupNewGame&amp;lt;/code&amp;gt; function in  &amp;lt;code&amp;gt;modules/php/Game.php&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
Replace &amp;lt;code&amp;gt;$default_colors = $gameinfos[&#039;player_colors&#039;];&amp;lt;/code&amp;gt; with the following line:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$default_colors = [&amp;quot;ffffff&amp;quot;, &amp;quot;000000&amp;quot;];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: A few lines below, you may have to remove the line &amp;lt;code&amp;gt;this-&amp;gt;reattributeColorsBasedOnPreferences( $players, $gameinfos[&#039;player_colors&#039;] );&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Test the Token===&lt;br /&gt;
Now, to test if everything works fine we will call the &amp;lt;code&amp;gt;addTokenOnBoard()&amp;lt;/code&amp;gt;  in .js file to Test&lt;br /&gt;
&lt;br /&gt;
In &amp;lt;code&amp;gt;modules/js/Game.js&amp;lt;/code&amp;gt;, in the &amp;lt;code&amp;gt;setup&amp;lt;/code&amp;gt; function, under the code we added to generate the squares. &lt;br /&gt;
 this.addDiscOnBoard(2, 2, this.bga.players.getCurrentPlayerId(), false);&lt;br /&gt;
Now restart the game.&lt;br /&gt;
&lt;br /&gt;
A token should appear and slide immediately to its position, like this:&lt;br /&gt;
&lt;br /&gt;
[[File:reversi3.jpg]]&lt;br /&gt;
&lt;br /&gt;
==The database==&lt;br /&gt;
&lt;br /&gt;
We did most of the client-side programming, so let&#039;s have a look on the other side now. To design the database model of our game, you will need to access the database. You won&#039;t need to do anything in database UI, yet.&lt;br /&gt;
&lt;br /&gt;
===Accessing the Database===&lt;br /&gt;
To access the database, start a game, then click &amp;quot;Go to game database&amp;quot; link at the bottom of our game, to access the database directly with a PhpMyAdmin instance.&lt;br /&gt;
&lt;br /&gt;
After the first time you&#039;ve access the database, you could skip opening a game and instead, go to https://studio.boardgamearena.com/db/ . Your PhpMyAdmin username/password is in your welcome email.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: do not remove existing tables&lt;br /&gt;
&lt;br /&gt;
===Create Table in .sql file===&lt;br /&gt;
Now, you are able to create the table(s) you need for your game, and report every SQL command used in your &amp;lt;code&amp;gt;dbmodel.sql&amp;lt;/code&amp;gt; file. &lt;br /&gt;
&lt;br /&gt;
The database model of Reversi is short: just one table with the squares of the board. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE TABLE IF NOT EXISTS `board` (&lt;br /&gt;
  `board_x` smallint unsigned NOT NULL,&lt;br /&gt;
  `board_y` smallint unsigned NOT NULL,&lt;br /&gt;
  `board_player` int unsigned DEFAULT NULL,&lt;br /&gt;
  PRIMARY KEY (`board_x`,`board_y`)&lt;br /&gt;
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Add the above SQL to &amp;lt;code&amp;gt;dbmodel.sql&amp;lt;/code&amp;gt;. Pay special attention to the backtick &amp;lt;code&amp;gt;`&amp;lt;/code&amp;gt; character vs. the single quote &amp;lt;code&amp;gt;&#039;&amp;lt;/code&amp;gt; when working with SQL.&lt;br /&gt;
&lt;br /&gt;
===Test the Table===&lt;br /&gt;
Now, a new database with a &amp;lt;code&amp;gt;board&amp;lt;/code&amp;gt; table will be created each time we start a Reversi game. This is why after modifying our &amp;lt;code&amp;gt;dbmodel.sql&amp;lt;/code&amp;gt; it&#039;s a good time to stop your current game &amp;amp; start a new game.&lt;br /&gt;
&lt;br /&gt;
Start a new game and verify a table is created : Click the &amp;quot;Go to game database&amp;quot;, use the login and password your received by email, then you should see &amp;lt;code&amp;gt;board&amp;lt;/code&amp;gt; in the list of tables (the db of the current table is automatically selected when clicking on this button).&lt;br /&gt;
&lt;br /&gt;
== Setup the initial game position ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: From now on, you must launch the game with &#039;&#039;&#039;two players&#039;&#039;&#039; to get two &amp;lt;code&amp;gt;player_id&amp;lt;/code&amp;gt;s within the database. Otherwise, the game will crash.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;setupNewGame&amp;lt;/code&amp;gt; method of our &amp;lt;code&amp;gt;Game.php&amp;lt;/code&amp;gt; is called during initial setup. This initializes our data and places the starting tokens on the board. At the beginning of the game, there should be 4 tokens on the board.&lt;br /&gt;
&lt;br /&gt;
===Initialize the Board in modules/php/Game.php file===&lt;br /&gt;
Under &amp;lt;code&amp;gt;// TODO: setup the initial game situation here&amp;lt;/code&amp;gt;, initialize the board&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
list( $blackplayer_id, $whiteplayer_id ) = array_keys( $players );&lt;br /&gt;
// Init the board&lt;br /&gt;
$this-&amp;gt;boardManager-&amp;gt;initializeBoard(8, (int) $blackplayer_id, (int) $whiteplayer_id);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wait, is that all?&lt;br /&gt;
&lt;br /&gt;
No, the boardManager doesn&#039;t exist, we&#039;ll create it so it will handle all the functions related the tokens on the board. Splitting the code in multiple files will help us have easier to read classes, even more if each class handle a specific responsability.&lt;br /&gt;
&lt;br /&gt;
Create a BoardManager.php file next to Game.php, and put this code in it:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
&lt;br /&gt;
namespace Bga\Games\Reversi;&lt;br /&gt;
&lt;br /&gt;
class BoardManager&lt;br /&gt;
{&lt;br /&gt;
    public function __construct(private Game $game)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function initializeBoard(int $boardSize, int $blackPlayerId, int $whitePlayerId): void&lt;br /&gt;
    {&lt;br /&gt;
        $sqlValues = [];&lt;br /&gt;
        $boardSizeHalf = (int) floor($boardSize / 2);&lt;br /&gt;
&lt;br /&gt;
        for ($x = 1; $x &amp;lt;= $boardSize; $x++) {&lt;br /&gt;
            for ($y = 1; $y &amp;lt;= $boardSize; $y++) {&lt;br /&gt;
                $discValue = &#039;NULL&#039;;&lt;br /&gt;
                if (($x === $boardSizeHalf &amp;amp;&amp;amp; $y === $boardSizeHalf) || ($x === ($boardSizeHalf + 1) &amp;amp;&amp;amp; $y === ($boardSizeHalf + 1))) {&lt;br /&gt;
                    $discValue = (string) $whitePlayerId;&lt;br /&gt;
                } elseif (($x === $boardSizeHalf &amp;amp;&amp;amp; $y === ($boardSizeHalf + 1)) || ($x === ($boardSizeHalf + 1) &amp;amp;&amp;amp; $y === $boardSizeHalf)) {&lt;br /&gt;
                    $discValue = (string) $blackPlayerId;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                $sqlValues[] = &amp;quot;($x,$y,$discValue)&amp;quot;;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $sql = &amp;quot;INSERT INTO `board` (`board_x`,`board_y`,`board_player`) VALUES &amp;quot; . implode(&#039;,&#039;, $sqlValues);&lt;br /&gt;
        $this-&amp;gt;game-&amp;gt;DbQuery($sql);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Once the php file is created edit the namespace and change \Reversi to be the name of your project&lt;br /&gt;
&lt;br /&gt;
Note: the file name match the class name, and the namespace will now match the one from Game.php (so we are following PSR-4 specification), allowing BGA to autoload the class when we will need it. Let&#039;s initialize it from the Game.php file!&lt;br /&gt;
&lt;br /&gt;
Before the Game __construct, add&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  public BoardManager $boardManager;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
At the end of the Game __construct, add&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $this-&amp;gt;boardManager = new BoardManager($this);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Passing the game object to the constructor allows us to use its db functions.&lt;br /&gt;
&lt;br /&gt;
If we were managing cards, we would have probably used the [[Deck]] component, and ideally we would create a CardManager that holds the Deck and all game specific logic about managing the cards.&lt;br /&gt;
&lt;br /&gt;
==== Board Initialization Explanation ====&lt;br /&gt;
&lt;br /&gt;
*We create one table entry for each square, with a &amp;lt;code&amp;gt;NULL&amp;lt;/code&amp;gt; value which means &amp;quot;empty square&amp;quot;&lt;br /&gt;
* On 4 of the squares, we place an initial token.&lt;br /&gt;
&lt;br /&gt;
After this, we set &amp;lt;code&amp;gt;activeNextPlayer&amp;lt;/code&amp;gt; to make the first player active at the beginning of the game (this line is already present in the default code template).&lt;br /&gt;
&lt;br /&gt;
If you didn&#039;t do it earlier, you need to remove the call to &amp;lt;code&amp;gt;this-&amp;gt;reattributeColorsBasedOnPreferences()&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;SetupNewGame()&amp;lt;/code&amp;gt;. If you don&#039;t, player color preferences will try (and fail) to override the two colors supported here.&lt;br /&gt;
&lt;br /&gt;
===Show the Initial Token Setup ===&lt;br /&gt;
Now, we need to make these tokens appear on the client side. The first step is to return the token positions with our &amp;lt;code&amp;gt;getAllDatas()&amp;lt;/code&amp;gt; PHP method. &amp;lt;code&amp;gt;getAllDatas()&amp;lt;/code&amp;gt; is called during each page reload.&lt;br /&gt;
&lt;br /&gt;
In the &amp;lt;code&amp;gt;getAllDatas()&amp;lt;/code&amp;gt; method, after &amp;lt;code&amp;gt;// TODO: Gather all information about current game situation (visible by player $current_player_id)&amp;lt;/code&amp;gt;, add the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// Get reversi board token&lt;br /&gt;
$result[&#039;board&#039;] = $this-&amp;gt;boardManager-&amp;gt;getOccupiedDiscs();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
And in the board manager, add:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function getOccupiedDiscs(): array&lt;br /&gt;
    {&lt;br /&gt;
        return $this-&amp;gt;game-&amp;gt;getObjectListFromDB(&lt;br /&gt;
            &amp;quot;SELECT `board_x` `x`, `board_y` `y`, `board_player` `player`&lt;br /&gt;
             FROM `board`&lt;br /&gt;
             WHERE `board_player` IS NOT NULL&amp;quot;&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Next, you will modify the query that gets player information to also get the player&#039;s colors. Above the lines you just inserted in &amp;lt;code&amp;gt;getAllDatas()&amp;lt;/code&amp;gt; notice how we&#039;ve added &amp;lt;code&amp;gt;player_color color&amp;lt;/code&amp;gt; to the sql query in the code below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result[&amp;quot;players&amp;quot;] = $this-&amp;gt;getCollectionFromDb(&lt;br /&gt;
    &amp;quot;SELECT `player_id` `id`, `player_score` `score`, `player_color` `color` FROM `player`&amp;quot;&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We are using the BGA framework&#039;s &amp;lt;code&amp;gt;getObjectListFromDB()&amp;lt;/code&amp;gt; that formats the result of this SQL query in a PHP array with x, y and player attributes. We add it to the result associative array with the key &amp;lt;code&amp;gt;board&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Last, we process this array client side. Let&#039;s place a token on the board for each array item. We&#039;ll do this in our Javascript &amp;lt;code&amp;gt;setup&amp;lt;/code&amp;gt; method under the code we added to generate the squares. &lt;br /&gt;
&lt;br /&gt;
This will result in a removal or edit of the previously added line &amp;lt;code&amp;gt;this.addDiscOnBoard(2, 2, this.bga.players.getCurrentPlayerId());&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for( var i in gamedatas.board ) {&lt;br /&gt;
    const square = gamedatas.board[i];&lt;br /&gt;
    &lt;br /&gt;
    if( square.player !== null ) {&lt;br /&gt;
        this.addDiscOnBoard( square.x, square.y, square.player );&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;board&amp;lt;/code&amp;gt; entry created in &amp;lt;code&amp;gt;getAllDatas()&amp;lt;/code&amp;gt; is used here as &amp;lt;code&amp;gt;gamedatas.board&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Test the Game Start===&lt;br /&gt;
Reload... and here we are:&lt;br /&gt;
&lt;br /&gt;
[[File:reversi5.jpg]]&lt;br /&gt;
&lt;br /&gt;
It starts to feel like Reversi here...&lt;br /&gt;
&lt;br /&gt;
==State Machine==&lt;br /&gt;
&lt;br /&gt;
Stop your game, again. You&#039;re about to start the core game logic.&lt;br /&gt;
&lt;br /&gt;
You already read [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine], so you know that this is the heart of your game logic. For reversi, it&#039;s relatively simple. Here&#039;s a diagram of our game state machine:&lt;br /&gt;
&lt;br /&gt;
[[File:reversi6.jpg]]&lt;br /&gt;
&lt;br /&gt;
===Build your States===&lt;br /&gt;
And here are the classes we need to create on the &amp;lt;code&amp;gt;module/php/States&amp;lt;/code&amp;gt; folder, according to this diagram:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PlayerTurn.php:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
&lt;br /&gt;
namespace Bga\Games\Reversi\States;&lt;br /&gt;
&lt;br /&gt;
use Bga\GameFramework\StateType;&lt;br /&gt;
use Bga\Games\Reversi\Game;&lt;br /&gt;
&lt;br /&gt;
class PlayerTurn extends \Bga\GameFramework\States\GameState&lt;br /&gt;
{&lt;br /&gt;
    public function __construct(protected Game $game) {&lt;br /&gt;
        parent::__construct($game, &lt;br /&gt;
            id: 10, &lt;br /&gt;
            type: StateType::ACTIVE_PLAYER,&lt;br /&gt;
&lt;br /&gt;
            description: clienttranslate(&#039;${actplayer} must play a disc&#039;),&lt;br /&gt;
            descriptionMyTurn: clienttranslate(&#039;${you} must play a disc&#039;),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function zombie(int $playerId) {&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(The &#039;&#039;declare(strict_types=1)&#039;&#039; is optional but recommended for new games). The &#039;&#039;&amp;lt;?php&#039;&#039; at the beginning is necessary for your IDE to understand the php syntax. ) &lt;br /&gt;
&lt;br /&gt;
In PlayerTurn.js remove the code in onEnteringState (which sets the title top you/activealayer must play a counter &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;NextPlayer.php:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
&lt;br /&gt;
namespace Bga\Games\Reversi\States;&lt;br /&gt;
&lt;br /&gt;
use Bga\GameFramework\StateType;&lt;br /&gt;
use Bga\Games\Reversi\Game;&lt;br /&gt;
&lt;br /&gt;
class NextPlayer extends \Bga\GameFramework\States\GameState&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
    public function __construct(protected Game $game) {&lt;br /&gt;
        parent::__construct($game, &lt;br /&gt;
            id: 90, &lt;br /&gt;
            type: StateType::GAME,&lt;br /&gt;
&lt;br /&gt;
            updateGameProgression: true,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note: state ids 1 and 99 are reserved by the framework for gameSetup and gameEnd. We don&#039;t need to describe those states as the framework will take care of that for us.&lt;br /&gt;
&lt;br /&gt;
In setupNewGame in Game.php, make sure the return is &amp;lt;code&amp;gt;return PlayerTurn::class;&amp;lt;/code&amp;gt; so the framework knows which game state is the initial one.&lt;br /&gt;
&lt;br /&gt;
===Test Your States===&lt;br /&gt;
... and start a new Reversi game.&lt;br /&gt;
&lt;br /&gt;
As you can see on the screen capture below, the BGA framework makes the game jump to our first game state &amp;lt;code&amp;gt;PlayerTurn&amp;lt;/code&amp;gt; right after the initial setup. That&#039;s why the status bar contains the description of &amp;lt;code&amp;gt;PlayDisc&amp;lt;/code&amp;gt; state (&amp;quot;XXXX must play a disc&amp;quot;):&lt;br /&gt;
&lt;br /&gt;
[[File:reversi7.jpg]]&lt;br /&gt;
&lt;br /&gt;
==The rules==&lt;br /&gt;
&lt;br /&gt;
We will use the &amp;lt;code&amp;gt;getPossibleMoves&amp;lt;/code&amp;gt; PHP method to: &lt;br /&gt;
*Indicate to the current player where she is allowed to play by returning a list of coordinates&lt;br /&gt;
*Check if the player has the right to play in the spot they choose&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
Example of &amp;lt;code&amp;gt;getPossibleMoves&amp;lt;/code&amp;gt; here https://gist.github.com/leocaseiro/a8bc2851bd0caddd06685b5035937d15&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is pure PHP programming here, and there are no special things from the BGA framework that can be used. This is why we won&#039;t go into details here. The overall idea is: &lt;br /&gt;
* Create a &amp;lt;code&amp;gt;getTurnedOverDiscs(x,y)&amp;lt;/code&amp;gt; method that returns coordinates of discs that would be turned over if a token would be played at &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;,&amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Loop through all free squares of the board and call the &amp;lt;code&amp;gt;getTurnedOverDiscs&amp;lt;/code&amp;gt; method on each of them. If at least 1 token is turned over, this is a valid move.&lt;br /&gt;
&lt;br /&gt;
IMPORTANT: Making a database query is slow! Please don&#039;t load the entire game board with a SQL query multiple times. In our implementation, we load the entire board once at the beginning of &amp;lt;code&amp;gt;getPossibleMoves&amp;lt;/code&amp;gt;, and then pass the board as an argument to all methods.&lt;br /&gt;
&lt;br /&gt;
If you want to look into details, please look at the &amp;quot;utility method&amp;quot; sections of &amp;lt;code&amp;gt;Game.php&amp;lt;/code&amp;gt;. If building the tutorial yourself, copy the functions under &amp;quot;Utility functions&amp;quot; comment from the Reversi tutorial.&lt;br /&gt;
&lt;br /&gt;
==Display allowed moves==&lt;br /&gt;
&lt;br /&gt;
Now we want to highlight the squares where the player can place a disc.&lt;br /&gt;
&lt;br /&gt;
To do this, we add a &amp;lt;code&amp;gt;getArgs&amp;lt;/code&amp;gt; method in &amp;lt;code&amp;gt;modules/php/States/PlayDisc.php&amp;lt;/code&amp;gt;. This method is called on the server each time we enter into &amp;lt;code&amp;gt;PlayDisc&amp;lt;/code&amp;gt; game state, and its result is transferred automatically to the client-side:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function getArgs(int $activePlayerId): array&lt;br /&gt;
{&lt;br /&gt;
    return [&lt;br /&gt;
        &#039;possibleMoves&#039; =&amp;gt; $this-&amp;gt;game-&amp;gt;boardManager-&amp;gt;getPossibleMoves($activePlayerId)&lt;br /&gt;
    ];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we need to handle it on the JS side.&lt;br /&gt;
Replace the state classes definition in the Game constructor by this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // Declare the State classes&lt;br /&gt;
        this.playDisc = new PlayDisc(this, bga);&lt;br /&gt;
        this.bga.states.register(&#039;PlayDisc&#039;, this.playDisc);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
So it matches the state we described in PHP. The state name is the PHP class name, that we use in register to link them.&lt;br /&gt;
&lt;br /&gt;
Replace the class PlayerTurn in &amp;lt;code&amp;gt;modules/js/Game.js&amp;lt;/code&amp;gt; by this one:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class PlayDisc {&lt;br /&gt;
    constructor(game, bga) {&lt;br /&gt;
        this.game = game;&lt;br /&gt;
        this.bga = bga;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    onEnteringState(args, isCurrentPlayerActive) {&lt;br /&gt;
        this.bga.statusBar.setTitle(isCurrentPlayerActive ? &lt;br /&gt;
            _(&#039;${you} must play a disc&#039;) :&lt;br /&gt;
            _(&#039;${actplayer} must play a disc&#039;)&lt;br /&gt;
        );&lt;br /&gt;
      &lt;br /&gt;
        if (isCurrentPlayerActive) {            &lt;br /&gt;
            this.updatePossibleMoves(args.possibleMoves);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Note:&amp;lt;/b&amp;gt; We set the title when entering the state, so you can now remove description/descriptionMyTurn in the PHP class that was just here for test purpose.&lt;br /&gt;
&lt;br /&gt;
We use the &amp;lt;code&amp;gt;getPossibleMoves&amp;lt;/code&amp;gt; method we just developed.&lt;br /&gt;
&lt;br /&gt;
Each time we enter into a new game state, we use the &amp;lt;code&amp;gt;onEnteringState&amp;lt;/code&amp;gt; Javascript method of the JS State class. This lets us use the data returned by the method above on the client side.&lt;br /&gt;
&lt;br /&gt;
So, when we enter into &amp;lt;code&amp;gt;PlayDisc&amp;lt;/code&amp;gt; game state, we call our &amp;lt;code&amp;gt;updatePossibleMoves&amp;lt;/code&amp;gt; method (under the &amp;quot;Utility methods&amp;quot; section). This method is added to PlayDisc class and looks like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
updatePossibleMoves(possibleMoves) {&lt;br /&gt;
    // Remove current possible moves&lt;br /&gt;
    document.querySelectorAll(&#039;.possibleMove&#039;).forEach(div =&amp;gt; div.classList.remove(&#039;possibleMove&#039;));&lt;br /&gt;
&lt;br /&gt;
    for( let x in possibleMoves ) {&lt;br /&gt;
        for( let y in possibleMoves[ x ] ) {&lt;br /&gt;
            // x,y is a possible move&lt;br /&gt;
            document.getElementById(`square_${x}_${y}`).classList.add(&#039;possibleMove&#039;);&lt;br /&gt;
        }            &lt;br /&gt;
    }&lt;br /&gt;
                &lt;br /&gt;
    this.bga.gameui.addTooltipToClass( &#039;possibleMove&#039;, &#039;&#039;, _(&#039;Place a disc here&#039;) );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Here&#039;s what this does. At first, it removes all &amp;lt;code&amp;gt;possibleMove&amp;lt;/code&amp;gt; classes currently applied with the very useful &amp;lt;code&amp;gt;document.querySelectorAll&amp;lt;/code&amp;gt; method.&lt;br /&gt;
&lt;br /&gt;
Then it loops through all possible moves our PHP &amp;lt;code&amp;gt;updatePossibleMoves&amp;lt;/code&amp;gt; function created for us, and adds the &amp;lt;code&amp;gt;possibleMove&amp;lt;/code&amp;gt; class to each corresponding square.&lt;br /&gt;
&lt;br /&gt;
Finally, it uses the BGA framework &amp;lt;code&amp;gt;addTooltipToClass&amp;lt;/code&amp;gt; method to associate a tooltip to all those highlighted squares so that players can understand their meaning.&lt;br /&gt;
&lt;br /&gt;
To see the possible moves we need to create a CSS class (&amp;lt;code&amp;gt;possibleMove&amp;lt;/code&amp;gt;) that can be applied to a &amp;lt;code&amp;gt;square&amp;lt;/code&amp;gt; element to highlight it:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.possibleMove {&lt;br /&gt;
    background-color: white;&lt;br /&gt;
    opacity: 0.2; &lt;br /&gt;
    cursor: pointer;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
And here we are:&lt;br /&gt;
&lt;br /&gt;
[[File:reversi8.jpg.jpg]]&lt;br /&gt;
&lt;br /&gt;
==Let&#039;s play ==&lt;br /&gt;
&lt;br /&gt;
From now, it&#039;s better to restart a game with 2 players, because we are going to implement a complete Reversi turn. The summary of what we are going to do is:&lt;br /&gt;
* When we click on a square, check if it is a &amp;lt;code&amp;gt;possibleMove&amp;lt;/code&amp;gt; and if so, send the move to the server.&lt;br /&gt;
*Server side, check the move is correct, apply Reversi rules and jump to next player.&lt;br /&gt;
*Client side, change the token position to reflect the move.&lt;br /&gt;
&lt;br /&gt;
First we associate each click on a square to one of our methods using our Javascript &amp;lt;code&amp;gt;setup&amp;lt;/code&amp;gt; method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
document.querySelectorAll(&#039;.square&#039;).forEach(square =&amp;gt; square.addEventListener(&#039;click&#039;, e =&amp;gt; this.onPlayDisc(e)));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, our &amp;quot;onPlayDisc&amp;quot; method is called each time someone clicks on a square.&lt;br /&gt;
&lt;br /&gt;
Here&#039;s our &amp;quot;onPlayDisc&amp;quot; method below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
onPlayDisc(evt) {&lt;br /&gt;
    // Stop this event propagation&lt;br /&gt;
    evt.preventDefault();&lt;br /&gt;
    evt.stopPropagation();&lt;br /&gt;
&lt;br /&gt;
    // Get the cliqued square x and y&lt;br /&gt;
    // Note: square id format is &amp;quot;square_X_Y&amp;quot;&lt;br /&gt;
    var coords = evt.currentTarget.id.split(&#039;_&#039;);&lt;br /&gt;
    var x = coords[1];&lt;br /&gt;
    var y = coords[2];&lt;br /&gt;
&lt;br /&gt;
    if(!document.getElementById(`square_${x}_${y}`).classList.contains(&#039;possibleMove&#039;)) {&lt;br /&gt;
        // This is not a possible move =&amp;gt; the click does nothing&lt;br /&gt;
        return ;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    this.bga.actions.performAction(&amp;quot;actPlayDisc&amp;quot;, {&lt;br /&gt;
        x:x,&lt;br /&gt;
        y:y&lt;br /&gt;
    });&lt;br /&gt;
},&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
What we do here is:&lt;br /&gt;
*We stop the propagation of the Javascript &amp;lt;code&amp;gt;onclick&amp;lt;/code&amp;gt; event. Otherwise, it can lead to random behavior so it&#039;s always a good idea.&lt;br /&gt;
*We get the x/y coordinates of the square by using &amp;lt;code&amp;gt;evt.currentTarget.id&amp;lt;/code&amp;gt;&lt;br /&gt;
*We check that clicked square has the &amp;lt;code&amp;gt;possibleMove&amp;lt;/code&amp;gt; class, otherwise we know for sure that we can&#039;t play there.&lt;br /&gt;
*Finally, we make a call to the server using BGA &amp;lt;code&amp;gt;bgaPerformAction&amp;lt;/code&amp;gt; method with argument x and y. This call will check that &amp;lt;code&amp;gt;actPlayDisc&amp;lt;/code&amp;gt; action is possible, according to current game state (we will add the &amp;lt;code&amp;gt;PossibleAction&amp;lt;/code&amp;gt; attribute on &amp;lt;code&amp;gt;actPlayDisc&amp;lt;/code&amp;gt; just after). This check is important to avoid issues if a player double clicks on a square.&lt;br /&gt;
&lt;br /&gt;
Now, we have to manage this &amp;lt;code&amp;gt;actPlayDisc&amp;lt;/code&amp;gt; action on the server side. Add a corresponding &amp;lt;code&amp;gt;actPlayDisc&amp;lt;/code&amp;gt; method in our &amp;lt;code&amp;gt;PlayDisc&amp;lt;/code&amp;gt; state class (&amp;lt;code&amp;gt;PlayDisc.php&amp;lt;/code&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#[PossibleAction]&lt;br /&gt;
function actPlayDisc( int $x, int $y, int $activePlayerId )&lt;br /&gt;
{&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
And add &amp;lt;code&amp;gt;use Bga\GameFramework\States\PossibleAction;&amp;lt;/code&amp;gt; at the top of the file.&lt;br /&gt;
&lt;br /&gt;
(The function will be called when the front-side action is triggered using the Autowire mechanism, if you want to see how it works in details check [[Main game logic: Game.php|https://en.doc.boardgamearena.com/Main_game_logic:_Game.php#Actions_%28autowired%29]] )&lt;br /&gt;
&lt;br /&gt;
now, we are using the &amp;lt;code&amp;gt;getTurnedOverDiscs&amp;lt;/code&amp;gt; method again to check that this move is possible:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // Now, check if this is a possible move&lt;br /&gt;
        $board = $this-&amp;gt;game-&amp;gt;boardManager-&amp;gt;getBoard();&lt;br /&gt;
        $turnedOverDiscs = $this-&amp;gt;game-&amp;gt;getTurnedOverDiscs( $x, $y, $activePlayerId, $board );&lt;br /&gt;
        &lt;br /&gt;
        if( count( $turnedOverDiscs ) === 0 ) {&lt;br /&gt;
            throw new SystemException(&amp;quot;Impossible move&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
we update the database to change the color of all turned over disc + the disc we just placed:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            // Let&#039;s place a disc at x,y and return all &amp;quot;$returned&amp;quot; discs to the active player&lt;br /&gt;
            &lt;br /&gt;
            $this-&amp;gt;game-&amp;gt;boardManager-&amp;gt;applyMove($activePlayerId, $x, $y, $turnedOverDiscs);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We add this part to the BoardManager before going back to our act function:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function applyMove(int $playerId, int $x, int $y, array $turnedOverDiscs): void&lt;br /&gt;
    {&lt;br /&gt;
        $positions = [];&lt;br /&gt;
        foreach ($turnedOverDiscs as $turnedOver) {&lt;br /&gt;
            $turnedX = (int) $turnedOver[&#039;x&#039;];&lt;br /&gt;
            $turnedY = (int) $turnedOver[&#039;y&#039;];&lt;br /&gt;
            $positions[] = &amp;quot;($turnedX,$turnedY)&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        $positions[] = &amp;quot;($x,$y)&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        $sql = &amp;quot;UPDATE `board` SET `board_player` = $playerId WHERE (`board_x`, `board_y`) IN (&amp;quot; . implode(&#039;,&#039;, $positions) . &amp;quot;)&amp;quot;;&lt;br /&gt;
        $this-&amp;gt;game-&amp;gt;DbQuery($sql);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
we manage game statistics:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $this-&amp;gt;bga-&amp;gt;playerStats-&amp;gt;inc(&#039;turnedOver&#039;, $disc_count, $activePlayerId);&lt;br /&gt;
        $updatedStat = &#039;discPlayedOnCenter&#039;;&lt;br /&gt;
        if( ($x==1 &amp;amp;&amp;amp; $y==1) || ($x==$board_size &amp;amp;&amp;amp; $y==1) || ($x==1 &amp;amp;&amp;amp; $y==$board_size) || ($x==$board_size &amp;amp;&amp;amp; $y==$board_size) ) {&lt;br /&gt;
            $updatedStat = &#039;discPlayedOnCorner&#039;;&lt;br /&gt;
        } else if( $x==1 || $x==$board_size || $y==1 || $y==$board_size ) {&lt;br /&gt;
            $updatedStat = &#039;discPlayedOnBorder&#039;;&lt;br /&gt;
        }&lt;br /&gt;
        $this-&amp;gt;bga-&amp;gt;playerStats-&amp;gt;inc($updatedStat, 1, $activePlayerId);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notify the turned discs:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            // Notify&lt;br /&gt;
            $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all( &amp;quot;playDisc&amp;quot;, clienttranslate( &#039;${player_name} plays a disc and turns over ${returned_nbr} disc(s)&#039; ), array(&lt;br /&gt;
                &#039;player_id&#039; =&amp;gt; $activePlayerId,&lt;br /&gt;
                &#039;player_name&#039; =&amp;gt; $this-&amp;gt;game-&amp;gt;getPlayerNameById($activePlayerId),&lt;br /&gt;
                &#039;returned_nbr&#039; =&amp;gt; count( $turnedOverDiscs ),&lt;br /&gt;
                &#039;x&#039; =&amp;gt; $x,&lt;br /&gt;
                &#039;y&#039; =&amp;gt; $y&lt;br /&gt;
            ) );&lt;br /&gt;
&lt;br /&gt;
            $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all( &amp;quot;turnOverDiscs&amp;quot;, &#039;&#039;, array(&lt;br /&gt;
                &#039;player_id&#039; =&amp;gt; $activePlayerId,&lt;br /&gt;
                &#039;turnedOver&#039; =&amp;gt; $turnedOverDiscs&lt;br /&gt;
            ) );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then we update the scores accordingly&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $playerIds = array_keys($this-&amp;gt;game-&amp;gt;loadPlayersBasicInfos());&lt;br /&gt;
        foreach ($playerIds as $playerId) {&lt;br /&gt;
            $tokens = $this-&amp;gt;game-&amp;gt;boardManager-&amp;gt;countDiscsForPlayer((int) $playerId);&lt;br /&gt;
            $this-&amp;gt;bga-&amp;gt;playerScore-&amp;gt;set($playerId, $tokens); // this will update the JS counter automatically&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In BoardManager, add:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function countDiscsForPlayer(int $playerId): int&lt;br /&gt;
    {&lt;br /&gt;
        return (int) $this-&amp;gt;game-&amp;gt;getUniqueValueFromDB(&amp;quot;SELECT COUNT(*) FROM `board` WHERE `board_player` = $playerId&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We are using for that 2 notifications (&amp;lt;code&amp;gt;playDisc&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;turnOverDiscs&amp;lt;/code&amp;gt;  that we are going to implement on client side later). Note that the description of the &amp;lt;code&amp;gt;playDisc&amp;lt;/code&amp;gt; notification will be logged in the game log.&lt;br /&gt;
&lt;br /&gt;
Finally, we jump to the next game state.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // Then, go to the next state&lt;br /&gt;
        return NextPlayer::class;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To make the statistics work, we have to initialize them in &amp;lt;code&amp;gt;stats.json&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;player&amp;quot;: {&lt;br /&gt;
    &amp;quot;discPlayedOnCorner&amp;quot;: {&lt;br /&gt;
      &amp;quot;id&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;Discs played on a corner&amp;quot;,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;discPlayedOnBorder&amp;quot;: {&lt;br /&gt;
      &amp;quot;id&amp;quot;: 11,&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;Discs played on a border&amp;quot;,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;discPlayedOnCenter&amp;quot;: {&lt;br /&gt;
      &amp;quot;id&amp;quot;: 12,&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;Discs played on board center part&amp;quot;,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;turnedOver&amp;quot;: {&lt;br /&gt;
      &amp;quot;id&amp;quot;: 13,&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;Number of discs turned over&amp;quot;,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A last thing to do on the server side is to activate the next player when we enter the &amp;lt;code&amp;gt;NextPlayer&amp;lt;/code&amp;gt; game state (in the &amp;lt;code&amp;gt;modules/php/States/NextPlayer.php&amp;lt;/code&amp;gt; file):&lt;br /&gt;
&lt;br /&gt;
Add `const ST_END_GAME = 99;` at the top of the file, then add:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function onEnteringState(): void&lt;br /&gt;
    {&lt;br /&gt;
        // Active next player&lt;br /&gt;
        $player_id = intval($this-&amp;gt;game-&amp;gt;activeNextPlayer());&lt;br /&gt;
&lt;br /&gt;
        // Check if both player has at least 1 discs, and if there are free squares to play&lt;br /&gt;
        $player_to_discs = $this-&amp;gt;game-&amp;gt;getCollectionFromDb( &amp;quot;SELECT board_player, COUNT( board_x )&lt;br /&gt;
                                                       FROM board&lt;br /&gt;
                                                       GROUP BY board_player&amp;quot;, true );&lt;br /&gt;
&lt;br /&gt;
        if( ! isset( $player_to_discs[ null ] ) )&lt;br /&gt;
        {&lt;br /&gt;
            // Index 0 has not been set =&amp;gt; there&#039;s no more free place on the board !&lt;br /&gt;
            // =&amp;gt; end of the game&lt;br /&gt;
            return ST_END_GAME;&lt;br /&gt;
        }&lt;br /&gt;
        else if( ! isset( $player_to_discs[ $player_id ] ) )&lt;br /&gt;
        {&lt;br /&gt;
            // Active player has no more disc on the board =&amp;gt; he looses immediately&lt;br /&gt;
            return ST_END_GAME;&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        // Can this player play?&lt;br /&gt;
&lt;br /&gt;
        $possibleMoves = $this-&amp;gt;game-&amp;gt;getPossibleMoves( $player_id );&lt;br /&gt;
        if( count( $possibleMoves ) == 0 )&lt;br /&gt;
        {&lt;br /&gt;
&lt;br /&gt;
            // This player can&#039;t play&lt;br /&gt;
            // Can his opponent play ?&lt;br /&gt;
            $opponent_id = (int)$this-&amp;gt;game-&amp;gt;getUniqueValueFromDb( &amp;quot;SELECT player_id FROM player WHERE player_id!=&#039;$player_id&#039; &amp;quot; );&lt;br /&gt;
            if( count( $this-&amp;gt;game-&amp;gt;getPossibleMoves( $opponent_id ) ) == 0 )&lt;br /&gt;
            {&lt;br /&gt;
                // Nobody can move =&amp;gt; end of the game&lt;br /&gt;
                return ST_END_GAME;&lt;br /&gt;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {            &lt;br /&gt;
                // =&amp;gt; pass his turn&lt;br /&gt;
                return NextPlayer::class;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            // This player can play. Give him some extra time&lt;br /&gt;
            $this-&amp;gt;game-&amp;gt;giveExtraTime( $player_id );&lt;br /&gt;
            return PlayDisc::class;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, when we play a token, the rules are checked and the token appears in the database.&lt;br /&gt;
&lt;br /&gt;
[[File:reversi9.jpg]]&lt;br /&gt;
&lt;br /&gt;
Of course, as we don&#039;t manage notifications on client side, we need to press F5 after each move to see the changes on the board.&lt;br /&gt;
&lt;br /&gt;
==Make the move appear automatically==&lt;br /&gt;
&lt;br /&gt;
Now, what we have to do is process the notifications sent by the server and make the move appear on the interface.&lt;br /&gt;
&lt;br /&gt;
In our &amp;lt;code&amp;gt;setupNotifications&amp;lt;/code&amp;gt; method in &amp;lt;code&amp;gt;modules/js/Game.js&amp;lt;/code&amp;gt;, we tell BGA to listen to notifications we will add later (&amp;lt;code&amp;gt;playDisc&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;turnOverDiscs&amp;lt;/code&amp;gt;) using promise-based notifications:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        setupNotifications() {&lt;br /&gt;
            console.log( &#039;notifications subscriptions setup&#039; );&lt;br /&gt;
&lt;br /&gt;
            // automatically listen to the notifications, based on the `notif_xxx` function on this class.&lt;br /&gt;
            this.bga.notifications.setupPromiseNotifications();&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We will associate each of our 3 notifications with a method prefixed with &amp;lt;code&amp;gt;notif_&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Let&#039;s have a look now on the &amp;lt;code&amp;gt;playDisc&amp;lt;/code&amp;gt; notification handler method:&amp;lt;pre&amp;gt;&lt;br /&gt;
        async notif_playDisc(args) {&lt;br /&gt;
            // Remove current possible moves (makes the board more clear)&lt;br /&gt;
            document.querySelectorAll(&#039;.possibleMove&#039;).forEach(div =&amp;gt; div.classList.remove(&#039;possibleMove&#039;));&lt;br /&gt;
        &lt;br /&gt;
            await this.addDiscOnBoard( args.x, args.y, args.player_id );&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
No surprise here, we re-used some existing stuff to: &lt;br /&gt;
*Remove the highlighted squares.&lt;br /&gt;
* Add a new disc on board, coming from player panel. That&#039;s where the async await part of &amp;lt;code&amp;gt;addDiscOnBoard&amp;lt;/code&amp;gt; becomes useful.&lt;br /&gt;
&lt;br /&gt;
Now, here&#039;s the method that handles the &amp;lt;code&amp;gt;turnOverDiscs&amp;lt;/code&amp;gt; notification:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        async animateTurnOverDisc(disc, targetColor) {&lt;br /&gt;
            const squareDiv = document.getElementById(`square_${disc.x}_${disc.y}`);&lt;br /&gt;
            const discDiv = document.getElementById(`disc_${disc.x}_${disc.y}`);&lt;br /&gt;
            &lt;br /&gt;
            squareDiv.classList.add(&#039;flip-animation&#039;);&lt;br /&gt;
            await this.bga.gameui.wait(500); // for the flip animation to finish&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            discDiv.dataset.color = targetColor;&lt;br /&gt;
&lt;br /&gt;
            const parallelAnimations = [{&lt;br /&gt;
                keyframes: [ // flip the disc&lt;br /&gt;
                    { transform: `rotateY(180deg)` },&lt;br /&gt;
                    { transform: `rotateY(0deg)` },&lt;br /&gt;
                ]&lt;br /&gt;
            }, {&lt;br /&gt;
                keyframes: [ // lift the disc&lt;br /&gt;
                    { transform: `translate(0, -12px) scale(1.2)`, offset: 0.5 },&lt;br /&gt;
                ]&lt;br /&gt;
            }];&lt;br /&gt;
&lt;br /&gt;
            await this.animationManager.slideAndAttach(discDiv, squareDiv, { duration: 1000, parallelAnimations });&lt;br /&gt;
            &lt;br /&gt;
            squareDiv.classList.remove(&#039;flip-animation&#039;);&lt;br /&gt;
            await this.bga.gameui.wait(500); // for the flip animation removal to finish&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        async notif_turnOverDiscs(args) {&lt;br /&gt;
            // Get the color of the player who is returning the discs&lt;br /&gt;
            const targetColor = this.gamedatas.players[ args.player_id ].color;&lt;br /&gt;
&lt;br /&gt;
            // wait for the animations of all turned discs to be over before considering the notif done&lt;br /&gt;
            await Promise.all(&lt;br /&gt;
                args.turnedOver.map(disc =&amp;gt; &lt;br /&gt;
                    this.animateTurnOverDisc(disc, targetColor)&lt;br /&gt;
                )&lt;br /&gt;
            );&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The list of the discs to be turned over has been made available by our server side code in &amp;lt;code&amp;gt;args.turnedOver&amp;lt;/code&amp;gt; (see previous paragraph). We loop through all these discs, and trigger a single disc animation using &amp;lt;code&amp;gt;animateTurnOverDisc&amp;lt;/code&amp;gt;. This function creates a complex animation using [[BgaAnimations]] for the disc in parameter.&lt;br /&gt;
&lt;br /&gt;
We create a chain of 3 animations using await to make the square highlight, then flip the token, then remove the square highlight.&lt;br /&gt;
&lt;br /&gt;
==Implement the zombie mode ==&lt;br /&gt;
When a player leave the table, a Zombie (bot) will play for the leaver, so the table can continue. For that, we write code in the zombieTurn function.&lt;br /&gt;
&lt;br /&gt;
to learn more about the Zombie mode, read [[Zombie Mode]]&lt;br /&gt;
&lt;br /&gt;
Add this at the end of your PlayDisc.php file :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function zombie(int $playerId) {&lt;br /&gt;
        // Zombie level 1&lt;br /&gt;
        $possibleMoves = $this-&amp;gt;game-&amp;gt;boardManager-&amp;gt;getPossibleMoves($playerId);&lt;br /&gt;
        // transform the 2 dimensional array into a flat array of possible [$x, $y]&lt;br /&gt;
        $possibleMovesArray = [];&lt;br /&gt;
        foreach($possibleMoves as $x =&amp;gt; $ys) {&lt;br /&gt;
            foreach($ys as $y =&amp;gt; $valid) {&lt;br /&gt;
                $possibleMovesArray[] = [$x, $y];&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $zombieChoice = $this-&amp;gt;getRandomZombieChoice($possibleMovesArray);&lt;br /&gt;
        return $this-&amp;gt;actPlayDisc($zombieChoice[0], $zombieChoice[1], $playerId);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And that&#039;s it ! Everytime a leaver should be playing, the Zombie will random play one of the possible moves, and the remaining player will be able to play the next move.&lt;br /&gt;
&lt;br /&gt;
== Add a debug function to help up test the code==&lt;br /&gt;
Add this at the end of your Game.php file :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    function debug_playAutomatically(int $moves = 50) {&lt;br /&gt;
        $this-&amp;gt;bga-&amp;gt;debug-&amp;gt;playUntil(fn(int $count) =&amp;gt; $count === $moves);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function debug_playToEndGame() {&lt;br /&gt;
        $this-&amp;gt;debug_playAutomatically(64); // reversi max moves is under 64 for the standard size board&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This function plays automatically until we reach the end of the game. The functions starting with &amp;quot;debug_&amp;quot; can be triggered in the Studio with a special menu.&lt;br /&gt;
It uses the tool &amp;lt;code&amp;gt;$this-&amp;gt;debug-&amp;gt;playUntil&amp;lt;/code&amp;gt; that will trigger zombie actions, but you could put any code in the debug function for the tests you need.&lt;br /&gt;
&lt;br /&gt;
Start a new game, click on the Bug icon on the top left then &amp;quot;playToEndGame&amp;quot;. You should see the game randomly playing until it reaches the end game, so it helps you check the animations, and you can see if the final scoring is also working as expected. This example will allow us to easily test the Zombie code, as it will play as a Zombie for all players until the end of the game. It&#039;s also an easy way to see if end score computation is working as expected!&lt;br /&gt;
&lt;br /&gt;
You can create as many debug function as you like!&lt;br /&gt;
&lt;br /&gt;
==Optional: add unit tests==&lt;br /&gt;
Follow the instruction on https://en.doc.boardgamearena.com/Testing_by_developer#PHP to setup phpunit and create the first Reversi unit tests that are given as examples there.&lt;br /&gt;
&lt;br /&gt;
Running &amp;lt;code&amp;gt;phpunit&amp;lt;/code&amp;gt;, it should be 4/4 OK! And we have a good hint the case of the full board should be handled OK without actually completing a game :)&lt;br /&gt;
&lt;br /&gt;
==After the tutorial==&lt;br /&gt;
You might want to check another tutorial, or start working on your first real project !&lt;br /&gt;
&lt;br /&gt;
[[Create a game in BGA Studio: Complete Walkthrough]]&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>RichardSPeters</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Tutorial_reversi&amp;diff=29139</id>
		<title>Tutorial reversi</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Tutorial_reversi&amp;diff=29139"/>
		<updated>2026-03-23T23:19:51Z</updated>

		<summary type="html">&lt;p&gt;RichardSPeters: Minor tweaks&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Using this tutorial, you can build a complete working game on the BGA environment: Reversi.&lt;br /&gt;
&lt;br /&gt;
Before you read this tutorial, you must:&lt;br /&gt;
* Read the overall presentations of the BGA Framework ([[Studio|see here]]).&lt;br /&gt;
* Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi].&lt;br /&gt;
* Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript&lt;br /&gt;
* &#039;&#039;&#039;Setup your development environment&#039;&#039;&#039; [http://en.doc.boardgamearena.com/First_steps_with_BGA_Studio First Steps with BGA Studio]&lt;br /&gt;
&lt;br /&gt;
== Create your first game ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: you should already have created a project following instructions in [[First_steps_with_BGA_Studio#Create_a_new_game_project|Create a new game project]]. While you will find a &#039;&#039;&#039;reversi&#039;&#039;&#039; directory in your SFTP folder, do not use it for this tutorial. Instead, use the project you have created as an (empty) starting point.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
With the initial skeleton of code provided in your project, you can already start a game from the BGA Studio:&lt;br /&gt;
* Go to your [https://studio.boardgamearena.com/controlpanel studio Control panel], then Manage games and select your initial project. &#039;&#039;Note: there are warnings displayed about a missing BGG_ID and presentation text. You can ignore that for now.&#039;&#039;&lt;br /&gt;
* Click the Play link next to your project name. This will open the Play page and offer to create a new table for your project. &#039;&#039;Optional: click the Heart icon to add your project to your favorite games list.&#039;&#039;&lt;br /&gt;
* On the Play page, on the top of the page, make sure that your settings are &amp;quot;Simple game&amp;quot;, &amp;quot;Real time&amp;quot; and &amp;quot;Manual&amp;quot;.&lt;br /&gt;
* Click &amp;quot;Create table&amp;quot; to create a table of your project. &lt;br /&gt;
* For now, we are going to work with one player only, so use the (-) button to set the number of players to 2. Most of the time it is simpler to proceed with only one player during the early phase of development of your game, as it&#039;s easy and fast to start/stop games. By default the game requires 2 players, so press &amp;quot;Express Start&amp;quot;. It will open another tab with another of your players.&lt;br /&gt;
* Reminder: Always use the &amp;quot;Express Start&amp;quot; button to start the game.&lt;br /&gt;
&lt;br /&gt;
Thus, you can start a &amp;quot;Reversi&amp;quot; game, and arrive on a void, empty game. Yeah.&lt;br /&gt;
&lt;br /&gt;
End the game by clicking on the game options icon on the top right, and then on &amp;quot;Express Stop&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Editing the game information (Optional) ==&lt;br /&gt;
&lt;br /&gt;
This step is optional and will fix the warnings on the project page (missing BGG_ID and presentation).&lt;br /&gt;
&lt;br /&gt;
==== Edits to fix Errors ====&lt;br /&gt;
* Edit your local copy of the &amp;lt;code&amp;gt;gameinfos.inc.php&amp;lt;/code&amp;gt; file:&lt;br /&gt;
** Change the &amp;lt;code&amp;gt;bgg_id&amp;lt;/code&amp;gt; value from &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;2389&amp;lt;/code&amp;gt; - that&#039;s around line 26.&lt;br /&gt;
** Add &amp;lt;code&amp;gt;1,&amp;lt;/code&amp;gt; to the players array (so you can start 1-player games while testing) - that&#039;s around line 29.&lt;br /&gt;
* Upload the &amp;lt;code&amp;gt;gameinfos.inc.php&amp;lt;/code&amp;gt; file to the SFTP server (see [[First_steps_with_BGA_Studio#Connect_to_your_SFTP_folder|Connect to your SFTP folder]]).&lt;br /&gt;
&lt;br /&gt;
==== Test your Edits ====&lt;br /&gt;
* Go back to your project page, and in the the local version, click Game page and check if the information match what you changed in the gameinfos file. [[File:Studio local version block.png|frameless]]&lt;br /&gt;
&lt;br /&gt;
===== Not working? =====&lt;br /&gt;
Some changes will require bypassing the cache (adding/changing images). It is often worth doing a hard refresh (Ctrl+F5) to make sure the latest version is used.&lt;br /&gt;
&lt;br /&gt;
== Make sure the IDE is properly configured ==&lt;br /&gt;
Try to add some invalid values in the framework names like this :&lt;br /&gt;
&lt;br /&gt;
[[File:Voluntary reversi tutorial ide error.png]]&lt;br /&gt;
&lt;br /&gt;
The error should be underlined if your IDE is properly configured. If it is not, check [[Setting up BGA Development environment using VSCode]]&lt;br /&gt;
&lt;br /&gt;
In the same fashion, the autocomplete of the IDE should propose you some values after &amp;lt;code&amp;gt;this-&amp;gt;bga-&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
You can also check if the upload works as expected, the game should now work and display the error (Undefined property: Bga\Games\EmptyGame\Game::$bgaTEST ...).&lt;br /&gt;
&lt;br /&gt;
Revert to the correct syntax.&lt;br /&gt;
&lt;br /&gt;
== Make it look like Reversi ==&lt;br /&gt;
&lt;br /&gt;
Let&#039;s start with the board. This will give you a good idea of how things will look and where tokens should go.&lt;br /&gt;
&lt;br /&gt;
Be careful designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case).&lt;br /&gt;
&lt;br /&gt;
For Reversi, it&#039;s useless to have a 750x750px board - much too big, so we chose this one which fit perfectly (536x528):&lt;br /&gt;
&lt;br /&gt;
[[File:Board.jpg]]&lt;br /&gt;
&lt;br /&gt;
Note that we are using a jpg file. Jpg files are lighter than png, so they are faster to load. Later, we are going to use PNGs for tokens because they allow for transparency.&lt;br /&gt;
&lt;br /&gt;
==== Add the board ====&lt;br /&gt;
use lowercase file names&lt;br /&gt;
* upload &amp;lt;code&amp;gt;board.jpg&amp;lt;/code&amp;gt; in your &amp;lt;code&amp;gt;img/&amp;lt;/code&amp;gt; directory. &lt;br /&gt;
* edit &amp;lt;code&amp;gt;modules/js/Game.js&amp;lt;/code&amp;gt; to add the &amp;lt;code&amp;gt;div&amp;lt;/code&amp;gt; for your board at the beginning of the setup function.&lt;br /&gt;
&lt;br /&gt;
Note: If you are building this game by following the tutorial, you will have a different project name than &amp;lt;code&amp;gt;reversi&amp;lt;/code&amp;gt; (i.e. &amp;lt;code&amp;gt;mygame.css&amp;lt;/code&amp;gt;). The file names in your project will be different than shown in this tutorial, replacing &amp;lt;code&amp;gt;reversi&amp;lt;/code&amp;gt; with your project name. Be sure that any code (other than comments) that references &amp;lt;code&amp;gt;reversi&amp;lt;/code&amp;gt; is changed to your actual project name.&lt;br /&gt;
&lt;br /&gt;
Edit the Game.js file and replace the code after &#039;&#039;&#039;// Example to add a div on the game area&#039;&#039;&#039; with the following:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.bga.gameArea.getElement().insertAdjacentHTML(&#039;beforeend&#039;, `&lt;br /&gt;
  &amp;lt;div id=&amp;quot;board&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
`);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: example above use backticks, aka string template literals. This technique used a lot in code of games as it allows to easily create html with strings substitutions, use quotes, double quotes and newlines. &lt;br /&gt;
If you don&#039;t know what it is check here&lt;br /&gt;
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals&lt;br /&gt;
&lt;br /&gt;
*edit your &amp;lt;code&amp;gt;reversi.css&amp;lt;/code&amp;gt; file to transform it into a visible board:&lt;br /&gt;
&lt;br /&gt;
 #board {&lt;br /&gt;
    width: 536px;&lt;br /&gt;
    height: 528px;&lt;br /&gt;
    background-image: url(&#039;img/board.jpg&#039;);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
and remove the code in Game.js in method onEnteringState that refers to adding action buttons and remove the section under &amp;lt;code&amp;gt;// example of adding a div for each player&amp;quot;&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important: refresh your page.&#039;&#039;&#039; Here&#039;s your board:[[File:reversi1.jpg]]&lt;br /&gt;
&lt;br /&gt;
If the board does not appear, refresh the page (always do this when you update the CSS file), and check the image filename. Remember file names are case sensitive!&lt;br /&gt;
&lt;br /&gt;
==== Code the Grid====&lt;br /&gt;
Now, we need to create some invisible HTML elements where squares are. These elements will be used as position references for the white and black tokens. &lt;br /&gt;
&lt;br /&gt;
=====Build the grid of squares=====&lt;br /&gt;
The board is 8 squares by 8 squares. This means we need 64 squares. To avoid writing 64 individual &amp;lt;code&amp;gt;div&amp;lt;/code&amp;gt; elements on our template, we are going to generate the squares on JS &amp;lt;code&amp;gt;setup&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
We&#039;ll do this in our Javascript &amp;lt;code&amp;gt;setup&amp;lt;/code&amp;gt; method under &amp;lt;code&amp;gt;// TODO: Set up your game interface here, according to &amp;quot;gamedatas&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const board = document.getElementById(&#039;board&#039;);&lt;br /&gt;
const hor_scale = 64.8;&lt;br /&gt;
const ver_scale = 64.4;&lt;br /&gt;
for (let x=1; x&amp;lt;=8; x++) {&lt;br /&gt;
    for (let y=1; y&amp;lt;=8; y++) {&lt;br /&gt;
        const left = Math.round((x - 1) * hor_scale + 10);&lt;br /&gt;
        const top = Math.round((y - 1) * ver_scale + 7);&lt;br /&gt;
        // we use afterbegin to make sure squares are placed before discs&lt;br /&gt;
        board.insertAdjacentHTML(`afterbegin`, `&amp;lt;div id=&amp;quot;square_${x}_${y}&amp;quot; class=&amp;quot;square&amp;quot; style=&amp;quot;left: ${left}px; top: ${top}px;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;`);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: as you can see, squares in our &amp;lt;code&amp;gt;board.jpg&amp;lt;/code&amp;gt; files do not have an exact width/height in pixels, and that&#039;s the reason we are using floating point numbers here.&lt;br /&gt;
&lt;br /&gt;
=====Style Those Squares=====&lt;br /&gt;
Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#board {&lt;br /&gt;
    width: 536px;&lt;br /&gt;
    height: 528px;&lt;br /&gt;
    background-image: url(&#039;img/board.jpg&#039;);&lt;br /&gt;
    position: relative;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.square {&lt;br /&gt;
    width: 62px;&lt;br /&gt;
    height: 62px;&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    background-color: red;&lt;br /&gt;
    display: flex;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Explanations:&lt;br /&gt;
*With &amp;quot;&amp;lt;code&amp;gt;position: relative&amp;lt;/code&amp;gt;&amp;quot; on board, we ensure square elements are positioned relatively to board.&lt;br /&gt;
*&amp;lt;code&amp;gt;background-color: red;&amp;lt;/code&amp;gt; is used for testing. This allows us to see the invisible elements. (You could instead do something like &amp;lt;code&amp;gt;outline: 2px solid orange;&amp;lt;/code&amp;gt; have fun and be creative)&lt;br /&gt;
*The display flex with center will place the disc in the center of the square.&lt;br /&gt;
&lt;br /&gt;
Let&#039;s refresh and check our (beautiful) squares:&lt;br /&gt;
&lt;br /&gt;
[[File:reversi2.jpg]]&lt;br /&gt;
&lt;br /&gt;
Now that you know the squares are there, you can remove the test line &amp;lt;code&amp;gt;background-color: red;&amp;lt;/code&amp;gt;  from your &amp;lt;code&amp;gt;.square&amp;lt;/code&amp;gt; class in the CSS stylesheet.&lt;br /&gt;
&lt;br /&gt;
===== Not Working?=====&lt;br /&gt;
If the styled squares do not appear, inspect and check your css (Chrome DevTools: Application &amp;gt; Frames &amp;gt; top &amp;gt; Stylesheets &amp;gt; reversi.css). &lt;br /&gt;
&lt;br /&gt;
==The Tokens==&lt;br /&gt;
&lt;br /&gt;
Now, our board is ready for some tokens!&lt;br /&gt;
&lt;br /&gt;
[Note: Throughout this tutorial, sometimes &amp;quot;tokens&amp;quot; is used, and sometimes &amp;quot;discs&amp;quot; is used. They are often swapped if you&#039;re looking at code in the reversi example project.]&lt;br /&gt;
&lt;br /&gt;
===Build the Token===&lt;br /&gt;
There are quite a few steps before the tokens will appear. You may be used to testing after every change, but that won&#039;t work well here. The token will &#039;&#039;&#039;not&#039;&#039;&#039; show until you have add styles to the css, utility method in the js,  adjusted the php file, and added the token to the board in the js file.&lt;br /&gt;
&lt;br /&gt;
====Add Token to img directory ====&lt;br /&gt;
Here&#039;s a new piece of art with the tokens. We need transparency here so we are using a png file:&lt;br /&gt;
&lt;br /&gt;
[[File:tokens.png]]&lt;br /&gt;
&lt;br /&gt;
Upload this image file &amp;lt;code&amp;gt;tokens.png&amp;lt;/code&amp;gt; in your &amp;lt;code&amp;gt;img/&amp;lt;/code&amp;gt; directory.  NB: may need to manually upload as have found that the automatic SFTP does&#039;t seem to pick up images.&lt;br /&gt;
&lt;br /&gt;
Important Fun Fact: we are using ONE file for both tokens. It is really important to use a minimum number of graphic files for your game. This is called the &amp;quot;CSS sprite&amp;quot; technique, because it makes the game load faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites].&lt;br /&gt;
&lt;br /&gt;
====Style the Tokens in .css file ====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.disc {&lt;br /&gt;
    position: relative;&lt;br /&gt;
    width: 54px;&lt;br /&gt;
    height: 54px;&lt;br /&gt;
    z-index: 3;&lt;br /&gt;
    transform-style: preserve-3d;&lt;br /&gt;
}&lt;br /&gt;
.disc .disc-faces { &lt;br /&gt;
    transform-style: preserve-3d;&lt;br /&gt;
}&lt;br /&gt;
.disc[data-color=&amp;quot;000000&amp;quot;] .disc-faces { &lt;br /&gt;
    transform: rotateY(180deg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.disc-face {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    width: 54px;&lt;br /&gt;
    height: 54px;&lt;br /&gt;
    background-image: url(&#039;img/tokens.png&#039;);&lt;br /&gt;
    background-size: auto 100%;&lt;br /&gt;
    backface-visibility: hidden;&lt;br /&gt;
}&lt;br /&gt;
.disc-face[data-side=&amp;quot;white&amp;quot;] { &lt;br /&gt;
    background-position-x: 0%; &lt;br /&gt;
    transform: rotateY(0deg);&lt;br /&gt;
}&lt;br /&gt;
.disc-face[data-side=&amp;quot;black&amp;quot;] { &lt;br /&gt;
    background-position-x: 100%; &lt;br /&gt;
    transform: rotateY(180deg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With this CSS code, we can set and change the token color by changing the &amp;lt;code&amp;gt;data-color&amp;lt;/code&amp;gt; attribute. Using data instead of a class ensures it can be only one of them (the disc cannot be black and white at the same time).&lt;br /&gt;
We will create a disc-faces div in the disc containing 2 disc-face divs, to represent each face of the disc. It will allow us to have a nice flip animation!&lt;br /&gt;
&lt;br /&gt;
====Add Token Utility Method in .js file====&lt;br /&gt;
Now, let&#039;s make the first token appear on our board. Tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, using a template string&lt;br /&gt;
&lt;br /&gt;
Let&#039;s create a method in our Javascript code (in the &amp;lt;code&amp;gt;modules/js/Game.js&amp;lt;/code&amp;gt; file) that will make a token appear on the board, using this template. Add under the section &amp;lt;code&amp;gt;//// Utility methods&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        async addDiscOnBoard( x, y, playerId, animate = true )&lt;br /&gt;
        {&lt;br /&gt;
            const color = this.gamedatas.players[ playerId ].color;&lt;br /&gt;
            const discId = `disc_${x}_${y}`;&lt;br /&gt;
&lt;br /&gt;
            document.getElementById(`square_${x}_${y}`).insertAdjacentHTML(&#039;beforeend&#039;, `&lt;br /&gt;
                &amp;lt;div class=&amp;quot;disc&amp;quot; data-color=&amp;quot;${color}&amp;quot; id=&amp;quot;${discId}&amp;quot;&amp;gt;&lt;br /&gt;
                    &amp;lt;div class=&amp;quot;disc-faces&amp;quot;&amp;gt;&lt;br /&gt;
                        &amp;lt;div class=&amp;quot;disc-face&amp;quot; data-side=&amp;quot;white&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
                        &amp;lt;div class=&amp;quot;disc-face&amp;quot; data-side=&amp;quot;black&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
                    &amp;lt;/div&amp;gt;&lt;br /&gt;
                &amp;lt;/div&amp;gt;&lt;br /&gt;
            `);&lt;br /&gt;
&lt;br /&gt;
            if (animate) {&lt;br /&gt;
                const element = document.getElementById(discId);&lt;br /&gt;
                await this.animationManager.fadeIn(element, document.getElementById(`overall_player_board_${playerId}`));&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For this to work, you will also need to initialize the animation manager. Include the function in your .js file by updating as outlined here: [[BgaAnimations]]. Then add this in the setup function:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            this.animationManager = new BgaAnimations.Manager({&lt;br /&gt;
                animationsActive: () =&amp;gt; this.bga.gameui.bgaAnimationsActive(),&lt;br /&gt;
            });&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Utility Method Explanation=====&lt;br /&gt;
*with &amp;lt;code&amp;gt;element.insertAdjacentHTML&amp;lt;/code&amp;gt; method, we create a HTML piece of code and insert it as a new child of the square div element.&lt;br /&gt;
*Immediately after, using &amp;lt;code&amp;gt;this.animationManager.fadeIn&amp;lt;/code&amp;gt; BGA method, we make the disc slide in (with a fade in animation) from the player panel.&lt;br /&gt;
&lt;br /&gt;
Also note the trailing comma - this is needed because there may be other functions defined after this one, making &amp;lt;code&amp;gt;addDiscOnBoard&amp;lt;/code&amp;gt; just one element in an array of functions.&lt;br /&gt;
&lt;br /&gt;
====Set Token Colors in setupNewGame in modules/php/Game.php file====&lt;br /&gt;
Before we can show a token, we need to set the player colors in the &amp;lt;code&amp;gt;setupNewGame&amp;lt;/code&amp;gt; function in  &amp;lt;code&amp;gt;modules/php/Game.php&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
Replace &amp;lt;code&amp;gt;$default_colors = $gameinfos[&#039;player_colors&#039;];&amp;lt;/code&amp;gt; with the following line:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$default_colors = [&amp;quot;ffffff&amp;quot;, &amp;quot;000000&amp;quot;];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: A few lines below, you may have to remove the line &amp;lt;code&amp;gt;this-&amp;gt;reattributeColorsBasedOnPreferences( $players, $gameinfos[&#039;player_colors&#039;] );&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Test the Token===&lt;br /&gt;
Now, to test if everything works fine we will call the &amp;lt;code&amp;gt;addTokenOnBoard()&amp;lt;/code&amp;gt;  in .js file to Test&lt;br /&gt;
&lt;br /&gt;
In &amp;lt;code&amp;gt;modules/js/Game.js&amp;lt;/code&amp;gt;, in the &amp;lt;code&amp;gt;setup&amp;lt;/code&amp;gt; function, under the code we added to generate the squares. &lt;br /&gt;
 this.addDiscOnBoard(2, 2, this.bga.players.getCurrentPlayerId(), false);&lt;br /&gt;
Now restart the game.&lt;br /&gt;
&lt;br /&gt;
A token should appear and slide immediately to its position, like this:&lt;br /&gt;
&lt;br /&gt;
[[File:reversi3.jpg]]&lt;br /&gt;
&lt;br /&gt;
==The database==&lt;br /&gt;
&lt;br /&gt;
We did most of the client-side programming, so let&#039;s have a look on the other side now. To design the database model of our game, you will need to access the database. You won&#039;t need to do anything in database UI, yet.&lt;br /&gt;
&lt;br /&gt;
===Accessing the Database===&lt;br /&gt;
To access the database, start a game, then click &amp;quot;Go to game database&amp;quot; link at the bottom of our game, to access the database directly with a PhpMyAdmin instance.&lt;br /&gt;
&lt;br /&gt;
After the first time you&#039;ve access the database, you could skip opening a game and instead, go to https://studio.boardgamearena.com/db/ . Your PhpMyAdmin username/password is in your welcome email.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: do not remove existing tables&lt;br /&gt;
&lt;br /&gt;
===Create Table in .sql file===&lt;br /&gt;
Now, you are able to create the table(s) you need for your game, and report every SQL command used in your &amp;lt;code&amp;gt;dbmodel.sql&amp;lt;/code&amp;gt; file. &lt;br /&gt;
&lt;br /&gt;
The database model of Reversi is short: just one table with the squares of the board. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE TABLE IF NOT EXISTS `board` (&lt;br /&gt;
  `board_x` smallint unsigned NOT NULL,&lt;br /&gt;
  `board_y` smallint unsigned NOT NULL,&lt;br /&gt;
  `board_player` int unsigned DEFAULT NULL,&lt;br /&gt;
  PRIMARY KEY (`board_x`,`board_y`)&lt;br /&gt;
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Add the above SQL to &amp;lt;code&amp;gt;dbmodel.sql&amp;lt;/code&amp;gt;. Pay special attention to the backtick &amp;lt;code&amp;gt;`&amp;lt;/code&amp;gt; character vs. the single quote &amp;lt;code&amp;gt;&#039;&amp;lt;/code&amp;gt; when working with SQL.&lt;br /&gt;
&lt;br /&gt;
===Test the Table===&lt;br /&gt;
Now, a new database with a &amp;lt;code&amp;gt;board&amp;lt;/code&amp;gt; table will be created each time we start a Reversi game. This is why after modifying our &amp;lt;code&amp;gt;dbmodel.sql&amp;lt;/code&amp;gt; it&#039;s a good time to stop your current game &amp;amp; start a new game.&lt;br /&gt;
&lt;br /&gt;
Start a new game and verify a table is created : Click the &amp;quot;Go to game database&amp;quot;, use the login and password your received by email, then you should see &amp;lt;code&amp;gt;board&amp;lt;/code&amp;gt; in the list of tables (the db of the current table is automatically selected when clicking on this button).&lt;br /&gt;
&lt;br /&gt;
== Setup the initial game position ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: From now on, you must launch the game with &#039;&#039;&#039;two players&#039;&#039;&#039; to get two &amp;lt;code&amp;gt;player_id&amp;lt;/code&amp;gt;s within the database. Otherwise, the game will crash.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;setupNewGame&amp;lt;/code&amp;gt; method of our &amp;lt;code&amp;gt;Game.php&amp;lt;/code&amp;gt; is called during initial setup. This initializes our data and places the starting tokens on the board. At the beginning of the game, there should be 4 tokens on the board.&lt;br /&gt;
&lt;br /&gt;
===Initialize the Board in modules/php/Game.php file===&lt;br /&gt;
Under &amp;lt;code&amp;gt;// TODO: setup the initial game situation here&amp;lt;/code&amp;gt;, initialize the board&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
list( $blackplayer_id, $whiteplayer_id ) = array_keys( $players );&lt;br /&gt;
// Init the board&lt;br /&gt;
$this-&amp;gt;boardManager-&amp;gt;initializeBoard(8, (int) $blackplayer_id, (int) $whiteplayer_id);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wait, is that all?&lt;br /&gt;
&lt;br /&gt;
No, the boardManager doesn&#039;t exist, we&#039;ll create it so it will handle all the functions related the tokens on the board. Splitting the code in multiple files will help us have easier to read classes, even more if each class handle a specific responsability.&lt;br /&gt;
&lt;br /&gt;
Create a BoardManager.php file next to Game.php, and put this code in it:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
&lt;br /&gt;
namespace Bga\Games\Reversi;&lt;br /&gt;
&lt;br /&gt;
class BoardManager&lt;br /&gt;
{&lt;br /&gt;
    public function __construct(private Game $game)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function initializeBoard(int $boardSize, int $blackPlayerId, int $whitePlayerId): void&lt;br /&gt;
    {&lt;br /&gt;
        $sqlValues = [];&lt;br /&gt;
        $boardSizeHalf = (int) floor($boardSize / 2);&lt;br /&gt;
&lt;br /&gt;
        for ($x = 1; $x &amp;lt;= $boardSize; $x++) {&lt;br /&gt;
            for ($y = 1; $y &amp;lt;= $boardSize; $y++) {&lt;br /&gt;
                $discValue = &#039;NULL&#039;;&lt;br /&gt;
                if (($x === $boardSizeHalf &amp;amp;&amp;amp; $y === $boardSizeHalf) || ($x === ($boardSizeHalf + 1) &amp;amp;&amp;amp; $y === ($boardSizeHalf + 1))) {&lt;br /&gt;
                    $discValue = (string) $whitePlayerId;&lt;br /&gt;
                } elseif (($x === $boardSizeHalf &amp;amp;&amp;amp; $y === ($boardSizeHalf + 1)) || ($x === ($boardSizeHalf + 1) &amp;amp;&amp;amp; $y === $boardSizeHalf)) {&lt;br /&gt;
                    $discValue = (string) $blackPlayerId;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                $sqlValues[] = &amp;quot;($x,$y,$discValue)&amp;quot;;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $sql = &amp;quot;INSERT INTO `board` (`board_x`,`board_y`,`board_player`) VALUES &amp;quot; . implode(&#039;,&#039;, $sqlValues);&lt;br /&gt;
        $this-&amp;gt;game-&amp;gt;DbQuery($sql);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Once the php file is created edit the namespace and change \Reversi to be the name of your project&lt;br /&gt;
&lt;br /&gt;
Note: the file name match the class name, and the namespace will now match the one from Game.php (so we are following PSR-4 specification), allowing BGA to autoload the class when we will need it. Let&#039;s initialize it from the Game.php file!&lt;br /&gt;
&lt;br /&gt;
Before the Game __construct, add&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  public BoardManager $boardManager;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
At the end of the Game __construct, add&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $this-&amp;gt;boardManager = new BoardManager($this);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Passing the game object to the constructor allows us to use its db functions.&lt;br /&gt;
&lt;br /&gt;
If we were managing cards, we would have probably used the [[Deck]] component, and ideally we would create a CardManager that holds the Deck and all game specific logic about managing the cards.&lt;br /&gt;
&lt;br /&gt;
==== Board Initialization Explanation ====&lt;br /&gt;
&lt;br /&gt;
*We create one table entry for each square, with a &amp;lt;code&amp;gt;NULL&amp;lt;/code&amp;gt; value which means &amp;quot;empty square&amp;quot;&lt;br /&gt;
* On 4 of the squares, we place an initial token.&lt;br /&gt;
&lt;br /&gt;
After this, we set &amp;lt;code&amp;gt;activeNextPlayer&amp;lt;/code&amp;gt; to make the first player active at the beginning of the game (this line is already present in the default code template).&lt;br /&gt;
&lt;br /&gt;
If you didn&#039;t do it earlier, you need to remove the call to &amp;lt;code&amp;gt;this-&amp;gt;reattributeColorsBasedOnPreferences()&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;SetupNewGame()&amp;lt;/code&amp;gt;. If you don&#039;t, player color preferences will try (and fail) to override the two colors supported here.&lt;br /&gt;
&lt;br /&gt;
===Show the Initial Token Setup ===&lt;br /&gt;
Now, we need to make these tokens appear on the client side. The first step is to return the token positions with our &amp;lt;code&amp;gt;getAllDatas()&amp;lt;/code&amp;gt; PHP method. &amp;lt;code&amp;gt;getAllDatas()&amp;lt;/code&amp;gt; is called during each page reload.&lt;br /&gt;
&lt;br /&gt;
In the &amp;lt;code&amp;gt;getAllDatas()&amp;lt;/code&amp;gt; method, after &amp;lt;code&amp;gt;// TODO: Gather all information about current game situation (visible by player $current_player_id)&amp;lt;/code&amp;gt;, add the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// Get reversi board token&lt;br /&gt;
$result[&#039;board&#039;] = $this-&amp;gt;boardManager-&amp;gt;getOccupiedDiscs();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
And in the board manager, add:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function getOccupiedDiscs(): array&lt;br /&gt;
    {&lt;br /&gt;
        return $this-&amp;gt;game-&amp;gt;getObjectListFromDB(&lt;br /&gt;
            &amp;quot;SELECT `board_x` `x`, `board_y` `y`, `board_player` `player`&lt;br /&gt;
             FROM `board`&lt;br /&gt;
             WHERE `board_player` IS NOT NULL&amp;quot;&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Next, you will modify the query that gets player information to also get the player&#039;s colors. Above the lines you just inserted in &amp;lt;code&amp;gt;getAllDatas()&amp;lt;/code&amp;gt; notice how we&#039;ve added &amp;lt;code&amp;gt;player_color color&amp;lt;/code&amp;gt; to the sql query in the code below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result[&amp;quot;players&amp;quot;] = $this-&amp;gt;getCollectionFromDb(&lt;br /&gt;
    &amp;quot;SELECT `player_id` `id`, `player_score` `score`, `player_color` `color` FROM `player`&amp;quot;&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We are using the BGA framework&#039;s &amp;lt;code&amp;gt;getObjectListFromDB()&amp;lt;/code&amp;gt; that formats the result of this SQL query in a PHP array with x, y and player attributes. We add it to the result associative array with the key &amp;lt;code&amp;gt;board&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Last, we process this array client side. Let&#039;s place a token on the board for each array item. We&#039;ll do this in our Javascript &amp;lt;code&amp;gt;setup&amp;lt;/code&amp;gt; method under the code we added to generate the squares. &lt;br /&gt;
&lt;br /&gt;
This will result in a removal or edit of the previously added line &amp;lt;code&amp;gt;this.addDiscOnBoard(2, 2, this.bga.players.getCurrentPlayerId());&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for( var i in gamedatas.board ) {&lt;br /&gt;
    const square = gamedatas.board[i];&lt;br /&gt;
    &lt;br /&gt;
    if( square.player !== null ) {&lt;br /&gt;
        this.addDiscOnBoard( square.x, square.y, square.player );&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;board&amp;lt;/code&amp;gt; entry created in &amp;lt;code&amp;gt;getAllDatas()&amp;lt;/code&amp;gt; is used here as &amp;lt;code&amp;gt;gamedatas.board&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Test the Game Start===&lt;br /&gt;
Reload... and here we are:&lt;br /&gt;
&lt;br /&gt;
[[File:reversi5.jpg]]&lt;br /&gt;
&lt;br /&gt;
It starts to feel like Reversi here...&lt;br /&gt;
&lt;br /&gt;
==State Machine==&lt;br /&gt;
&lt;br /&gt;
Stop your game, again. You&#039;re about to start the core game logic.&lt;br /&gt;
&lt;br /&gt;
You already read [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine], so you know that this is the heart of your game logic. For reversi, it&#039;s relatively simple. Here&#039;s a diagram of our game state machine:&lt;br /&gt;
&lt;br /&gt;
[[File:reversi6.jpg]]&lt;br /&gt;
&lt;br /&gt;
===Build your States===&lt;br /&gt;
And here are the classes we need to create on the &amp;lt;code&amp;gt;module/php/States&amp;lt;/code&amp;gt; folder, according to this diagram:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PlayDisc.php:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
&lt;br /&gt;
namespace Bga\Games\Reversi\States;&lt;br /&gt;
&lt;br /&gt;
use Bga\GameFramework\StateType;&lt;br /&gt;
use Bga\Games\Reversi\Game;&lt;br /&gt;
&lt;br /&gt;
class PlayDisc extends \Bga\GameFramework\States\GameState&lt;br /&gt;
{&lt;br /&gt;
    public function __construct(protected Game $game) {&lt;br /&gt;
        parent::__construct($game, &lt;br /&gt;
            id: 10, &lt;br /&gt;
            type: StateType::ACTIVE_PLAYER,&lt;br /&gt;
&lt;br /&gt;
            description: clienttranslate(&#039;${actplayer} must play a disc&#039;),&lt;br /&gt;
            descriptionMyTurn: clienttranslate(&#039;${you} must play a disc&#039;),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function zombie(int $playerId) {&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(The &#039;&#039;declare(strict_types=1)&#039;&#039; is optional but recommended for new games). The &#039;&#039;&amp;lt;?php&#039;&#039; at the beginning is necessary for your IDE to understand the php syntax. ) &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;NextPlayer.php:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
&lt;br /&gt;
namespace Bga\Games\Reversi\States;&lt;br /&gt;
&lt;br /&gt;
use Bga\GameFramework\StateType;&lt;br /&gt;
use Bga\Games\Reversi\Game;&lt;br /&gt;
&lt;br /&gt;
class NextPlayer extends \Bga\GameFramework\States\GameState&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
    public function __construct(protected Game $game) {&lt;br /&gt;
        parent::__construct($game, &lt;br /&gt;
            id: 90, &lt;br /&gt;
            type: StateType::GAME,&lt;br /&gt;
&lt;br /&gt;
            updateGameProgression: true,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note: state ids 1 and 99 are reserved by the framework for gameSetup and gameEnd. We don&#039;t need to describe those states as the framework will take care of that for us.&lt;br /&gt;
&lt;br /&gt;
In setupNewGame, make sure the return is &amp;lt;code&amp;gt;return PlayerTurn::class;&amp;lt;/code&amp;gt; so the framework knows which game state is the initial one.&lt;br /&gt;
&lt;br /&gt;
===Test Your States===&lt;br /&gt;
... and start a new Reversi game.&lt;br /&gt;
&lt;br /&gt;
As you can see on the screen capture below, the BGA framework makes the game jump to our first game state &amp;lt;code&amp;gt;PlayDisc&amp;lt;/code&amp;gt; right after the initial setup. That&#039;s why the status bar contains the description of &amp;lt;code&amp;gt;PlayDisc&amp;lt;/code&amp;gt; state (&amp;quot;XXXX must play a disc&amp;quot;):&lt;br /&gt;
&lt;br /&gt;
[[File:reversi7.jpg]]&lt;br /&gt;
&lt;br /&gt;
==The rules==&lt;br /&gt;
&lt;br /&gt;
We will use the &amp;lt;code&amp;gt;getPossibleMoves&amp;lt;/code&amp;gt; PHP method to: &lt;br /&gt;
*Indicate to the current player where she is allowed to play by returning a list of coordinates&lt;br /&gt;
*Check if the player has the right to play in the spot they choose&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
Example of &amp;lt;code&amp;gt;getPossibleMoves&amp;lt;/code&amp;gt; here https://gist.github.com/leocaseiro/a8bc2851bd0caddd06685b5035937d15&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is pure PHP programming here, and there are no special things from the BGA framework that can be used. This is why we won&#039;t go into details here. The overall idea is: &lt;br /&gt;
* Create a &amp;lt;code&amp;gt;getTurnedOverDiscs(x,y)&amp;lt;/code&amp;gt; method that returns coordinates of discs that would be turned over if a token would be played at &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;,&amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Loop through all free squares of the board and call the &amp;lt;code&amp;gt;getTurnedOverDiscs&amp;lt;/code&amp;gt; method on each of them. If at least 1 token is turned over, this is a valid move.&lt;br /&gt;
&lt;br /&gt;
IMPORTANT: Making a database query is slow! Please don&#039;t load the entire game board with a SQL query multiple times. In our implementation, we load the entire board once at the beginning of &amp;lt;code&amp;gt;getPossibleMoves&amp;lt;/code&amp;gt;, and then pass the board as an argument to all methods.&lt;br /&gt;
&lt;br /&gt;
If you want to look into details, please look at the &amp;quot;utility method&amp;quot; sections of &amp;lt;code&amp;gt;Game.php&amp;lt;/code&amp;gt;. If building the tutorial yourself, copy the functions under &amp;quot;Utility functions&amp;quot; comment from the Reversi tutorial.&lt;br /&gt;
&lt;br /&gt;
==Display allowed moves==&lt;br /&gt;
&lt;br /&gt;
Now we want to highlight the squares where the player can place a disc.&lt;br /&gt;
&lt;br /&gt;
To do this, we add a &amp;lt;code&amp;gt;getArgs&amp;lt;/code&amp;gt; method in &amp;lt;code&amp;gt;modules/php/States/PlayDisc.php&amp;lt;/code&amp;gt;. This method is called on the server each time we enter into &amp;lt;code&amp;gt;PlayDisc&amp;lt;/code&amp;gt; game state, and its result is transferred automatically to the client-side:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function getArgs(int $activePlayerId): array&lt;br /&gt;
{&lt;br /&gt;
    return [&lt;br /&gt;
        &#039;possibleMoves&#039; =&amp;gt; $this-&amp;gt;game-&amp;gt;boardManager-&amp;gt;getPossibleMoves($activePlayerId)&lt;br /&gt;
    ];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we need to handle it on the JS side.&lt;br /&gt;
Replace the state classes definition in the Game constructor by this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // Declare the State classes&lt;br /&gt;
        this.playDisc = new PlayDisc(this, bga);&lt;br /&gt;
        this.bga.states.register(&#039;PlayDisc&#039;, this.playDisc);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
So it matches the state we described in PHP. The state name is the PHP class name, that we use in register to link them.&lt;br /&gt;
&lt;br /&gt;
Replace the class PlayerTurn in &amp;lt;code&amp;gt;modules/js/Game.js&amp;lt;/code&amp;gt; by this one:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class PlayDisc {&lt;br /&gt;
    constructor(game, bga) {&lt;br /&gt;
        this.game = game;&lt;br /&gt;
        this.bga = bga;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    onEnteringState(args, isCurrentPlayerActive) {&lt;br /&gt;
        this.bga.statusBar.setTitle(isCurrentPlayerActive ? &lt;br /&gt;
            _(&#039;${you} must play a disc&#039;) :&lt;br /&gt;
            _(&#039;${actplayer} must play a disc&#039;)&lt;br /&gt;
        );&lt;br /&gt;
      &lt;br /&gt;
        if (isCurrentPlayerActive) {            &lt;br /&gt;
            this.updatePossibleMoves(args.possibleMoves);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Note:&amp;lt;/b&amp;gt; We set the title when entering the state, so you can now remove description/descriptionMyTurn in the PHP class that was just here for test purpose.&lt;br /&gt;
&lt;br /&gt;
We use the &amp;lt;code&amp;gt;getPossibleMoves&amp;lt;/code&amp;gt; method we just developed.&lt;br /&gt;
&lt;br /&gt;
Each time we enter into a new game state, we use the &amp;lt;code&amp;gt;onEnteringState&amp;lt;/code&amp;gt; Javascript method of the JS State class. This lets us use the data returned by the method above on the client side.&lt;br /&gt;
&lt;br /&gt;
So, when we enter into &amp;lt;code&amp;gt;PlayDisc&amp;lt;/code&amp;gt; game state, we call our &amp;lt;code&amp;gt;updatePossibleMoves&amp;lt;/code&amp;gt; method (under the &amp;quot;Utility methods&amp;quot; section). This method is added to PlayDisc class and looks like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
updatePossibleMoves(possibleMoves) {&lt;br /&gt;
    // Remove current possible moves&lt;br /&gt;
    document.querySelectorAll(&#039;.possibleMove&#039;).forEach(div =&amp;gt; div.classList.remove(&#039;possibleMove&#039;));&lt;br /&gt;
&lt;br /&gt;
    for( let x in possibleMoves ) {&lt;br /&gt;
        for( let y in possibleMoves[ x ] ) {&lt;br /&gt;
            // x,y is a possible move&lt;br /&gt;
            document.getElementById(`square_${x}_${y}`).classList.add(&#039;possibleMove&#039;);&lt;br /&gt;
        }            &lt;br /&gt;
    }&lt;br /&gt;
                &lt;br /&gt;
    this.bga.gameui.addTooltipToClass( &#039;possibleMove&#039;, &#039;&#039;, _(&#039;Place a disc here&#039;) );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Here&#039;s what this does. At first, it removes all &amp;lt;code&amp;gt;possibleMove&amp;lt;/code&amp;gt; classes currently applied with the very useful &amp;lt;code&amp;gt;document.querySelectorAll&amp;lt;/code&amp;gt; method.&lt;br /&gt;
&lt;br /&gt;
Then it loops through all possible moves our PHP &amp;lt;code&amp;gt;updatePossibleMoves&amp;lt;/code&amp;gt; function created for us, and adds the &amp;lt;code&amp;gt;possibleMove&amp;lt;/code&amp;gt; class to each corresponding square.&lt;br /&gt;
&lt;br /&gt;
Finally, it uses the BGA framework &amp;lt;code&amp;gt;addTooltipToClass&amp;lt;/code&amp;gt; method to associate a tooltip to all those highlighted squares so that players can understand their meaning.&lt;br /&gt;
&lt;br /&gt;
To see the possible moves we need to create a CSS class (&amp;lt;code&amp;gt;possibleMove&amp;lt;/code&amp;gt;) that can be applied to a &amp;lt;code&amp;gt;square&amp;lt;/code&amp;gt; element to highlight it:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.possibleMove {&lt;br /&gt;
    background-color: white;&lt;br /&gt;
    opacity: 0.2; &lt;br /&gt;
    cursor: pointer;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
And here we are:&lt;br /&gt;
&lt;br /&gt;
[[File:reversi8.jpg.jpg]]&lt;br /&gt;
&lt;br /&gt;
==Let&#039;s play ==&lt;br /&gt;
&lt;br /&gt;
From now, it&#039;s better to restart a game with 2 players, because we are going to implement a complete Reversi turn. The summary of what we are going to do is:&lt;br /&gt;
* When we click on a square, check if it is a &amp;lt;code&amp;gt;possibleMove&amp;lt;/code&amp;gt; and if so, send the move to the server.&lt;br /&gt;
*Server side, check the move is correct, apply Reversi rules and jump to next player.&lt;br /&gt;
*Client side, change the token position to reflect the move.&lt;br /&gt;
&lt;br /&gt;
First we associate each click on a square to one of our methods using our Javascript &amp;lt;code&amp;gt;setup&amp;lt;/code&amp;gt; method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
document.querySelectorAll(&#039;.square&#039;).forEach(square =&amp;gt; square.addEventListener(&#039;click&#039;, e =&amp;gt; this.onPlayDisc(e)));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, our &amp;quot;onPlayDisc&amp;quot; method is called each time someone clicks on a square.&lt;br /&gt;
&lt;br /&gt;
Here&#039;s our &amp;quot;onPlayDisc&amp;quot; method below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
onPlayDisc(evt) {&lt;br /&gt;
    // Stop this event propagation&lt;br /&gt;
    evt.preventDefault();&lt;br /&gt;
    evt.stopPropagation();&lt;br /&gt;
&lt;br /&gt;
    // Get the cliqued square x and y&lt;br /&gt;
    // Note: square id format is &amp;quot;square_X_Y&amp;quot;&lt;br /&gt;
    var coords = evt.currentTarget.id.split(&#039;_&#039;);&lt;br /&gt;
    var x = coords[1];&lt;br /&gt;
    var y = coords[2];&lt;br /&gt;
&lt;br /&gt;
    if(!document.getElementById(`square_${x}_${y}`).classList.contains(&#039;possibleMove&#039;)) {&lt;br /&gt;
        // This is not a possible move =&amp;gt; the click does nothing&lt;br /&gt;
        return ;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    this.bga.actions.performAction(&amp;quot;actPlayDisc&amp;quot;, {&lt;br /&gt;
        x:x,&lt;br /&gt;
        y:y&lt;br /&gt;
    });&lt;br /&gt;
},&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
What we do here is:&lt;br /&gt;
*We stop the propagation of the Javascript &amp;lt;code&amp;gt;onclick&amp;lt;/code&amp;gt; event. Otherwise, it can lead to random behavior so it&#039;s always a good idea.&lt;br /&gt;
*We get the x/y coordinates of the square by using &amp;lt;code&amp;gt;evt.currentTarget.id&amp;lt;/code&amp;gt;&lt;br /&gt;
*We check that clicked square has the &amp;lt;code&amp;gt;possibleMove&amp;lt;/code&amp;gt; class, otherwise we know for sure that we can&#039;t play there.&lt;br /&gt;
*Finally, we make a call to the server using BGA &amp;lt;code&amp;gt;bgaPerformAction&amp;lt;/code&amp;gt; method with argument x and y. This call will check that &amp;lt;code&amp;gt;actPlayDisc&amp;lt;/code&amp;gt; action is possible, according to current game state (we will add the &amp;lt;code&amp;gt;PossibleAction&amp;lt;/code&amp;gt; attribute on &amp;lt;code&amp;gt;actPlayDisc&amp;lt;/code&amp;gt; just after). This check is important to avoid issues if a player double clicks on a square.&lt;br /&gt;
&lt;br /&gt;
Now, we have to manage this &amp;lt;code&amp;gt;actPlayDisc&amp;lt;/code&amp;gt; action on the server side. Add a corresponding &amp;lt;code&amp;gt;actPlayDisc&amp;lt;/code&amp;gt; method in our &amp;lt;code&amp;gt;PlayDisc&amp;lt;/code&amp;gt; state class (&amp;lt;code&amp;gt;PlayDisc.php&amp;lt;/code&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#[PossibleAction]&lt;br /&gt;
function actPlayDisc( int $x, int $y, int $activePlayerId )&lt;br /&gt;
{&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
And add &amp;lt;code&amp;gt;use Bga\GameFramework\States\PossibleAction;&amp;lt;/code&amp;gt; at the top of the file.&lt;br /&gt;
&lt;br /&gt;
(The function will be called when the front-side action is triggered using the Autowire mechanism, if you want to see how it works in details check [[Main game logic: Game.php|https://en.doc.boardgamearena.com/Main_game_logic:_Game.php#Actions_%28autowired%29]] )&lt;br /&gt;
&lt;br /&gt;
now, we are using the &amp;lt;code&amp;gt;getTurnedOverDiscs&amp;lt;/code&amp;gt; method again to check that this move is possible:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // Now, check if this is a possible move&lt;br /&gt;
        $board = $this-&amp;gt;game-&amp;gt;boardManager-&amp;gt;getBoard();&lt;br /&gt;
        $turnedOverDiscs = $this-&amp;gt;game-&amp;gt;getTurnedOverDiscs( $x, $y, $activePlayerId, $board );&lt;br /&gt;
        &lt;br /&gt;
        if( count( $turnedOverDiscs ) === 0 ) {&lt;br /&gt;
            throw new SystemException(&amp;quot;Impossible move&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
we update the database to change the color of all turned over disc + the disc we just placed:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            // Let&#039;s place a disc at x,y and return all &amp;quot;$returned&amp;quot; discs to the active player&lt;br /&gt;
            &lt;br /&gt;
            $this-&amp;gt;game-&amp;gt;boardManager-&amp;gt;applyMove($activePlayerId, $x, $y, $turnedOverDiscs);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We add this part to the BoardManager before going back to our act function:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function applyMove(int $playerId, int $x, int $y, array $turnedOverDiscs): void&lt;br /&gt;
    {&lt;br /&gt;
        $positions = [];&lt;br /&gt;
        foreach ($turnedOverDiscs as $turnedOver) {&lt;br /&gt;
            $turnedX = (int) $turnedOver[&#039;x&#039;];&lt;br /&gt;
            $turnedY = (int) $turnedOver[&#039;y&#039;];&lt;br /&gt;
            $positions[] = &amp;quot;($turnedX,$turnedY)&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        $positions[] = &amp;quot;($x,$y)&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        $sql = &amp;quot;UPDATE `board` SET `board_player` = $playerId WHERE (`board_x`, `board_y`) IN (&amp;quot; . implode(&#039;,&#039;, $positions) . &amp;quot;)&amp;quot;;&lt;br /&gt;
        $this-&amp;gt;game-&amp;gt;DbQuery($sql);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
we manage game statistics:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $this-&amp;gt;bga-&amp;gt;playerStats-&amp;gt;inc(&#039;turnedOver&#039;, $disc_count, $activePlayerId);&lt;br /&gt;
        $updatedStat = &#039;discPlayedOnCenter&#039;;&lt;br /&gt;
        if( ($x==1 &amp;amp;&amp;amp; $y==1) || ($x==$board_size &amp;amp;&amp;amp; $y==1) || ($x==1 &amp;amp;&amp;amp; $y==$board_size) || ($x==$board_size &amp;amp;&amp;amp; $y==$board_size) ) {&lt;br /&gt;
            $updatedStat = &#039;discPlayedOnCorner&#039;;&lt;br /&gt;
        } else if( $x==1 || $x==$board_size || $y==1 || $y==$board_size ) {&lt;br /&gt;
            $updatedStat = &#039;discPlayedOnBorder&#039;;&lt;br /&gt;
        }&lt;br /&gt;
        $this-&amp;gt;bga-&amp;gt;playerStats-&amp;gt;inc($updatedStat, 1, $activePlayerId);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notify the turned discs:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            // Notify&lt;br /&gt;
            $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all( &amp;quot;playDisc&amp;quot;, clienttranslate( &#039;${player_name} plays a disc and turns over ${returned_nbr} disc(s)&#039; ), array(&lt;br /&gt;
                &#039;player_id&#039; =&amp;gt; $activePlayerId,&lt;br /&gt;
                &#039;player_name&#039; =&amp;gt; $this-&amp;gt;game-&amp;gt;getPlayerNameById($activePlayerId),&lt;br /&gt;
                &#039;returned_nbr&#039; =&amp;gt; count( $turnedOverDiscs ),&lt;br /&gt;
                &#039;x&#039; =&amp;gt; $x,&lt;br /&gt;
                &#039;y&#039; =&amp;gt; $y&lt;br /&gt;
            ) );&lt;br /&gt;
&lt;br /&gt;
            $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all( &amp;quot;turnOverDiscs&amp;quot;, &#039;&#039;, array(&lt;br /&gt;
                &#039;player_id&#039; =&amp;gt; $activePlayerId,&lt;br /&gt;
                &#039;turnedOver&#039; =&amp;gt; $turnedOverDiscs&lt;br /&gt;
            ) );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then we update the scores accordingly&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $playerIds = array_keys($this-&amp;gt;game-&amp;gt;loadPlayersBasicInfos());&lt;br /&gt;
        foreach ($playerIds as $playerId) {&lt;br /&gt;
            $tokens = $this-&amp;gt;game-&amp;gt;boardManager-&amp;gt;countDiscsForPlayer((int) $playerId);&lt;br /&gt;
            $this-&amp;gt;bga-&amp;gt;playerScore-&amp;gt;set($playerId, $tokens); // this will update the JS counter automatically&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In BoardManager, add:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function countDiscsForPlayer(int $playerId): int&lt;br /&gt;
    {&lt;br /&gt;
        return (int) $this-&amp;gt;game-&amp;gt;getUniqueValueFromDB(&amp;quot;SELECT COUNT(*) FROM `board` WHERE `board_player` = $playerId&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We are using for that 2 notifications (&amp;lt;code&amp;gt;playDisc&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;turnOverDiscs&amp;lt;/code&amp;gt;  that we are going to implement on client side later). Note that the description of the &amp;lt;code&amp;gt;playDisc&amp;lt;/code&amp;gt; notification will be logged in the game log.&lt;br /&gt;
&lt;br /&gt;
Finally, we jump to the next game state.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // Then, go to the next state&lt;br /&gt;
        return NextPlayer::class;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To make the statistics work, we have to initialize them in &amp;lt;code&amp;gt;stats.json&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;player&amp;quot;: {&lt;br /&gt;
    &amp;quot;discPlayedOnCorner&amp;quot;: {&lt;br /&gt;
      &amp;quot;id&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;Discs played on a corner&amp;quot;,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;discPlayedOnBorder&amp;quot;: {&lt;br /&gt;
      &amp;quot;id&amp;quot;: 11,&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;Discs played on a border&amp;quot;,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;discPlayedOnCenter&amp;quot;: {&lt;br /&gt;
      &amp;quot;id&amp;quot;: 12,&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;Discs played on board center part&amp;quot;,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;turnedOver&amp;quot;: {&lt;br /&gt;
      &amp;quot;id&amp;quot;: 13,&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;Number of discs turned over&amp;quot;,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A last thing to do on the server side is to activate the next player when we enter the &amp;lt;code&amp;gt;NextPlayer&amp;lt;/code&amp;gt; game state (in the &amp;lt;code&amp;gt;modules/php/States/NextPlayer.php&amp;lt;/code&amp;gt; file):&lt;br /&gt;
&lt;br /&gt;
Add `const ST_END_GAME = 99;` at the top of the file, then add:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function onEnteringState(): void&lt;br /&gt;
    {&lt;br /&gt;
        // Active next player&lt;br /&gt;
        $player_id = intval($this-&amp;gt;game-&amp;gt;activeNextPlayer());&lt;br /&gt;
&lt;br /&gt;
        // Check if both player has at least 1 discs, and if there are free squares to play&lt;br /&gt;
        $player_to_discs = $this-&amp;gt;game-&amp;gt;getCollectionFromDb( &amp;quot;SELECT board_player, COUNT( board_x )&lt;br /&gt;
                                                       FROM board&lt;br /&gt;
                                                       GROUP BY board_player&amp;quot;, true );&lt;br /&gt;
&lt;br /&gt;
        if( ! isset( $player_to_discs[ null ] ) )&lt;br /&gt;
        {&lt;br /&gt;
            // Index 0 has not been set =&amp;gt; there&#039;s no more free place on the board !&lt;br /&gt;
            // =&amp;gt; end of the game&lt;br /&gt;
            return ST_END_GAME;&lt;br /&gt;
        }&lt;br /&gt;
        else if( ! isset( $player_to_discs[ $player_id ] ) )&lt;br /&gt;
        {&lt;br /&gt;
            // Active player has no more disc on the board =&amp;gt; he looses immediately&lt;br /&gt;
            return ST_END_GAME;&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        // Can this player play?&lt;br /&gt;
&lt;br /&gt;
        $possibleMoves = $this-&amp;gt;game-&amp;gt;getPossibleMoves( $player_id );&lt;br /&gt;
        if( count( $possibleMoves ) == 0 )&lt;br /&gt;
        {&lt;br /&gt;
&lt;br /&gt;
            // This player can&#039;t play&lt;br /&gt;
            // Can his opponent play ?&lt;br /&gt;
            $opponent_id = (int)$this-&amp;gt;game-&amp;gt;getUniqueValueFromDb( &amp;quot;SELECT player_id FROM player WHERE player_id!=&#039;$player_id&#039; &amp;quot; );&lt;br /&gt;
            if( count( $this-&amp;gt;game-&amp;gt;getPossibleMoves( $opponent_id ) ) == 0 )&lt;br /&gt;
            {&lt;br /&gt;
                // Nobody can move =&amp;gt; end of the game&lt;br /&gt;
                return ST_END_GAME;&lt;br /&gt;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {            &lt;br /&gt;
                // =&amp;gt; pass his turn&lt;br /&gt;
                return NextPlayer::class;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            // This player can play. Give him some extra time&lt;br /&gt;
            $this-&amp;gt;game-&amp;gt;giveExtraTime( $player_id );&lt;br /&gt;
            return PlayDisc::class;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, when we play a token, the rules are checked and the token appears in the database.&lt;br /&gt;
&lt;br /&gt;
[[File:reversi9.jpg]]&lt;br /&gt;
&lt;br /&gt;
Of course, as we don&#039;t manage notifications on client side, we need to press F5 after each move to see the changes on the board.&lt;br /&gt;
&lt;br /&gt;
==Make the move appear automatically==&lt;br /&gt;
&lt;br /&gt;
Now, what we have to do is process the notifications sent by the server and make the move appear on the interface.&lt;br /&gt;
&lt;br /&gt;
In our &amp;lt;code&amp;gt;setupNotifications&amp;lt;/code&amp;gt; method in &amp;lt;code&amp;gt;modules/js/Game.js&amp;lt;/code&amp;gt;, we tell BGA to listen to notifications we will add later (&amp;lt;code&amp;gt;playDisc&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;turnOverDiscs&amp;lt;/code&amp;gt;) using promise-based notifications:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        setupNotifications() {&lt;br /&gt;
            console.log( &#039;notifications subscriptions setup&#039; );&lt;br /&gt;
&lt;br /&gt;
            // automatically listen to the notifications, based on the `notif_xxx` function on this class.&lt;br /&gt;
            this.bga.notifications.setupPromiseNotifications();&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We will associate each of our 3 notifications with a method prefixed with &amp;lt;code&amp;gt;notif_&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Let&#039;s have a look now on the &amp;lt;code&amp;gt;playDisc&amp;lt;/code&amp;gt; notification handler method:&amp;lt;pre&amp;gt;&lt;br /&gt;
        async notif_playDisc(args) {&lt;br /&gt;
            // Remove current possible moves (makes the board more clear)&lt;br /&gt;
            document.querySelectorAll(&#039;.possibleMove&#039;).forEach(div =&amp;gt; div.classList.remove(&#039;possibleMove&#039;));&lt;br /&gt;
        &lt;br /&gt;
            await this.addDiscOnBoard( args.x, args.y, args.player_id );&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
No surprise here, we re-used some existing stuff to: &lt;br /&gt;
*Remove the highlighted squares.&lt;br /&gt;
* Add a new disc on board, coming from player panel. That&#039;s where the async await part of &amp;lt;code&amp;gt;addDiscOnBoard&amp;lt;/code&amp;gt; becomes useful.&lt;br /&gt;
&lt;br /&gt;
Now, here&#039;s the method that handles the &amp;lt;code&amp;gt;turnOverDiscs&amp;lt;/code&amp;gt; notification:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        async animateTurnOverDisc(disc, targetColor) {&lt;br /&gt;
            const squareDiv = document.getElementById(`square_${disc.x}_${disc.y}`);&lt;br /&gt;
            const discDiv = document.getElementById(`disc_${disc.x}_${disc.y}`);&lt;br /&gt;
            &lt;br /&gt;
            squareDiv.classList.add(&#039;flip-animation&#039;);&lt;br /&gt;
            await this.bga.gameui.wait(500); // for the flip animation to finish&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            discDiv.dataset.color = targetColor;&lt;br /&gt;
&lt;br /&gt;
            const parallelAnimations = [{&lt;br /&gt;
                keyframes: [ // flip the disc&lt;br /&gt;
                    { transform: `rotateY(180deg)` },&lt;br /&gt;
                    { transform: `rotateY(0deg)` },&lt;br /&gt;
                ]&lt;br /&gt;
            }, {&lt;br /&gt;
                keyframes: [ // lift the disc&lt;br /&gt;
                    { transform: `translate(0, -12px) scale(1.2)`, offset: 0.5 },&lt;br /&gt;
                ]&lt;br /&gt;
            }];&lt;br /&gt;
&lt;br /&gt;
            await this.animationManager.slideAndAttach(discDiv, squareDiv, { duration: 1000, parallelAnimations });&lt;br /&gt;
            &lt;br /&gt;
            squareDiv.classList.remove(&#039;flip-animation&#039;);&lt;br /&gt;
            await this.bga.gameui.wait(500); // for the flip animation removal to finish&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        async notif_turnOverDiscs(args) {&lt;br /&gt;
            // Get the color of the player who is returning the discs&lt;br /&gt;
            const targetColor = this.gamedatas.players[ args.player_id ].color;&lt;br /&gt;
&lt;br /&gt;
            // wait for the animations of all turned discs to be over before considering the notif done&lt;br /&gt;
            await Promise.all(&lt;br /&gt;
                args.turnedOver.map(disc =&amp;gt; &lt;br /&gt;
                    this.animateTurnOverDisc(disc, targetColor)&lt;br /&gt;
                )&lt;br /&gt;
            );&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The list of the discs to be turned over has been made available by our server side code in &amp;lt;code&amp;gt;args.turnedOver&amp;lt;/code&amp;gt; (see previous paragraph). We loop through all these discs, and trigger a single disc animation using &amp;lt;code&amp;gt;animateTurnOverDisc&amp;lt;/code&amp;gt;. This function creates a complex animation using [[BgaAnimations]] for the disc in parameter.&lt;br /&gt;
&lt;br /&gt;
We create a chain of 3 animations using await to make the square highlight, then flip the token, then remove the square highlight.&lt;br /&gt;
&lt;br /&gt;
==Implement the zombie mode ==&lt;br /&gt;
When a player leave the table, a Zombie (bot) will play for the leaver, so the table can continue. For that, we write code in the zombieTurn function.&lt;br /&gt;
&lt;br /&gt;
to learn more about the Zombie mode, read [[Zombie Mode]]&lt;br /&gt;
&lt;br /&gt;
Add this at the end of your PlayDisc.php file :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function zombie(int $playerId) {&lt;br /&gt;
        // Zombie level 1&lt;br /&gt;
        $possibleMoves = $this-&amp;gt;game-&amp;gt;boardManager-&amp;gt;getPossibleMoves($playerId);&lt;br /&gt;
        // transform the 2 dimensional array into a flat array of possible [$x, $y]&lt;br /&gt;
        $possibleMovesArray = [];&lt;br /&gt;
        foreach($possibleMoves as $x =&amp;gt; $ys) {&lt;br /&gt;
            foreach($ys as $y =&amp;gt; $valid) {&lt;br /&gt;
                $possibleMovesArray[] = [$x, $y];&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $zombieChoice = $this-&amp;gt;getRandomZombieChoice($possibleMovesArray);&lt;br /&gt;
        return $this-&amp;gt;actPlayDisc($zombieChoice[0], $zombieChoice[1], $playerId);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And that&#039;s it ! Everytime a leaver should be playing, the Zombie will random play one of the possible moves, and the remaining player will be able to play the next move.&lt;br /&gt;
&lt;br /&gt;
== Add a debug function to help up test the code==&lt;br /&gt;
Add this at the end of your Game.php file :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    function debug_playAutomatically(int $moves = 50) {&lt;br /&gt;
        $this-&amp;gt;bga-&amp;gt;debug-&amp;gt;playUntil(fn(int $count) =&amp;gt; $count === $moves);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function debug_playToEndGame() {&lt;br /&gt;
        $this-&amp;gt;debug_playAutomatically(64); // reversi max moves is under 64 for the standard size board&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This function plays automatically until we reach the end of the game. The functions starting with &amp;quot;debug_&amp;quot; can be triggered in the Studio with a special menu.&lt;br /&gt;
It uses the tool &amp;lt;code&amp;gt;$this-&amp;gt;debug-&amp;gt;playUntil&amp;lt;/code&amp;gt; that will trigger zombie actions, but you could put any code in the debug function for the tests you need.&lt;br /&gt;
&lt;br /&gt;
Start a new game, click on the Bug icon on the top left then &amp;quot;playToEndGame&amp;quot;. You should see the game randomly playing until it reaches the end game, so it helps you check the animations, and you can see if the final scoring is also working as expected. This example will allow us to easily test the Zombie code, as it will play as a Zombie for all players until the end of the game. It&#039;s also an easy way to see if end score computation is working as expected!&lt;br /&gt;
&lt;br /&gt;
You can create as many debug function as you like!&lt;br /&gt;
&lt;br /&gt;
==Optional: add unit tests==&lt;br /&gt;
Follow the instruction on https://en.doc.boardgamearena.com/Testing_by_developer#PHP to setup phpunit and create the first Reversi unit tests that are given as examples there.&lt;br /&gt;
&lt;br /&gt;
Running &amp;lt;code&amp;gt;phpunit&amp;lt;/code&amp;gt;, it should be 4/4 OK! And we have a good hint the case of the full board should be handled OK without actually completing a game :)&lt;br /&gt;
&lt;br /&gt;
==After the tutorial==&lt;br /&gt;
You might want to check another tutorial, or start working on your first real project !&lt;br /&gt;
&lt;br /&gt;
[[Create a game in BGA Studio: Complete Walkthrough]]&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>RichardSPeters</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Tutorial_hearts&amp;diff=29137</id>
		<title>Tutorial hearts</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Tutorial_hearts&amp;diff=29137"/>
		<updated>2026-03-23T21:06:10Z</updated>

		<summary type="html">&lt;p&gt;RichardSPeters: small tweaks to sample code to bring it in line with current framework&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Using this tutorial, you can build a complete working game on the BGA environment: Hearts.&lt;br /&gt;
&lt;br /&gt;
Before you read this tutorial, you must:&lt;br /&gt;
* Read the overall presentations of the BGA Framework ([[Studio|see here]]).&lt;br /&gt;
* Some-what know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript&lt;br /&gt;
* Set up your development environment [http://en.doc.boardgamearena.com/First_steps_with_BGA_Studio First Steps with BGA Studio]&lt;br /&gt;
* As part of setup you have to have access to your ftp home folder in studio, which would have the full &#039;hearts&#039; game source code. We will be using some resources of this game in this tutorial, so copy it over to local disk if you have not done so.&lt;br /&gt;
&lt;br /&gt;
If you are stuck or have question about this tutorial, post on [https://forum.boardgamearena.com/viewforum.php?f=12 BGA Developers forum]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Hearts Rules ==&lt;br /&gt;
&lt;br /&gt;
Hearts is a trick-taking card game for four players where the goal is to score the fewest points. &lt;br /&gt;
Players aim to avoid taking tricks with heart cards (1 point each) and the Queen of Spades (13 points). &lt;br /&gt;
Each round, 13 cards are dealt, players pass three cards, and the player with the 2 of Clubs starts the first trick. &lt;br /&gt;
Play continues clockwise, with players needing to follow suit if they can, and the highest card of the lead suit wins the trick. &lt;br /&gt;
Hearts cannot be played until they are &amp;quot;broken&amp;quot; by a player who can&#039;t follow suit and discards a heart, or by a player leading with a heart after they&#039;ve been broken. &lt;br /&gt;
&lt;br /&gt;
== Create your first game ==&lt;br /&gt;
&lt;br /&gt;
If you have not already, you have to create a project in BGA Studio. For this tutorial you can create a project heartsYOURNAME where&lt;br /&gt;
YOURNAME is your developer login name (or shorter version of thereof). You can also re-use the project you have created for the &amp;quot;First Steps&amp;quot; tutorial above.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;&lt;br /&gt;
Note: please do &#039;&#039;&#039;not&#039;&#039;&#039; use the hearts project code as a base. This tutorial assumes you started with a TEMPLATE project with no prior modifications. &lt;br /&gt;
Using the hearts project as a base will be very confusing and you won&#039;t be able to follow all the steps. Also it will not match exactly with this tutorial for different reasons.&lt;br /&gt;
&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
With the initial skeleton of code provided, you can already start a game from the BGA Studio. &lt;br /&gt;
&lt;br /&gt;
1. Find and express start the game in turn-based mode with 4 players. Make sure it works. If you want to see the game as 2nd player press red arrow button on the player panel to switch to that player. More details can be found in [[First_steps_with_BGA_Studio]]&lt;br /&gt;
&lt;br /&gt;
2. Modify the text in .js file (for example replace &amp;quot;Player zone content goes here&amp;quot; to &amp;quot;Hello&amp;quot;), reload the page in the browser and make sure your ftp sync works as expected.&lt;br /&gt;
Note: if you have not setup auto-sync do it now, manually copying files is a no-starter.&lt;br /&gt;
&lt;br /&gt;
3. Express stop from settings menu (the gear icon).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Attention!!!&amp;lt;/b&amp;gt; Very important note about reloading, if you don&#039;t remember this you may spend hours debugging. The browser caches images. If you change any of these files, you have to do &amp;quot;full reload&amp;quot; which is usually Ctrl+F5 (or Ctrl+reload button on browser) not just a regular reload.&lt;br /&gt;
&lt;br /&gt;
== Hook version control system ==&lt;br /&gt;
&lt;br /&gt;
For a real game, or even for this tutorial, we recommend committing the code to version control right from the start. You are going to find yourself in a situation where the game doesn&#039;t even start anymore and no way of debugging it, unless you have a way to revert. That is where version control becomes very handy. If you are not familiar with version control (e.g. [https://git-scm.com/docs/gittutorial git]) then at least back up your files after each major change. Start now.&lt;br /&gt;
&lt;br /&gt;
Code for this tutorial available is on github: https://github.com/elaskavaia/bga-heartsla&lt;br /&gt;
&lt;br /&gt;
Different revisions represent different steps along the process, starting from original template to a PARTIAL game.&lt;br /&gt;
&lt;br /&gt;
Note: the game was re-written using new template, the old code is in &amp;quot;oldframework&amp;quot; branch. The new template is in main branch.&lt;br /&gt;
&lt;br /&gt;
The real hearts game (that you can play on BGA) can be found in your FTP home folder, after getting read-only access, go to https://studio.boardgamearena.com/projects, select Already Published and find Hearts to get access&lt;br /&gt;
(It may not match this tutorial as framework diverged since this game was created and it may not have been updated)&lt;br /&gt;
&lt;br /&gt;
== Update game infos and box graphics ==&lt;br /&gt;
&lt;br /&gt;
Even it does nothing yet, always start by making sure the game looks decent in the game selector, meaning it has nice box graphics and its information is correct. For that we need to edit [[Game_meta-information: gameinfos.inc.php|gameinfos.inc.php]].&lt;br /&gt;
&lt;br /&gt;
For a real game, you would go to [http://boardgamegeek.com BoardGameGeek], find the game, and use the information from BGG to fill in the gameinfos.&lt;br /&gt;
&lt;br /&gt;
So let&#039;s do that. Find &amp;quot;hearts&amp;quot; on BoardGameGeek. (Hint: Original release 1850 :))&lt;br /&gt;
&lt;br /&gt;
You can fill in the year of publishing and bgg id, put &#039;&#039;Public Domain&#039;&#039; under publisher (for a real game, leave an empty string so it won&#039;t be displayed), and a publisher id of 171 for public domain. And as designer and author you can just put your own name just for fun. Set number of players to 4.&lt;br /&gt;
&lt;br /&gt;
  // Game publisher&lt;br /&gt;
    &#039;publisher&#039; =&amp;gt; &#039;Public Domain&#039;,&lt;br /&gt;
&lt;br /&gt;
  // Board Game Geek ID of the publisher&lt;br /&gt;
    &#039;publisher_bgg_id&#039; =&amp;gt; 171,&lt;br /&gt;
&lt;br /&gt;
  // Players configuration that can be played (ex: 2 to 4 players)&lt;br /&gt;
  &#039;players&#039; =&amp;gt; array( 4 ),  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important step&#039;&#039;&#039;: you have to refresh the information in the Studio website through the control panel. So go to Control Panel -&amp;gt; Manage Games -&amp;gt; heartsYOURNAME&lt;br /&gt;
and press Reload for &#039;Reload game informations&#039;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The next step would be to replace game box with nicer images. This can be done from the [[Game metadata manager]]. &lt;br /&gt;
&lt;br /&gt;
Now try to start the game again. If you somehow introduced a syntax error in the gameinfos file it may not work (the game won&#039;t start).&lt;br /&gt;
Always use the &amp;quot;Express Start&amp;quot; button to start the game. You should see a standard state prompt from the template. You should see 4 players on the right: testdude0 .. testdude3.&lt;br /&gt;
To switch between them press the red arrow button near their names, it will open another tab. This way you don&#039;t need to login and logout from multiple accounts!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Note: if you had run the game before with less than 4 players there is a bug that will prevent you from running it with 4 only (if you did not run it before or run it with 4 players as instructed stop reading this note), to workaround revert back to original players array (i.e. 1,2,3,4), reload game options, then create a table with 4 players, exit that game table, then change gameoptions to 4 only as above, reload game options, create table again.&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Layout and Graphics ==&lt;br /&gt;
&lt;br /&gt;
In this section we will do graphics of the game, and main layout of the game.&lt;br /&gt;
&lt;br /&gt;
First copy a sprite with cards image from [https://x.boardgamearena.net/data/others/cards/FULLREZ_CARDS_ORIGINAL_NORMAL.jpg]  into img/cards.jpg folder of your project. &lt;br /&gt;
&lt;br /&gt;
Details about images can be found here: [[Game art: img directory]]. If you did not setup auto-sync of files, sync the graphics manually with remote folder (re-sync with your workspace).&lt;br /&gt;
&lt;br /&gt;
Edit .js to add some divs to represent player table and hand area, at the beginning of the setup function&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  setup(gamedatas) {&lt;br /&gt;
    console.log(&amp;quot;Starting game setup&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    this.bga.gameArea.getElement().insertAdjacentHTML(&lt;br /&gt;
      &amp;quot;beforeend&amp;quot;,&lt;br /&gt;
      `&lt;br /&gt;
                &amp;lt;div id=&amp;quot;myhand_wrap&amp;quot; class=&amp;quot;whiteblock&amp;quot;&amp;gt;&lt;br /&gt;
                    &amp;lt;b id=&amp;quot;myhand_label&amp;quot;&amp;gt;${_(&amp;quot;My hand&amp;quot;)}&amp;lt;/b&amp;gt;&lt;br /&gt;
                        &amp;lt;div id=&amp;quot;myhand&amp;quot;&amp;gt;&lt;br /&gt;
                        &amp;lt;/div&amp;gt;&lt;br /&gt;
                    &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            `,&lt;br /&gt;
    );&lt;br /&gt;
    // ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you refresh you should see now white area with My Hand title.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Heartsla-tpl2.png]]&lt;br /&gt;
&lt;br /&gt;
Now lets add a card into the hand, just so you can feel it. Edit the html snippet we inserted earlier buy adding a line representing a card&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    &amp;lt;div id=&amp;quot;myhand&amp;quot;&amp;gt;&lt;br /&gt;
       &amp;lt;div class=&amp;quot;fakecard&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Edit .css file, add this code (.css file is empty now, only has comments, just tuck this at the end)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.fakecard {&lt;br /&gt;
    display: inline-block;&lt;br /&gt;
    position: relative;&lt;br /&gt;
    margin-top: 5px;&lt;br /&gt;
    border-radius: 5%;&lt;br /&gt;
    width: 100px;&lt;br /&gt;
    height: 135px;&lt;br /&gt;
    background-size: calc(100px * 15);&lt;br /&gt;
    background-image: url(&#039;img/cards.jpg&#039;); /* temp hack to see it */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When you change existing graphics files remember that you have to FORCE-reload page, i.e. Ctrl-F5, otherwise its cached.&lt;br /&gt;
&lt;br /&gt;
You should see this (more less):&lt;br /&gt;
&lt;br /&gt;
[[File:Heartsla-tpl3.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Note: If you don&#039;t see the card a) check it was synced to remote folder b) force reload page&lt;br /&gt;
&lt;br /&gt;
Awesome! Now lets do the rest of layout.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Let&#039;s complete the game template. You template should have this code, just leave it there&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      // Example to add a div on the game area&lt;br /&gt;
      this.bga.gameArea.getElement().insertAdjacentHTML(&amp;quot;beforeend&amp;quot;,&lt;br /&gt;
                            &amp;lt;div id=&amp;quot;player-tables&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
                        `&lt;br /&gt;
      );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then change the code following comment &amp;quot;// Setting up player boards&amp;quot; with this&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      // Setting up player boards&lt;br /&gt;
      const numPlayers = Object.keys(gamedatas.players).length;&lt;br /&gt;
      Object.values(gamedatas.players).forEach((player, index) =&amp;gt; {&lt;br /&gt;
        document.getElementById(&amp;quot;player-tables&amp;quot;).insertAdjacentHTML(&lt;br /&gt;
          &amp;quot;beforeend&amp;quot;,&lt;br /&gt;
          // we generate this html snippet for each player&lt;br /&gt;
          `&lt;br /&gt;
    &amp;lt;div class=&amp;quot;playertable whiteblock playertable_${DIRECTIONS[index]}&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;div class=&amp;quot;playertablename&amp;quot; style=&amp;quot;color:#${player.color};&amp;quot;&amp;gt;${player.name}&amp;lt;/div&amp;gt;&lt;br /&gt;
        &amp;lt;div id=&amp;quot;tableau_${player.id}&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
    `&lt;br /&gt;
        );&lt;br /&gt;
      });&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
What we did is we added a template for every players at the table.&lt;br /&gt;
Now try to reload you game.&lt;br /&gt;
Oops! it won&#039;t load. This is to teach you how it will look like when you have syntax error in your js file. The game will hang loading at 10% or so. How to know what happened?&lt;br /&gt;
Open dev tools in browser (usually F12) and navigate to Console tab. You will see a stack trace of where error is. In our case&lt;br /&gt;
  HeartsFIXME.js:68 Uncaught (in promise) ReferenceError: DIRECTIONS is not defined&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In real hearts game they use this direction array to map every player to direction (like North) but its not needed, we can just use player index. &lt;br /&gt;
Lets just replace DIRECTIONS[index] with index, i.e&lt;br /&gt;
  &amp;lt;div class=&amp;quot;playertable whiteblock playertable_${index}&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now delete the following section as we won&#039;t be using it&amp;lt;pre&amp;gt;&lt;br /&gt;
      // Add test action buttons in the action status bar, simulating a card click:&lt;br /&gt;
      playableCardsIds.forEach((cardId) =&amp;gt;&lt;br /&gt;
        this.bga.statusBar.addActionButton(&lt;br /&gt;
          _(&amp;quot;Play card with id ${card_id}&amp;quot;).replace(&amp;quot;${card_id}&amp;quot;, cardId),&lt;br /&gt;
          () =&amp;gt; this.onCardClick(cardId),&lt;br /&gt;
        ),&lt;br /&gt;
      );&lt;br /&gt;
&lt;br /&gt;
      this.bga.statusBar.addActionButton(&lt;br /&gt;
        _(&amp;quot;Pass&amp;quot;),&lt;br /&gt;
        () =&amp;gt; this.bga.actions.performAction(&amp;quot;actPass&amp;quot;),&lt;br /&gt;
        { color: &amp;quot;secondary&amp;quot; },&lt;br /&gt;
      );&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Reload. If everything went well you should see this:&lt;br /&gt;
&lt;br /&gt;
[[File:Heartsla-tpl4.png|alt=Display player space of all players]]&lt;br /&gt;
&lt;br /&gt;
These are &amp;quot;tableau&amp;quot; areas for 4 players plus My hand visible only to one player. They are not exactly how we wanted them to be because we did not edit .css yet.&lt;br /&gt;
&lt;br /&gt;
Now edit .css, add these lines after import before our previous definition&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:root {&lt;br /&gt;
  --h-card-width: 100px;&lt;br /&gt;
  --h-card-height: 135px;&lt;br /&gt;
  --h-tableau-width: 220px;&lt;br /&gt;
  --h-tableau-height: 180px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#player-tables {&lt;br /&gt;
  position: relative;&lt;br /&gt;
  width: calc(var(--h-tableau-width) * 3.9);&lt;br /&gt;
  height: calc(var(--h-tableau-height) * 2.4);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.playertablename {&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.playertable {&lt;br /&gt;
  position: absolute;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  width: var(--h-tableau-width);&lt;br /&gt;
  height: var(--h-tableau-height);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.playertable_0 {&lt;br /&gt;
  top: 0px;&lt;br /&gt;
  left: 50%;&lt;br /&gt;
  margin-left: calc(var(--h-tableau-width) / 2 * -1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.playertable_1 {&lt;br /&gt;
  left: 0px;&lt;br /&gt;
  top: 50%;&lt;br /&gt;
  margin-top: calc(var(--h-tableau-height) / 2 * -1);&lt;br /&gt;
}&lt;br /&gt;
.playertable_2 {&lt;br /&gt;
  right: 0px;&lt;br /&gt;
  top: 50%;&lt;br /&gt;
  margin-top: calc(var(--h-tableau-height) / 2 * -1);&lt;br /&gt;
}&lt;br /&gt;
.playertable_3 {&lt;br /&gt;
  bottom: 0px;&lt;br /&gt;
  left: 50%;&lt;br /&gt;
  margin-left: calc(var(--h-tableau-width) / 2 * -1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and delete the following section from game.js as we won&#039;t be using it&amp;lt;pre&amp;gt;&lt;br /&gt;
      // example of adding a div for each player&lt;br /&gt;
      document.getElementById(&amp;quot;player-tables&amp;quot;).insertAdjacentHTML(&lt;br /&gt;
        &amp;quot;beforeend&amp;quot;,&lt;br /&gt;
        `&lt;br /&gt;
                &amp;lt;div id=&amp;quot;player-table-${player.id}&amp;quot;&amp;gt;&lt;br /&gt;
                    &amp;lt;strong&amp;gt;${player.name}&amp;lt;/strong&amp;gt;&lt;br /&gt;
                    &amp;lt;div&amp;gt;Player zone content goes here&amp;lt;/div&amp;gt;&lt;br /&gt;
                &amp;lt;/div&amp;gt;&lt;br /&gt;
            `,&lt;br /&gt;
      );&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now you force Reload and you should see this:&lt;br /&gt;
&lt;br /&gt;
[[File:Heartsla-tpl5.png]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Note: if you did not see changes you may have not force reloaded, force means you use Ctrl+F5 or Ctrl+Shift-R, if you don&#039;t &amp;quot;force&amp;quot; browser will use cached version of images! Which is not what you just changed&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here is some explanations about CSS (if you know everything about css already skip this):&lt;br /&gt;
* At top we defined some variables for sizes of cards and player &amp;quot;mats&amp;quot; (which we call tableau)&lt;br /&gt;
* We trying to layout mats in kind of diamond shape&lt;br /&gt;
* We define positions of our elements using top/bottom/left/right style property&lt;br /&gt;
* We used standard technique of centering the element which is use 50% for lets say &amp;quot;left&amp;quot;, and then shift by half of size of object to actually center it (margin-left). You can remove margins to see how it look if we did not do that&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Another Note: In general if you have auto-sync you don&#039;t need to reload if you change Game.php file, you need normal reload if you change js, and force reload for images. If you changed state machine or database you likely need to restart the game.&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Game Interface with BGA Cards ==&lt;br /&gt;
&lt;br /&gt;
The BGA framework provides a few out of the box classes to deal with cards. The client side&lt;br /&gt;
contains a component called [[BgaCards]] and it can be used for any dynamic html &amp;quot;pieces&amp;quot; management and animation. &lt;br /&gt;
On the server side we will use the [[Deck]] class which we discuss later.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you open cards.jpg in an image viewer you will see that it is a &amp;quot;sprite&amp;quot; image - a 15x4 grid of images stitched together,&lt;br /&gt;
which is a very efficient way to transport images. So we will use the card manager class to mark up these images and create&lt;br /&gt;
&amp;quot;card&amp;quot; divs for us and place them on the board.&lt;br /&gt;
&lt;br /&gt;
First, we need to add dependencies in the Game.js file:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const BgaAnimations = await importEsmLib(&#039;bga-animations&#039;, &#039;1.x&#039;);&lt;br /&gt;
const BgaCards = await importEsmLib(&#039;bga-cards&#039;, &#039;1.x&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we will remove the fake card we added (in Game.js file) search and remove: &amp;lt;pre&amp;gt;&lt;br /&gt;
    &amp;lt;div id=&amp;quot;myhand&amp;quot;&amp;gt;&lt;br /&gt;
       &amp;lt;div class=&amp;quot;fakecard&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then we will add initialization code of bga cards and related component in setup method after the template code (i.e. where we defined the myhand div as this div is referenced by the following code) and before setupNotifications &amp;lt;pre&amp;gt;&lt;br /&gt;
      // create the animation manager, and bind it to the `game.bgaAnimationsActive()` function&lt;br /&gt;
      this.animationManager = new BgaAnimations.Manager({&lt;br /&gt;
        animationsActive: () =&amp;gt; this.bga.gameui.bgaAnimationsActive(),&lt;br /&gt;
      });&lt;br /&gt;
&lt;br /&gt;
      const cardWidth = 100;&lt;br /&gt;
      const cardHeight = 135;&lt;br /&gt;
&lt;br /&gt;
      // create the card manager&lt;br /&gt;
      this.cardsManager = new BgaCards.Manager({&lt;br /&gt;
        animationManager: this.animationManager,&lt;br /&gt;
        type: &amp;quot;ha-card&amp;quot;, // the &amp;quot;type&amp;quot; of our cards in css&lt;br /&gt;
        getId: (card) =&amp;gt; card.id,&lt;br /&gt;
&lt;br /&gt;
        cardWidth: cardWidth,&lt;br /&gt;
        cardHeight: cardHeight,&lt;br /&gt;
        cardBorderRadius: &amp;quot;5%&amp;quot;,&lt;br /&gt;
        setupFrontDiv: (card, div) =&amp;gt; {&lt;br /&gt;
          div.dataset.type = card.type; // suit 1..4&lt;br /&gt;
          div.dataset.typeArg = card.type_arg; // value 2..14&lt;br /&gt;
          div.style.backgroundPositionX = `calc(100% / 14 * (${card.type_arg} - 2))`; // 14 is number of columns in stock image minus 1&lt;br /&gt;
          div.style.backgroundPositionY = `calc(100% / 3 * (${card.type} - 1))`; // 3 is number of rows in stock image minus 1&lt;br /&gt;
          this.bga.gameui.addTooltipHtml(div.id, `tooltip of ${card.type}`);&lt;br /&gt;
        },&lt;br /&gt;
      });&lt;br /&gt;
&lt;br /&gt;
      // create the stock, in the game setup&lt;br /&gt;
      this.handStock = new BgaCards.HandStock(&lt;br /&gt;
        this.cardsManager,&lt;br /&gt;
        document.getElementById(&amp;quot;myhand&amp;quot;)&lt;br /&gt;
      );&lt;br /&gt;
          // TODO: fix handStock&lt;br /&gt;
      this.handStock.addCards([&lt;br /&gt;
        { id: 1, type: 2, type_arg: 4 }, // 4 of hearts&lt;br /&gt;
        { id: 2, type: 3, type_arg: 11 }, // Jack of clubs&lt;br /&gt;
      ]); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Also we need to add this .css (anywhere), that will map front face of the card to our image (1500% is because this image 15 times bigger than single card on X axis)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.ha-card-front {&lt;br /&gt;
  background-size: 1500% auto;&lt;br /&gt;
  background-image: url(&amp;quot;img/cards.jpg&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Explanations:&lt;br /&gt;
* First we created animation manager which will be used later&lt;br /&gt;
* Then we define constant with width and height of our cards in pixes&lt;br /&gt;
* Then we create the cards manager. We tell it how to get unique id of each card (getId), and how to setup the div representing the front of the card (setupFrontDiv). In this function we set data attributes for type and type_arg which we will use later, and we set background position to show correct part of sprite image.&lt;br /&gt;
* Then we create a hand stock component which will represent player&#039;s hand. It is attached to div with id &amp;quot;myhand&amp;quot;.&lt;br /&gt;
* Finally we add two cards into the hand stock just for testing.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now if you reload you should see two cards in your hand:&lt;br /&gt;
&lt;br /&gt;
[[File:Heartsla-tpl6.png|alt=Display two cards in player&#039;s hand]]   &lt;br /&gt;
&lt;br /&gt;
Now we will add the &amp;quot;stock&amp;quot; object that will control player tableau (add in setup function before setupNotification)&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      // map stocks&lt;br /&gt;
&lt;br /&gt;
      this.tableauStocks = [];&lt;br /&gt;
      Object.values(gamedatas.players).forEach((player, index) =&amp;gt; {&lt;br /&gt;
        // add player tableau stock&lt;br /&gt;
        this.tableauStocks[player.id] = new BgaCards.LineStock(&lt;br /&gt;
          this.cardsManager,&lt;br /&gt;
          document.getElementById(`tableau_${player.id}`)&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
        // TODO: fix tableauStocks&lt;br /&gt;
        this.tableauStocks[player.id].addCards([&lt;br /&gt;
          { id: index + 10, type: index + 1, type_arg: index + 2 },&lt;br /&gt;
        ]);&lt;br /&gt;
      });&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Heartsla-tpl7.png]]   &lt;br /&gt;
&lt;br /&gt;
Explanations:&lt;br /&gt;
* We go over each player and create component called LineStock to represent player tableau, it will hold a single card&lt;br /&gt;
* We assign this into tableauStocks map indexed by player id to use later&lt;br /&gt;
* Finally we add a fake card into that stock just to see something&lt;br /&gt;
&lt;br /&gt;
Stock control can handle clicking on items and forms the selection. You can immediately react to selection&lt;br /&gt;
or you can query it later; for example when user presses some other button.&lt;br /&gt;
&lt;br /&gt;
Let&#039;s hook it up. Add this in the setup method in .js file, before // TODO: fix handStock:&lt;br /&gt;
&lt;br /&gt;
      this.handStock.setSelectionMode(&amp;quot;single&amp;quot;);&lt;br /&gt;
      this.handStock.onCardClick = (card) =&amp;gt; {&lt;br /&gt;
        alert(&amp;quot;boom!&amp;quot;);&lt;br /&gt;
      };&lt;br /&gt;
&lt;br /&gt;
Reload the game and click on one of the two Cards in your hand. You should get &amp;quot;boom&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
We will stop for now with client because we need to code some server stuff.&lt;br /&gt;
&lt;br /&gt;
== Game Database and Game Initialization ==&lt;br /&gt;
&lt;br /&gt;
Next step, you want to design a game database and setup a new game (on the server side).&lt;br /&gt;
For that we need to a) modify the database schema to add our cards data b) add some global variables into&lt;br /&gt;
the existing globals table.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
When you develop a game you need to figure out how you store your game pieces in database. This should be maximum 2 tables (like one for cards, items, tokens and meeples and one for counters).&lt;br /&gt;
In this game we will be using two tables, the default Deck table (supported by Deck component) and default &amp;quot;state variables&amp;quot; tables which called globals, to store some of integers.&lt;br /&gt;
In you never dealt with web servers - the database stores all information about your game and your php (server) code does not exists in memory between users actions.&lt;br /&gt;
&lt;br /&gt;
To modify the schema, first exit your existing game(s). Open &#039;&#039;&#039;dbmodel.sql&#039;&#039;&#039; file and uncomment the card table creation.&lt;br /&gt;
&lt;br /&gt;
This is the &amp;quot;card&amp;quot; table which will be managed by the Deck php class.&lt;br /&gt;
&lt;br /&gt;
This is how we map the database to our game:&lt;br /&gt;
* card_id: unique id of each card, it will be auto-generated&lt;br /&gt;
* card_type: it will be suite of the card 1 to 4 (Spades,Hearts,Clubs,Diamonds).&lt;br /&gt;
* card_type_arg: will be &amp;quot;value&amp;quot; of the card, 2 to 14 (2 is 2,...,10 is 10, 11 is Jack, ...)&lt;br /&gt;
* card_location: will be location of the card, like &amp;quot;deck&amp;quot;, &amp;quot;hand&amp;quot;, &amp;quot;tableau&amp;quot; etc&lt;br /&gt;
* card_location_arg: will be additional argument for location - the player id if card is in player&#039;s hand or tableau.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Game State Variables ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE TABLE IF NOT EXISTS `card` (&lt;br /&gt;
  `card_id` int(10) unsigned NOT NULL AUTO_INCREMENT,&lt;br /&gt;
  `card_type` varchar(16) NOT NULL,&lt;br /&gt;
  `card_type_arg` int(11) NOT NULL,&lt;br /&gt;
  `card_location` varchar(16) NOT NULL,&lt;br /&gt;
  `card_location_arg` int(11) NOT NULL,&lt;br /&gt;
  PRIMARY KEY (`card_id`)&lt;br /&gt;
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;&lt;br /&gt;
&amp;lt;/pre&amp;gt;Next we finally get into Game.php class (in modules/php subdir), where the main logic and db interaction would be. Find php constructor which should be &lt;br /&gt;
  function __construct( )&lt;br /&gt;
This is first function in a file. Add this code to constructor (replace existing initGameStateLabel if any).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function __construct()&lt;br /&gt;
    {&lt;br /&gt;
        parent::__construct();&lt;br /&gt;
        $this-&amp;gt;initGameStateLabels(&lt;br /&gt;
            [&lt;br /&gt;
                &amp;quot;trick_color&amp;quot; =&amp;gt; 11,&lt;br /&gt;
            ]&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;cards = $this-&amp;gt;deckFactory-&amp;gt;createDeck(&#039;card&#039;); // card is the our database name&lt;br /&gt;
        // ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you see errors in IDE its because we also have to declared &amp;quot;cards&amp;quot; as class member, add &#039;&#039;public Deck $cards;&#039;&#039; before the contructor.&lt;br /&gt;
Also if you using IDE it will suggest to import Deck class, accept it. If you are using &#039;vi&#039; just add this import where other imports (use in php) at the begging of the file after namespace declaration.&lt;br /&gt;
&lt;br /&gt;
  use Bga\GameFramework\Components\Deck;&lt;br /&gt;
&lt;br /&gt;
Here we are initializing three &amp;quot;Game State Variables&amp;quot; which are variables stored in the database. They are integers.&lt;br /&gt;
It must start with values higher or equal to 10 since values lower than 10 are reserved. These values are stored by numeric ids&lt;br /&gt;
in the database, but in the php we associate them with string labels for convenience of access. &lt;br /&gt;
&lt;br /&gt;
The variables are:&lt;br /&gt;
&lt;br /&gt;
*&amp;quot;trick_color&amp;quot;: numbers from 1 to 4 that map to card suit (not sure why it&#039;s called color; maybe it&#039;s a translation from French);&lt;br /&gt;
&lt;br /&gt;
The next 2 lines are creating $this-&amp;gt;cards object and associating it with &amp;quot;card&amp;quot; table in the the database.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;If we called db table &#039;foo&#039; instead of &#039;card&#039; the last statement would have been  $this-&amp;gt;cards-&amp;gt;createDeck( &amp;quot;foo&amp;quot; )&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: if you have some other leftovers from template like playerEnergy, leave it for now as is.&lt;br /&gt;
&lt;br /&gt;
Since we changed the database schema, we cannot re-use our existing game, we have to do express stop (from burger menu).&lt;br /&gt;
&lt;br /&gt;
Then start a new game and make sure it starts, then exit.&lt;br /&gt;
&amp;lt;i&amp;gt;&lt;br /&gt;
If you made a mistake&lt;br /&gt;
in the .sql or php constructor the game won&#039;t start, and good luck debugging it. (That is why it&#039;s important to check&lt;br /&gt;
once in a while to make sure it still starts while you remember what you have changed.)&lt;br /&gt;
&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you game won&#039;t even load and you want to stop it:&lt;br /&gt;
https://en.doc.boardgamearena.com/Tools_and_tips_of_BGA_Studio#Stopping_Hanging_Game&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Game Setup ===&lt;br /&gt;
Now we can go to game initialization &#039;&#039;&#039;setupNewGame&#039;&#039;&#039; in Game.php. This method is called only once when the game is created.&lt;br /&gt;
&lt;br /&gt;
In your template project you should have code that deals with player table, just leave it as is. Start inserting the&lt;br /&gt;
other code after &amp;quot;// Init global values with their initial values&amp;quot; comment.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// Init global values with their initial values&lt;br /&gt;
&lt;br /&gt;
// Set current trick color to zero (= no trick color)&lt;br /&gt;
$this-&amp;gt;setGameStateInitialValue(&#039;trick_color&#039;, 0);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we initialize all the globals to 0.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Next is to create our cards in the database. We have one deck of cards so it&#039;s pretty simple.&lt;br /&gt;
Insert this after  // TODO: Setup the initial game situation here.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // Create cards&lt;br /&gt;
        $cards = [];&lt;br /&gt;
        foreach ($this-&amp;gt;card_types[&amp;quot;suites&amp;quot;] as $suit =&amp;gt; $suit_info) {&lt;br /&gt;
            // spade, heart, diamond, club&lt;br /&gt;
            foreach ($this-&amp;gt;card_types[&amp;quot;types&amp;quot;] as $value =&amp;gt; $info_value) {&lt;br /&gt;
                //  2, 3, 4, ... K, A&lt;br /&gt;
                $cards[] = [&#039;type&#039; =&amp;gt; $suit, &#039;type_arg&#039; =&amp;gt; $value, &#039;nbr&#039; =&amp;gt; 1];&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        $this-&amp;gt;cards-&amp;gt;createCards($cards, &#039;deck&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code that will create one of each card. But don&#039;t run it yet, because we missing &#039;&#039;card_types&#039;&#039;.&lt;br /&gt;
So we have state of the game in the database, but there is some static game information which never changes.&lt;br /&gt;
This information should be stored in .php and this way it can be accessed from all .php files (and .js if you send it via getAllDatas()).&lt;br /&gt;
&lt;br /&gt;
Note: originally it was stored in material.inc.php file which is no longer part of default template, when you have a lot of material it makes sence to get it out of Game.php&lt;br /&gt;
&lt;br /&gt;
We will edit  Game.php now by adding these lines in constructor (and replacing &#039;&#039;self::$CARD_TYPES&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $this-&amp;gt;card_types = [&lt;br /&gt;
            &amp;quot;suites&amp;quot; =&amp;gt; [&lt;br /&gt;
                1 =&amp;gt; [&lt;br /&gt;
                    &#039;name&#039; =&amp;gt; clienttranslate(&#039;Spade&#039;),&lt;br /&gt;
                ],&lt;br /&gt;
                2 =&amp;gt; [&lt;br /&gt;
                    &#039;name&#039; =&amp;gt; clienttranslate(&#039;Heart&#039;),&lt;br /&gt;
                ],&lt;br /&gt;
                3 =&amp;gt; [&lt;br /&gt;
                    &#039;name&#039; =&amp;gt; clienttranslate(&#039;Club&#039;),&lt;br /&gt;
                ],&lt;br /&gt;
                4 =&amp;gt; [&lt;br /&gt;
                    &#039;name&#039; =&amp;gt; clienttranslate(&#039;Diamond&#039;),&lt;br /&gt;
                ]&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;types&amp;quot; =&amp;gt; [&lt;br /&gt;
                2 =&amp;gt; [&#039;name&#039; =&amp;gt; &#039;2&#039;],&lt;br /&gt;
                3 =&amp;gt; [&#039;name&#039; =&amp;gt; &#039;3&#039;],&lt;br /&gt;
                4 =&amp;gt; [&#039;name&#039; =&amp;gt; &#039;4&#039;],&lt;br /&gt;
                5 =&amp;gt; [&#039;name&#039; =&amp;gt; &#039;5&#039;],&lt;br /&gt;
                6 =&amp;gt; [&#039;name&#039; =&amp;gt; &#039;6&#039;],&lt;br /&gt;
                7 =&amp;gt; [&#039;name&#039; =&amp;gt; &#039;7&#039;],&lt;br /&gt;
                8 =&amp;gt; [&#039;name&#039; =&amp;gt; &#039;8&#039;],&lt;br /&gt;
                9 =&amp;gt; [&#039;name&#039; =&amp;gt; &#039;9&#039;],&lt;br /&gt;
                10 =&amp;gt; [&#039;name&#039; =&amp;gt; &#039;10&#039;],&lt;br /&gt;
                11 =&amp;gt; [&#039;name&#039; =&amp;gt; clienttranslate(&#039;J&#039;)],&lt;br /&gt;
                12 =&amp;gt; [&#039;name&#039; =&amp;gt; clienttranslate(&#039;Q&#039;)],&lt;br /&gt;
                13 =&amp;gt; [&#039;name&#039; =&amp;gt; clienttranslate(&#039;K&#039;)],&lt;br /&gt;
                14 =&amp;gt; [&#039;name&#039; =&amp;gt; clienttranslate(&#039;A&#039;)]&lt;br /&gt;
            ]&lt;br /&gt;
        ];&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
If you pass a value to the client via notification you should always use untranslated strings, and the client will translate it. Function &#039;clienttranslate&#039; marks the value for translation but does not actually change it for php. For more about this wonderful translation stuff see [[Translations]].&lt;br /&gt;
&lt;br /&gt;
Can also have declared this in the class (replace $CARD_TYPES)&lt;br /&gt;
 public array $card_types;&lt;br /&gt;
&lt;br /&gt;
Reload to make it still works (no errors).&lt;br /&gt;
&lt;br /&gt;
==== Dealing Cards ====&lt;br /&gt;
After we have initialized our deck, we want to deal 13 at random for each player. Add this after createCards in setupNewGame function in the Game.php file:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// Shuffle deck&lt;br /&gt;
$this-&amp;gt;cards-&amp;gt;shuffle(&#039;deck&#039;);&lt;br /&gt;
// Deal 13 cards to each players&lt;br /&gt;
$players = $this-&amp;gt;loadPlayersBasicInfos();&lt;br /&gt;
foreach ($players as $player_id =&amp;gt; $player) {&lt;br /&gt;
    $this-&amp;gt;cards-&amp;gt;pickCards(13, &#039;deck&#039;, $player_id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
In the next section we are going to learn how to show those cards to the right players, without exposing other player hands. &lt;br /&gt;
&lt;br /&gt;
==Full Game Model Synchronization==&lt;br /&gt;
&lt;br /&gt;
Now at any point in the game we need to make sure that database information can be reflected back in the UI, so we must fix the &#039;&#039;&#039;getAllDatas&#039;&#039;&#039; function&lt;br /&gt;
to return all possible data we need to reconstruct the game. This is in the Game.php file. &lt;br /&gt;
&lt;br /&gt;
=== Player&#039;s Hand ===&lt;br /&gt;
The template for getAllDatas() already takes care of player info. Let&#039;s just&lt;br /&gt;
add hand and tableau data before we return a result.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// Cards in player hand&lt;br /&gt;
$result[&#039;hand&#039;] = $this-&amp;gt;cards-&amp;gt;getCardsInLocation(&#039;hand&#039;, $currentPlayerId);&lt;br /&gt;
&lt;br /&gt;
// Cards played on the table&lt;br /&gt;
$result[&#039;cardsontable&#039;] = $this-&amp;gt;cards-&amp;gt;getCardsInLocation(&#039;cardsontable&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now on the client side we should display this data, so in your game.js file in the setup function (which is the receiver of getAllDatas), find // TODO: fix handStock&lt;br /&gt;
and replace our hack of putting cards directly into the hand with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      // Cards in player&#039;s hand&lt;br /&gt;
      this.handStock.addCards(Array.from(Object.values(this.gamedatas.hand)));&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
So we added all cards from server to the hand stock. Have to do this ugly array convertion, hopefully they will fix addCards so we don&#039;t need to do this.&lt;br /&gt;
&lt;br /&gt;
At this point, you have to RESTART a game and each player should see their hand!&lt;br /&gt;
&lt;br /&gt;
At any point if code does not work on clinet side, add command &amp;quot;debugger;&amp;quot; in the code. In browser press F12 to get dev tools, then reload.&lt;br /&gt;
You will hit breakpoint and can you in browser debugger. Don&#039;t forget to remove debugger; code after.&lt;br /&gt;
&lt;br /&gt;
=== Cards on Table ===&lt;br /&gt;
Now lets fix out tableau, find comment in setup method of .js file  // TODO: fix tableau&lt;br /&gt;
and remove stock.addCards... from that loop. But right after this add&lt;br /&gt;
      // Cards played on table&lt;br /&gt;
      for (i in this.gamedatas.cardsontable) {&lt;br /&gt;
        var card = this.gamedatas.cardsontable[i];&lt;br /&gt;
        var player_id = card.location_arg;&lt;br /&gt;
        this.tableauStocks[player_id].addCards([card]);&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
If you reload now you can see nothing on the table.&lt;br /&gt;
&lt;br /&gt;
Next, we will hook-up clicking on card and test if our animation.&lt;br /&gt;
&lt;br /&gt;
Find the &amp;quot;boom&amp;quot; we put in the click handler. Replace with this&lt;br /&gt;
      this.handStock.onCardClick = (card) =&amp;gt; {&lt;br /&gt;
         this.tableauStocks[card.location_arg].addCards([card]);&lt;br /&gt;
      };&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now if you reload you should be able to click on card from your hand and see it moving,&lt;br /&gt;
you can click on few cards this way. When you done enjoying the animation, press F5 to get your hand back.&lt;br /&gt;
&lt;br /&gt;
[[File:Heartsla-sync.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==State Machine==&lt;br /&gt;
&lt;br /&gt;
Stop the game. We are about to work on the game logic.&lt;br /&gt;
&lt;br /&gt;
You already read [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine], so you know that this is the heart of your game logic.&lt;br /&gt;
Note: ignore all the source snippets in this presentation, as framework changed, just note the concepts. &lt;br /&gt;
&lt;br /&gt;
Here are the states we need to build (excluding two more states we will add later to handle the exchange of cards at the beginning of the rounds):&lt;br /&gt;
&lt;br /&gt;
*Cards are dealt to all players (lets call it &amp;quot;NewHand&amp;quot;)&lt;br /&gt;
*Player start or respond to played card (&amp;quot;PlayerTurn&amp;quot;)&lt;br /&gt;
*Game control is passed to next player or trick is ended (&amp;quot;NextPlayer&amp;quot;)&lt;br /&gt;
*End of hand processing (scoring and check for end of game) (&amp;quot;EndHand&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Note: if you find states.inc.php file in top level directory - delete it now.&lt;br /&gt;
&lt;br /&gt;
=== State Templates ===&lt;br /&gt;
We will create just barebones state files first:&lt;br /&gt;
&lt;br /&gt;
==== States/NewHand.php ====&lt;br /&gt;
Let&#039;s create our first state - &amp;quot;NewHand&amp;quot;. Create a new file under &amp;quot;module/php/States&amp;quot; and name it &amp;quot;&#039;&#039;&#039;NewHand.php&#039;&#039;&#039;&amp;quot;. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
namespace Bga\Games\HeartsFIXME\States;&lt;br /&gt;
&lt;br /&gt;
use Bga\Games\HeartsFIXME\Game;&lt;br /&gt;
use Bga\GameFramework\StateType;&lt;br /&gt;
use Bga\GameFramework\States\GameState;&lt;br /&gt;
&lt;br /&gt;
class NewHand extends GameState&lt;br /&gt;
{&lt;br /&gt;
  public function __construct(protected Game $game)&lt;br /&gt;
  {&lt;br /&gt;
    parent::__construct(&lt;br /&gt;
      $game,&lt;br /&gt;
      id: 2, // the idea of the state&lt;br /&gt;
      type: StateType::GAME, // This type means that no player is active, and the game will automatically progress&lt;br /&gt;
      updateGameProgression: true, // entering this state can update the progress bar of the game&lt;br /&gt;
    );&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // The action we do when entering the state&lt;br /&gt;
  public function onEnteringState()&lt;br /&gt;
  {&lt;br /&gt;
    return PlayerTurn::class;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can read more about it here: [[State classes: State directory]]&lt;br /&gt;
&lt;br /&gt;
If you use IDE you see few errors:&lt;br /&gt;
* First there is no Bga\Games\HeartsFIXME\Game - that is because you games is not called HeartsFIXME, is something like HeartsFooBar - so you change this and namespace to your game name.&lt;br /&gt;
* Second it will compain abot NewTrick class - it does not exist yet&lt;br /&gt;
&lt;br /&gt;
Let&#039;s implement the other states:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== States/PlayerTurn.php ====&lt;br /&gt;
If file exists replace its content.&lt;br /&gt;
This action is different because it has an action a player must take. Read the comments in the code below to understand the syntax:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
&lt;br /&gt;
namespace Bga\Games\HeartsFIXME\States;&lt;br /&gt;
&lt;br /&gt;
use Bga\Games\HeartsFIXME\Game;&lt;br /&gt;
use Bga\GameFramework\StateType;&lt;br /&gt;
use Bga\GameFramework\States\PossibleAction;&lt;br /&gt;
use Bga\GameFramework\States\GameState;&lt;br /&gt;
use Bga\GameFramework\UserException;&lt;br /&gt;
&lt;br /&gt;
class PlayerTurn extends GameState&lt;br /&gt;
{&lt;br /&gt;
    public function __construct(protected Game $game)&lt;br /&gt;
    {&lt;br /&gt;
        parent::__construct(&lt;br /&gt;
            $game,&lt;br /&gt;
            id: 31,&lt;br /&gt;
            type: StateType::ACTIVE_PLAYER, // This state type means that one player is active and can do actions&lt;br /&gt;
            description: clienttranslate(&#039;${actplayer} must play a card&#039;), // We tell OTHER players what they are waiting for&lt;br /&gt;
            descriptionMyTurn: clienttranslate(&#039;${you} must play a card&#039;), // We tell the ACTIVE player what they must do&lt;br /&gt;
            // We suround the code with clienttranslate() so that the text is sent to the client for translation (this will enable the game to support other languages)&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    #[PossibleAction] // a PHP attribute that tells BGA &amp;quot;this method describes a possible action that the player could take&amp;quot;, so that you can call that action from the front (the client)&lt;br /&gt;
    public function actPlayCard(int $cardId, int $activePlayerId)&lt;br /&gt;
    {&lt;br /&gt;
        // TODO: implement logic&lt;br /&gt;
        return NextPlayer::class; // after the action, we move to the next player&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function zombie(int $playerId)&lt;br /&gt;
    {&lt;br /&gt;
        // We must implement this so BGA can auto play in the case a player becomes a zombie, but for this tutorial we won&#039;t handle this case&lt;br /&gt;
        throw new UserException(&#039;Not implemented: zombie for player ${player_id}&#039;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
You would also notice the &amp;quot;zombie&amp;quot; method. This would allow BGA to auto-player for the player if they became inactive. This is mandatory, but we will implement this later.&lt;br /&gt;
&lt;br /&gt;
==== States/NextPlayer.php ====&lt;br /&gt;
This state have a couple of different options for what would be the next state:&lt;br /&gt;
&lt;br /&gt;
* If not all players played a card in the current trick - we need to go to &#039;&#039;&#039;PlayerTurn&#039;&#039;&#039; (for the next player)&lt;br /&gt;
* If all players finished the trick but still have cards in their hand - we need to go to &#039;&#039;&#039;PlayerTurn&#039;&#039;&#039;&lt;br /&gt;
* If this is the last trick (no more cards in end) and it&#039;s finished, we need to go to &#039;&#039;&#039;EndHand&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
We will implement this logic later. For now let&#039;s return &#039;&#039;&#039;NewHandPlayerTurn&#039;&#039;&#039; (see onEnteringState).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
namespace Bga\Games\HeartsFIXME\States;&lt;br /&gt;
&lt;br /&gt;
use Bga\GameFramework\StateType;&lt;br /&gt;
use Bga\Games\HeartsFIXME\Game;&lt;br /&gt;
use Bga\GameFramework\States\GameState;&lt;br /&gt;
&lt;br /&gt;
class NextPlayer extends GameState&lt;br /&gt;
{&lt;br /&gt;
  public function __construct(protected Game $game)&lt;br /&gt;
  {&lt;br /&gt;
    parent::__construct(&lt;br /&gt;
      $game,&lt;br /&gt;
      id: 32,&lt;br /&gt;
      type: StateType::GAME,&lt;br /&gt;
    );&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public function onEnteringState()&lt;br /&gt;
  {&lt;br /&gt;
    return PlayerTurn::class;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== States/EndHand.php ====&lt;br /&gt;
Here too we will have two options for transition, either we play another hand (&#039;&#039;&#039;NewHand&#039;&#039;&#039;) or we finish the game (a reserved id for finishing the game is &#039;&#039;&#039;99&#039;&#039;&#039;). &lt;br /&gt;
&lt;br /&gt;
We will implement this logic later. For now let&#039;s return &#039;&#039;&#039;NewHand&#039;&#039;&#039; (see onEnteringState).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
namespace Bga\Games\HeartsFIXME\States;&lt;br /&gt;
&lt;br /&gt;
use Bga\GameFramework\StateType;&lt;br /&gt;
use Bga\Games\HeartsFIXME\Game;&lt;br /&gt;
use Bga\GameFramework\States\GameState;&lt;br /&gt;
&lt;br /&gt;
class EndHand extends GameState&lt;br /&gt;
{&lt;br /&gt;
  public function __construct(protected Game $game)&lt;br /&gt;
  {&lt;br /&gt;
    parent::__construct(&lt;br /&gt;
      $game,&lt;br /&gt;
      id: 40,&lt;br /&gt;
      type: StateType::GAME,&lt;br /&gt;
      description: &amp;quot;&amp;quot;,&lt;br /&gt;
    );&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public function onEnteringState()&lt;br /&gt;
  {&lt;br /&gt;
    // TODO: implement logic&lt;br /&gt;
    return NewHand::class;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Check again that no HeartsFIXME left in the code, if yes replace with game name.&lt;br /&gt;
&lt;br /&gt;
Remove EndScore.php - don&#039;t need it.&lt;br /&gt;
&lt;br /&gt;
Don&#039;t start the game yet, it won&#039;t load, we have to clean up bunch of template code in the .js&lt;br /&gt;
&lt;br /&gt;
=== Test Your Game is not broken ===&lt;br /&gt;
We changed state related logic, so we need to restart the game. If the game starts without error we are good. We won&#039;t be able to test the interactions yet because we need to implement the client side. &lt;br /&gt;
&lt;br /&gt;
Think the following is from the old framework and no longer relevant.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;s&amp;gt;Since we added bunch of different states we need to remove some more templace code, in .js file find onUpdateActionButtons, and remove all functional code, leaving just this&amp;lt;/s&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
    onUpdateActionButtons: function (stateName, args) {&lt;br /&gt;
      console.log(&amp;quot;onUpdateActionButtons: &amp;quot; + stateName, args);&lt;br /&gt;
&lt;br /&gt;
      if (this.isCurrentPlayerActive()) {&lt;br /&gt;
        switch (stateName) {&lt;br /&gt;
          case &amp;quot;playerTurn&amp;quot;:&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== State Logic ===&lt;br /&gt;
Now if you RESTART the game, it should not crash and you see 13 cards in your hand&lt;br /&gt;
&lt;br /&gt;
==== New Hand ====&lt;br /&gt;
We need to:&lt;br /&gt;
&lt;br /&gt;
# Move all cards to the deck&lt;br /&gt;
# Shuffle the cards&lt;br /&gt;
# Deal the cards to the players&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Here&#039;s the code insert infro NewHand.php state:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // The action we do when entering the state&lt;br /&gt;
    public function onEnteringState()&lt;br /&gt;
    {&lt;br /&gt;
        $game = $this-&amp;gt;game;&lt;br /&gt;
        // Take back all cards (from any location =&amp;gt; null) to deck&lt;br /&gt;
        $game-&amp;gt;cards-&amp;gt;moveAllCardsInLocation(null, &amp;quot;deck&amp;quot;);&lt;br /&gt;
        $game-&amp;gt;cards-&amp;gt;shuffle(&#039;deck&#039;);&lt;br /&gt;
        // Deal 13 cards to each players&lt;br /&gt;
        // Create deck, shuffle it and give 13 initial cards&lt;br /&gt;
        $players = $game-&amp;gt;loadPlayersBasicInfos();&lt;br /&gt;
        foreach ($players as $player_id =&amp;gt; $player) {&lt;br /&gt;
            $cards = $game-&amp;gt;cards-&amp;gt;pickCards(13, &#039;deck&#039;, $player_id);&lt;br /&gt;
            // Notify player about his cards&lt;br /&gt;
            $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;player($player_id, &#039;newHand&#039;, &#039;&#039;, array(&#039;cards&#039; =&amp;gt; $cards));&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // reset trick color&lt;br /&gt;
        $this-&amp;gt;game-&amp;gt;setGameStateInitialValue(&#039;trick_color&#039;, 0);&lt;br /&gt;
&lt;br /&gt;
        // FIXME: first player one with 2 of clubs&lt;br /&gt;
        $first_player = (int) $this-&amp;gt;game-&amp;gt;getActivePlayerId();&lt;br /&gt;
        $this-&amp;gt;game-&amp;gt;gamestate-&amp;gt;changeActivePlayer($first_player);&lt;br /&gt;
        return PlayerTurn::class;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Next Player ====&lt;br /&gt;
Here we can handle the logic of what is the next state we need to move to:&amp;lt;pre&amp;gt;&lt;br /&gt;
public function onEnteringState()&lt;br /&gt;
  {&lt;br /&gt;
    $game = $this-&amp;gt;game;&lt;br /&gt;
    // Active next player OR end the trick and go to the next trick OR end the hand&lt;br /&gt;
    if ($game-&amp;gt;cards-&amp;gt;countCardInLocation(&#039;cardsontable&#039;) == 4) {&lt;br /&gt;
      // This is the end of the trick&lt;br /&gt;
      // Select the winner&lt;br /&gt;
      $best_value_player_id = $game-&amp;gt;activeNextPlayer(); // TODO figure out winner of trick&lt;br /&gt;
&lt;br /&gt;
      // Move all cards to &amp;quot;cardswon&amp;quot; of the given player&lt;br /&gt;
      $game-&amp;gt;cards-&amp;gt;moveAllCardsInLocation(&#039;cardsontable&#039;, &#039;cardswon&#039;, null, $best_value_player_id);&lt;br /&gt;
&lt;br /&gt;
      if ($game-&amp;gt;cards-&amp;gt;countCardInLocation(&#039;hand&#039;) == 0) {&lt;br /&gt;
        // End of the hand&lt;br /&gt;
        return EndHand::class;&lt;br /&gt;
      } else {&lt;br /&gt;
        // End of the trick&lt;br /&gt;
        // Reset trick suite to 0 &lt;br /&gt;
        $this-&amp;gt;game-&amp;gt;setGameStateInitialValue(&#039;trick_color&#039;, 0);&lt;br /&gt;
        return PlayerTurn::class;&lt;br /&gt;
      }&lt;br /&gt;
    } else {&lt;br /&gt;
      // Standard case (not the end of the trick)&lt;br /&gt;
      // =&amp;gt; just active the next player&lt;br /&gt;
      $player_id = $game-&amp;gt;activeNextPlayer();&lt;br /&gt;
      $game-&amp;gt;giveExtraTime($player_id);&lt;br /&gt;
      return PlayerTurn::class;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&#039;&#039;&#039;Important&#039;&#039;&#039;: All state actions game or player must return the next state transition (or thrown exception). &lt;br /&gt;
&lt;br /&gt;
==== Player Turn ====&lt;br /&gt;
We will not implement this yet, but we can throw an exception to check that the interaction is working properly. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#[PossibleAction] // a PHP attribute that tells BGA &amp;quot;this method describes a possible action that the player could take&amp;quot;, so that you can call that action from the front (the client)&lt;br /&gt;
  public function actPlayCard(int $cardId, int $activePlayerId)&lt;br /&gt;
  {&lt;br /&gt;
    throw new UserException(&amp;quot;Not implemented: $activePlayerId played card $cardId&amp;quot;);    &lt;br /&gt;
    return NextPlayer::class; // after the action, we move to the next player&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Client - Server Interactions==&lt;br /&gt;
&lt;br /&gt;
Now to implement things for real we have hook UI actions to ajax calls, and process notifications sent by the server.&lt;br /&gt;
So previously we hooked playCardOnTable right into js handler which caused client animation, in real game its a two&lt;br /&gt;
step operation. When user clicks on game element js client sends an ajax call to server, server processes it and updates database, server sends&lt;br /&gt;
notification in response, client hooks animations to server notification.&lt;br /&gt;
&lt;br /&gt;
So in .js code replace find out handStock.onCardClick and replace  the handler to&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    this.handStock.onCardClick = (card) =&amp;gt; {&lt;br /&gt;
      {&lt;br /&gt;
        console.log(&amp;quot;onCardClick : card &amp;quot;, card);&lt;br /&gt;
        console.log(&amp;quot;onCardClick : namestate &amp;quot;, this.gamedatas.gamestate.name);&lt;br /&gt;
        if (!card) return; // hmm - should never happen&lt;br /&gt;
        switch (this.gamedatas.gamestate.name) {&lt;br /&gt;
          case &amp;quot;PlayerTurn&amp;quot;:&lt;br /&gt;
            // Can play a card&lt;br /&gt;
            this.bga.actions.performAction(&amp;quot;actPlayCard&amp;quot;, { cardId: card.id });&lt;br /&gt;
&lt;br /&gt;
            break;&lt;br /&gt;
          case &amp;quot;GiveCards&amp;quot;:&lt;br /&gt;
            // Can give cards TODO&lt;br /&gt;
            break;&lt;br /&gt;
          default: {&lt;br /&gt;
            this.handStock.unselectAll();&lt;br /&gt;
            break;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now reload and when you click on card you should get a server response: Not implemented...&lt;br /&gt;
&lt;br /&gt;
Lets implement it, in &#039;&#039;&#039;PlayerTurn.php&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
We need to:&lt;br /&gt;
&lt;br /&gt;
# Move the card&lt;br /&gt;
# Notify all player on the the move&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#[PossibleAction]&lt;br /&gt;
  public function actPlayCard(int $cardId, int $activePlayerId)&lt;br /&gt;
  {&lt;br /&gt;
    $game = $this-&amp;gt;game;&lt;br /&gt;
    $game-&amp;gt;cards-&amp;gt;moveCard($cardId, &#039;cardsontable&#039;, $activePlayerId);&lt;br /&gt;
    // TODO: check rules here&lt;br /&gt;
    $currentCard = $game-&amp;gt;cards-&amp;gt;getCard($cardId);&lt;br /&gt;
    // And notify&lt;br /&gt;
        $game-&amp;gt;notify-&amp;gt;all(&#039;playCard&#039;, clienttranslate(&#039;${player_name} plays ${value_displayed} ${color_displayed}&#039;), [&lt;br /&gt;
            &#039;i18n&#039; =&amp;gt; array(&#039;color_displayed&#039;, &#039;value_displayed&#039;),&lt;br /&gt;
            &#039;card&#039; =&amp;gt; $currentCard,&lt;br /&gt;
            &#039;player_id&#039; =&amp;gt; $activePlayerId,&lt;br /&gt;
            &#039;player_name&#039; =&amp;gt; $game-&amp;gt;getActivePlayerName(),&lt;br /&gt;
            &#039;value_displayed&#039; =&amp;gt; $game-&amp;gt;card_types[&#039;types&#039;][$currentCard[&#039;type_arg&#039;]][&#039;name&#039;],&lt;br /&gt;
            &#039;color_displayed&#039; =&amp;gt; $game-&amp;gt;card_types[&#039;suites&#039;][$currentCard[&#039;type&#039;]][&#039;name&#039;]&lt;br /&gt;
        ]&lt;br /&gt;
        );&lt;br /&gt;
    return NextPlayer::class;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We get the card from client, we move it to the table (moveCard is hooked to database directly, its part of deck class),&lt;br /&gt;
we notify all players and we change state. What we are missing here is bunch of checks (rule enforcements), we will add it later.&lt;br /&gt;
&lt;br /&gt;
Interesting part about this notify is that we use i18n array for strings that needs to be translated by client, so&lt;br /&gt;
they are sent as English text in notification, then client has to know which parameters needs translating.&lt;br /&gt;
&lt;br /&gt;
On the client side .js we have to implement a notification handler to do the animation. Below the &#039;&#039;&#039;setupNotification&#039;&#039;&#039; method (which you don&#039;t need to touch) &lt;br /&gt;
after &amp;lt;code&amp;gt;// TODO: from this point and below, you can write your game notifications handling methods&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
you can put the following code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  async notif_newHand(args) {&lt;br /&gt;
    // We received a new full hand of 13 cards.&lt;br /&gt;
    this.handStock.removeAll();&lt;br /&gt;
    this.handStock.addCards(Array.from(Object.values(args.hand)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  async notif_playCard(args) {&lt;br /&gt;
    // Play a card on the table&lt;br /&gt;
    this.tableauStocks[args.player_id].addCards([args.card]);&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
BGA will automatically bind the event to the &#039;&#039;&#039;notif_{eventName} handler&#039;&#039;&#039; which will receive the &amp;quot;args&amp;quot; you passed from php.&lt;br /&gt;
&lt;br /&gt;
Refresh the page and try to play a card from the correct player. The card should move to the played area. When you refresh - you should still see the card there.&lt;br /&gt;
Swicth to next player using the arrows near player name and play next card.&lt;br /&gt;
Just before last card save the game state in &amp;quot;Save 1&amp;quot; slot (buttons in the bottom). These saves game states and you can reload it using &amp;quot;Load 1&amp;quot; later.&lt;br /&gt;
It is very handy.&lt;br /&gt;
Finish playing the trick. You will notice&lt;br /&gt;
after trick is done all cards remains on the table, but if you press F5 they would disappear, this is because&lt;br /&gt;
we updated database to pick-up the cards but did not send notification about it.&lt;br /&gt;
&lt;br /&gt;
So in &#039;&#039;&#039;NextPlayer.php&#039;&#039;&#039; file add notification after // Move all cards to &amp;quot;cardswon&amp;quot; of the given player:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            // Move all win cards to cardswon location&lt;br /&gt;
            $moved_cards = $game-&amp;gt;cards-&amp;gt;getCardsInLocation(&#039;cardsontable&#039;); // remember for notification what we moved&lt;br /&gt;
            $game-&amp;gt;cards-&amp;gt;moveAllCardsInLocation(&#039;cardsontable&#039;, &#039;cardswon&#039;, null, $best_value_player_id);&lt;br /&gt;
&lt;br /&gt;
            // Note: we use 2 notifications here in order we can pause the display during the first notification&lt;br /&gt;
            //  before we move all cards to the winner (during the second)&lt;br /&gt;
            $players = $game-&amp;gt;loadPlayersBasicInfos();&lt;br /&gt;
            $game-&amp;gt;notify-&amp;gt;all(&#039;trickWin&#039;, clienttranslate(&#039;${player_name} wins the trick&#039;), array(&lt;br /&gt;
                &#039;player_id&#039; =&amp;gt; $best_value_player_id,&lt;br /&gt;
                &#039;player_name&#039; =&amp;gt; $players[$best_value_player_id][&#039;player_name&#039;],&lt;br /&gt;
&lt;br /&gt;
            ));&lt;br /&gt;
&lt;br /&gt;
            $game-&amp;gt;notify-&amp;gt;all(&#039;giveAllCardsToPlayer&#039;, &#039;&#039;, array(&lt;br /&gt;
                &#039;player_id&#039; =&amp;gt; $best_value_player_id,&lt;br /&gt;
                &#039;cards&#039; =&amp;gt; $game-&amp;gt;cards-&amp;gt;getCards(array_keys($moved_cards))&lt;br /&gt;
            ));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You notice that we passes player_name in notification because its used in the message but its pretty redundant,&lt;br /&gt;
as we should be able to figure out player_name by player_id.&lt;br /&gt;
&lt;br /&gt;
There is a way to fix it.&lt;br /&gt;
&lt;br /&gt;
in constructor of Game.php uncomment the decorator and remove second part that related to cards, so you will end up with&lt;br /&gt;
        /* notification decorator */&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;notify-&amp;gt;addDecorator(function(string $message, array $args) {&lt;br /&gt;
            if (isset($args[&#039;player_id&#039;]) &amp;amp;&amp;amp; !isset($args[&#039;player_name&#039;]) &amp;amp;&amp;amp; str_contains($message, &#039;${player_name}&#039;)) {&lt;br /&gt;
                $args[&#039;player_name&#039;] = $this-&amp;gt;getPlayerNameById($args[&#039;player_id&#039;]);&lt;br /&gt;
            }&lt;br /&gt;
    &lt;br /&gt;
            return $args;&lt;br /&gt;
        });&lt;br /&gt;
Now we can remove player_name as notification argument in NextPlayer.php (and other states) and $players variable because its not used anymore.&lt;br /&gt;
&lt;br /&gt;
            $game-&amp;gt;notify-&amp;gt;all(&#039;trickWin&#039;, clienttranslate(&#039;${player_name} wins the trick&#039;), array(&lt;br /&gt;
                &#039;player_id&#039; =&amp;gt; $best_value_player_id,&lt;br /&gt;
            ));&lt;br /&gt;
&lt;br /&gt;
Now lets add these handlers in the .js file to handle our notifications:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  async notif_trickWin(args) {&lt;br /&gt;
    // We do nothing here (just wait in order players can view the 4 cards played before they&#039;re gone)&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  async notif_giveAllCardsToPlayer(args) {&lt;br /&gt;
    // Move all cards on table to given table, then destroy them&lt;br /&gt;
    const winner_id = args.player_id;&lt;br /&gt;
    const cards = Array.from(Object.values(args.cards));&lt;br /&gt;
    await this.tableauStocks[winner_id].addCards(cards);&lt;br /&gt;
  }&lt;br /&gt;
  // TODO: cards has to dissapear after&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ok we notice that cards that was won bunched up in ugly column and stay on tableau, but they should dissaper after trick is taken.&lt;br /&gt;
&lt;br /&gt;
Now lets fix the ugly stock. We can make tableau a bit bigger to fit 4 cards or we should make cards overlap, later makes more sense since making tableau too big will be ugly.&lt;br /&gt;
&lt;br /&gt;
I could not figure out how to do overlap in LineStock,  AI thinks that there is attribute cardOverlap that I can set when creatingt stock, but it does not work on LineStock (as on 1.7), &lt;br /&gt;
so lets just add css for this in .css file&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.playertable .ha-card ~ .ha-card {&lt;br /&gt;
    margin-left: calc(var(--h-card-width) * -0.8);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This uses tilda operator that target the sibling, which is essentially all cards except first.&lt;br /&gt;
&lt;br /&gt;
If you want to test that it works you can reload you test state using Load 1 button to see the finishing of a trick.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now reload to test the trick taking - it is pretty now .&lt;br /&gt;
&lt;br /&gt;
Final touch, we need card to dissapear into the void. The void we have to create first.&lt;br /&gt;
We need to add another node in the dom for that void stock, on server we called location &amp;quot;cardswon&amp;quot; so lets use same name, change the tableau template in .js file to this&amp;lt;pre&amp;gt;&lt;br /&gt;
            &amp;lt;div id=&amp;quot;tableau_${player.id}&amp;quot;/&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div id=&amp;quot;cardswon_${player.id}&amp;quot;/&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We added cardswon (and class for tableau just in case we need it later).&lt;br /&gt;
&lt;br /&gt;
Now in setup method of .js file we need to create stock for this location, in the loop where we adding tableau stock and the end of loop add this code:&lt;br /&gt;
        // add void stock&lt;br /&gt;
        new BgaCards.VoidStock(&lt;br /&gt;
          this.cardsManager,&lt;br /&gt;
          document.getElementById(`cardswon_${player.id}`),&lt;br /&gt;
          {&lt;br /&gt;
            autoPlace: (card) =&amp;gt;&lt;br /&gt;
              card.location === &amp;quot;cardswon&amp;quot; &amp;amp;&amp;amp; card.location_arg == player.id,&lt;br /&gt;
          }&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
If you notice we did not assign this to any variable, this is because we won&#039;t need to refer to it, we will use autoPlace feature, where cardManager will know where to place it based on the location from server.&lt;br /&gt;
Finally we just have to modify notification handler to add this animation,  this is final version (in .js file)&lt;br /&gt;
&lt;br /&gt;
    notif_giveAllCardsToPlayer: async function (args) {&lt;br /&gt;
      // Move all cards on table to given table, then destroy them&lt;br /&gt;
      const winner_id = args.player_id;&lt;br /&gt;
&lt;br /&gt;
      const cards = Array.from(Object.values(args.cards));&lt;br /&gt;
      await this.tableauStocks[winner_id].addCards(cards);&lt;br /&gt;
      await this.cardsManager.placeCards(cards); // auto-placement&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
So the function is async means it will return Promise. We are doing it so we can wait other animations to complete.&lt;br /&gt;
First we adding cards to player tableau, waiting for animation, then adding to our void stock where they are dissapear.&lt;br /&gt;
&lt;br /&gt;
Now after the trick you see all cards move towards the &amp;quot;player&#039;s stash&amp;quot;.&lt;br /&gt;
The animation is not ideal, so lets at void stock settings to see if can improve it: https://x.boardgamearena.net/data/game-libs/bga-cards/1.0.7/docs/classes/stocks_void-stock.VoidStock.html&lt;br /&gt;
Ok, well I could not figure it out, but now you know where docs for these components are.&lt;br /&gt;
We will do our CSS hack, in .css add:&lt;br /&gt;
&lt;br /&gt;
 .cardswon &amp;gt; .ha-card {&lt;br /&gt;
   position: absolute;&lt;br /&gt;
   top: 0 !important;&lt;br /&gt;
 } &lt;br /&gt;
&lt;br /&gt;
==Zombie turn==&lt;br /&gt;
&lt;br /&gt;
We will implement a zombie function now because a) we have to do it at some point&lt;br /&gt;
b) playing 13 cards from 4 players manually to test this game is super annoying - but we can actually re-use this feature to &amp;quot;auto-play&amp;quot;&lt;br /&gt;
&lt;br /&gt;
In &#039;&#039;&#039;PlayerTurn.php&#039;&#039;&#039; file, replace the zombie function with this code:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function zombie(int $playerId)&lt;br /&gt;
    {&lt;br /&gt;
        $game = $this-&amp;gt;game;&lt;br /&gt;
        // Auto-play a random card from player&#039;s hand&lt;br /&gt;
        $cards_in_hand = $game-&amp;gt;cards-&amp;gt;getCardsInLocation(&#039;hand&#039;, $playerId);&lt;br /&gt;
        if (count($cards_in_hand) &amp;gt; 0) {&lt;br /&gt;
            $card_to_play = $cards_in_hand[array_rand($cards_in_hand)];&lt;br /&gt;
            $game-&amp;gt;cards-&amp;gt;moveCard($card_to_play[&#039;id&#039;], &#039;cardsontable&#039;, $playerId);&lt;br /&gt;
            // Notify&lt;br /&gt;
            $game-&amp;gt;notify-&amp;gt;all(&lt;br /&gt;
                &#039;playCard&#039;,&lt;br /&gt;
                clienttranslate(&#039;${player_name} auto plays ${value_displayed} ${color_displayed}&#039;),&lt;br /&gt;
                [&lt;br /&gt;
                    &#039;i18n&#039; =&amp;gt; array(&#039;color_displayed&#039;, &#039;value_displayed&#039;),&lt;br /&gt;
                    &#039;card&#039; =&amp;gt; $card_to_play,&lt;br /&gt;
                    &#039;player_id&#039; =&amp;gt; $playerId,&lt;br /&gt;
                    &#039;value_displayed&#039; =&amp;gt; $game-&amp;gt;card_types[&#039;types&#039;][$card_to_play[&#039;type_arg&#039;]][&#039;name&#039;],&lt;br /&gt;
                    &#039;color_displayed&#039; =&amp;gt; $game-&amp;gt;card_types[&#039;suites&#039;][$card_to_play[&#039;type&#039;]][&#039;name&#039;]&lt;br /&gt;
                ]&lt;br /&gt;
            );&lt;br /&gt;
        }&lt;br /&gt;
        return NextPlayer::class;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, watch this! Click Debug symbol on top bar (bug) and select function &amp;quot;playAutomatically&amp;quot; (this is actually function in your php file! it starts with debug_),&lt;br /&gt;
and select number of moves, i.e. 4.&lt;br /&gt;
If your zombie function works correctly you will see player play automatically. To play whole hand it will be 52 moves (13*4).&lt;br /&gt;
&lt;br /&gt;
==Scoring and End of game handling==&lt;br /&gt;
&lt;br /&gt;
Now we should calculate scoring and for that we need to actually track who wins the trick.&lt;br /&gt;
Trick is won by the player with highest card (no trump). We just need to remember what is trick suite.&lt;br /&gt;
For which we will use state variable &#039;trick_color&#039; which we already conveniently created.&lt;br /&gt;
&lt;br /&gt;
In &#039;&#039;&#039;PlayerTurn.php&#039;&#039;&#039; state, add this before notification&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$currenttrick_color = $game-&amp;gt;getGameStateValue(&#039;trick_color&#039;);&lt;br /&gt;
if ($currenttrick_color == 0) $game-&amp;gt;setGameStateValue(&#039;trick_color&#039;, $currentCard[&#039;type&#039;]);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will make sure we remember the first suit being played, now we will use it. Modify the &#039;&#039;&#039;NextPlayer.php&#039;&#039;&#039; state to fix our TODO comment in onEnteringState&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // Active next player OR end the trick and go to the next trick OR end the hand&lt;br /&gt;
        if ($game-&amp;gt;cards-&amp;gt;countCardInLocation(&#039;cardsontable&#039;) == 4) {&lt;br /&gt;
            // This is the end of the trick&lt;br /&gt;
            $cards_on_table = $game-&amp;gt;cards-&amp;gt;getCardsInLocation(&#039;cardsontable&#039;);&lt;br /&gt;
            $best_value = 0;&lt;br /&gt;
            $best_value_player_id = $this-&amp;gt;game-&amp;gt;getActivePlayerId(); // fallback &lt;br /&gt;
            $currenttrick_color = $game-&amp;gt;getGameStateValue(&#039;trick_color&#039;);&lt;br /&gt;
            foreach ($cards_on_table as $card) {&lt;br /&gt;
                if ($card[&#039;type&#039;] == $currenttrick_color) {   // type is card suite&lt;br /&gt;
                    if ($best_value_player_id === null || $card[&#039;type_arg&#039;] &amp;gt; $best_value) {&lt;br /&gt;
                        $best_value_player_id = $card[&#039;location_arg&#039;]; // location_arg is player who played this card on table&lt;br /&gt;
                        $best_value = $card[&#039;type_arg&#039;]; // type_arg is value of the card (2 to 14)&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // Active this player =&amp;gt; he&#039;s the one who starts the next trick&lt;br /&gt;
            $this-&amp;gt;gamestate-&amp;gt;changeActivePlayer($best_value_player_id);&lt;br /&gt;
&lt;br /&gt;
            // Move all win cards to cardswon location&lt;br /&gt;
            $win_location = &#039;cardswon&#039;;&lt;br /&gt;
            $game-&amp;gt;cards-&amp;gt;moveAllCardsInLocation(&#039;cardsontable&#039;, $win_location, null, $best_value_player_id);&lt;br /&gt;
            // ... notification is the same as before&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The scoring rule in the studio example code is huge multi-page function, for this tutorial we will make simplier.&lt;br /&gt;
Lets score -1 point per heart and call it a day. And game will end when somebody goes -100 or below.&lt;br /&gt;
&lt;br /&gt;
As UI goes for scoring, the main thing to update is:&lt;br /&gt;
&lt;br /&gt;
* The scoring on the mini boards represented by stars&lt;br /&gt;
* Show that in the log. &lt;br /&gt;
&lt;br /&gt;
For a real game, you might consider showing the scoring in a [[Game_interface_logic:_yourgamename.js#Scoring_dialogs|Scoring Dialog]] using tableWindow notification, but this is out of scope of this tutorial. You can do that as homework. &lt;br /&gt;
&lt;br /&gt;
In &#039;&#039;&#039;EndHand.php&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use Bga\GameFramework\NotificationMessage; // add this to the top of the file, together with the other &amp;quot;use&amp;quot; statements&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
public function onEnteringState()&lt;br /&gt;
{&lt;br /&gt;
  $game = $this-&amp;gt;game;&lt;br /&gt;
  // Count and score points, then end the game or go to the next hand.&lt;br /&gt;
  $players = $game-&amp;gt;loadPlayersBasicInfos();&lt;br /&gt;
  // Gets all &amp;quot;hearts&amp;quot; + queen of spades&lt;br /&gt;
&lt;br /&gt;
  $player_to_points = array();&lt;br /&gt;
  foreach ($players as $player_id =&amp;gt; $player) {&lt;br /&gt;
    $player_to_points[$player_id] = 0;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  $cards = $game-&amp;gt;cards-&amp;gt;getCardsInLocation(&amp;quot;cardswon&amp;quot;);&lt;br /&gt;
  foreach ($cards as $card) {&lt;br /&gt;
    $player_id = $card[&#039;location_arg&#039;];&lt;br /&gt;
    // Note: 2 = heart&lt;br /&gt;
    if ($card[&#039;type&#039;] == 2) {&lt;br /&gt;
      $player_to_points[$player_id]++;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Apply scores to player&lt;br /&gt;
  foreach ($player_to_points as $player_id =&amp;gt; $points) {&lt;br /&gt;
    if ($points != 0) {&lt;br /&gt;
      $game-&amp;gt;playerScore-&amp;gt;inc(&lt;br /&gt;
        $player_id,&lt;br /&gt;
        -$points,&lt;br /&gt;
        new NotificationMessage(&lt;br /&gt;
          clienttranslate(&#039;${player_name} gets ${absInc} hearts and looses ${absInc} points&#039;),&lt;br /&gt;
        )&lt;br /&gt;
      );&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ///// Test if this is the end of the game&lt;br /&gt;
  if ($game-&amp;gt;playerScore-&amp;gt;getMin() &amp;lt;= -100) {&lt;br /&gt;
    // Trigger the end of the game !&lt;br /&gt;
    return 99; // end game&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  return NewHand::class;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The game should work now. Try to play it!&lt;br /&gt;
==Clean Up==&lt;br /&gt;
We left some code that comes from template and our first code, we should remove it now.&lt;br /&gt;
&lt;br /&gt;
* In .js file remove debugger; statements if any&lt;br /&gt;
* Remove debug code from setupNewGame to deal cards, cards are now dealt in stNewHand state handler&lt;br /&gt;
        // Shuffle deck&lt;br /&gt;
        $this-&amp;gt;cards-&amp;gt;shuffle(&#039;deck&#039;);&lt;br /&gt;
        // Deal 13 cards to each players&lt;br /&gt;
        $players = $this-&amp;gt;loadPlayersBasicInfos();&lt;br /&gt;
        foreach ($players as $player_id =&amp;gt; $player) {&lt;br /&gt;
            $this-&amp;gt;cards-&amp;gt;pickCards(13, &#039;deck&#039;, $player_id);&lt;br /&gt;
        }&lt;br /&gt;
* Find and remove $playerEnergy variable and it&#039;s uses from Game.php (was part of template)&lt;br /&gt;
&lt;br /&gt;
==Rule Enforcements==&lt;br /&gt;
Now we have a working game, but there is no rule enforcement.&lt;br /&gt;
You can implement these rules in the &#039;&#039;&#039;actPlayCard&#039;&#039;&#039; function of &#039;&#039;&#039;PlayerTurn.php&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
    public function actPlayCard(int $cardId, int $activePlayerId)&lt;br /&gt;
    {&lt;br /&gt;
        $game = $this-&amp;gt;game;&lt;br /&gt;
        $card = $game-&amp;gt;cards-&amp;gt;getCard($cardId);&lt;br /&gt;
        if (!$card) {&lt;br /&gt;
            throw new \BgaSystemException(&amp;quot;Invalid move&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        // Rule checks&lt;br /&gt;
&lt;br /&gt;
        // Check that player has this card in hand&lt;br /&gt;
        if ($card[&#039;location&#039;] != &amp;quot;hand&amp;quot;) {&lt;br /&gt;
            throw new \BgaUserException(&lt;br /&gt;
                clienttranslate(&#039;You do not have this card in your hand&#039;)&lt;br /&gt;
            );&lt;br /&gt;
        }&lt;br /&gt;
        $currenttrick_color = $game-&amp;gt;getGameStateValue(&#039;trick_color&#039;);&lt;br /&gt;
        // Check that player follows suit if possible&lt;br /&gt;
        if ($currenttrick_color != 0) {&lt;br /&gt;
            $has_suit = false;&lt;br /&gt;
            $hand_cards = $game-&amp;gt;cards-&amp;gt;getCardsInLocation(&#039;hand&#039;, $activePlayerId);&lt;br /&gt;
            foreach ($hand_cards as $hand_card) {&lt;br /&gt;
                if ($hand_card[&#039;type&#039;] == $currenttrick_color) {&lt;br /&gt;
                    $has_suit = true;&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if ($has_suit &amp;amp;&amp;amp; $card[&#039;type&#039;] != $currenttrick_color) {&lt;br /&gt;
                throw new \BgaUserException(&lt;br /&gt;
                    clienttranslate(&#039;You must follow suit&#039;)&lt;br /&gt;
                );&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can try to play wrong card now and you will see error!&lt;br /&gt;
But you noticed we broke the zombie mode as you cannot play random card anymore and now uThe user cannot play ANY card, there are only some cards they can play but we don&#039;t show this information which is annoying.&lt;br /&gt;
To fix this we can add a helper function that will return a list of playable cards for a given player.&lt;br /&gt;
Add these functions in &#039;&#039;&#039;Game.php&#039;&#039;&#039; file:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function getPlayableCards($player_id): array&lt;br /&gt;
    {&lt;br /&gt;
        // Get all data needed to check playable cards at the moment&lt;br /&gt;
        $currentTrickColor = $this-&amp;gt;getGameStateValue(&#039;trick_color&#039;);&lt;br /&gt;
        $broken_heart = $this-&amp;gt;brokenHeart();&lt;br /&gt;
        $total_played = $this-&amp;gt;cards-&amp;gt;countCardInLocation(&#039;cardswon&#039;) + $this-&amp;gt;cards-&amp;gt;countCardInLocation(&#039;cardsontable&#039;);&lt;br /&gt;
        $hand = $this-&amp;gt;cards-&amp;gt;getPlayerHand($player_id);&lt;br /&gt;
&lt;br /&gt;
        $playable_card_ids = [];&lt;br /&gt;
        $all_ids = array_keys($hand);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        if ($this-&amp;gt;cards-&amp;gt;getCardsInLocation(&#039;cardsontable&#039;, $player_id)) return []; // Already played a card&lt;br /&gt;
&lt;br /&gt;
        // Check whether the first card of the hand has been played or not&lt;br /&gt;
        // if ($total_played == 0) {&lt;br /&gt;
        //     // No cards have been played yet, find and return the starter card only&lt;br /&gt;
        //     foreach ($hand as $card) if ($card[&#039;type&#039;] == 3 &amp;amp;&amp;amp; $card[&#039;type_arg&#039;] == 2) return [$card[&#039;id&#039;]]; // 2 of clubs&lt;br /&gt;
        //     return [];&lt;br /&gt;
        // } else&lt;br /&gt;
        if (!$currentTrickColor) { // First card of the trick&lt;br /&gt;
            if ($broken_heart) return $all_ids; // Broken Heart or no limitation, can play any card&lt;br /&gt;
            else {&lt;br /&gt;
                // Exclude Heart as Heart hasn&#039;t been broken yet&lt;br /&gt;
                foreach ($hand as $card) if ($card[&#039;type&#039;] != 2) $playable_card_ids[] = $card[&#039;id&#039;];&lt;br /&gt;
                if (!$playable_card_ids) return $all_ids; // All Heart cards!&lt;br /&gt;
                else return $playable_card_ids;&lt;br /&gt;
            }&lt;br /&gt;
        } else {&lt;br /&gt;
            // Must follow the lead suit if possible&lt;br /&gt;
            $same_suit = false;&lt;br /&gt;
            foreach ($hand as $card)&lt;br /&gt;
                if ($card[&#039;type&#039;] == $currentTrickColor) {&lt;br /&gt;
                    $same_suit = true;&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
            if ($same_suit) return $this-&amp;gt;getObjectListFromDB(&amp;quot;SELECT card_id FROM card WHERE card_type = $currentTrickColor AND card_location = &#039;hand&#039; AND card_location_arg = $player_id&amp;quot;, true); // Has at least 1 card of the same suit&lt;br /&gt;
&lt;br /&gt;
            else return $all_ids;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function brokenHeart(): bool&lt;br /&gt;
    {&lt;br /&gt;
        // Check Heart in the played card piles&lt;br /&gt;
        return (bool)$this-&amp;gt;getUniqueValueFromDB(&amp;quot;SELECT count(*) FROM card WHERE card_location = &#039;cardswon&#039; AND card_type = 2&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function tableHeart(): bool&lt;br /&gt;
    {&lt;br /&gt;
        // Check Heart in the current trick&lt;br /&gt;
        return (bool)$this-&amp;gt;getUniqueValueFromDB(&amp;quot;SELECT count(*) FROM card WHERE card_location = &#039;cardsontable&#039; AND card_type = 2&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we can use this function in the zombie function of &#039;&#039;&#039;PlayerTurn.php&#039;&#039;&#039; to pick a random playable card:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function zombie(int $playerId)&lt;br /&gt;
    {&lt;br /&gt;
        $playable_cards = $this-&amp;gt;game-&amp;gt;getPlayableCards($playerId);&lt;br /&gt;
        $zombieChoice = $this-&amp;gt;getRandomZombieChoice($playable_cards); // random choice over possible moves&lt;br /&gt;
        return $this-&amp;gt;actPlayCard((int)$zombieChoice, $playerId);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And we can significantly simplify our rule check at actPlayCard&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $game = $this-&amp;gt;game;&lt;br /&gt;
        $currentCard = $game-&amp;gt;cards-&amp;gt;getCard($cardId);&lt;br /&gt;
        if (!$currentCard) {&lt;br /&gt;
            throw new \BgaSystemException(&amp;quot;Invalid move&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        // Rule checks&lt;br /&gt;
        $playable_cards = $game-&amp;gt;getPlayableCards($activePlayerId);&lt;br /&gt;
        if (!in_array($cardId, $playable_cards)) {&lt;br /&gt;
            throw new \BgaUserException(clienttranslate(&amp;quot;You cannot play this card now&amp;quot;));&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Finally we can send this to the client via state args so the user can see what moves are valid:&lt;br /&gt;
In &#039;&#039;&#039;PlayerTurn.php&#039;&#039;&#039; add this method:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function getArgs(int $activePlayerId): array&lt;br /&gt;
    {&lt;br /&gt;
        // Send playable card ids of the active player privately&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;_private&#039; =&amp;gt; [&lt;br /&gt;
                $activePlayerId =&amp;gt; [&lt;br /&gt;
                    &#039;playableCards&#039; =&amp;gt; $this-&amp;gt;game-&amp;gt;getPlayableCards($activePlayerId)&lt;br /&gt;
                ],&lt;br /&gt;
            ],&lt;br /&gt;
        ];&lt;br /&gt;
    }    &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On the client side in the .js file replace the onEnteringState method with this code (and ALSO remove the this.handStock.setSelectionMode(&amp;quot;single&amp;quot;) line from the setup method. If you leave it there it won&#039;t work as this is async function that collide with our other async&lt;br /&gt;
functions that we are using in the onEnteringState):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    onEnteringState(args, isCurrentPlayerActive) {&lt;br /&gt;
      console.log(&amp;quot;Entering state: &amp;quot; + this.bga.states.currentStateName, args);&lt;br /&gt;
&lt;br /&gt;
      this.bga.statusBar.setTitle(&lt;br /&gt;
        isCurrentPlayerActive&lt;br /&gt;
          ? _(&amp;quot;${you} must play a card&amp;quot;)&lt;br /&gt;
          : _(&amp;quot;${actplayer} must play a card&amp;quot;),&lt;br /&gt;
      );&lt;br /&gt;
&lt;br /&gt;
      switch (stateName) {&lt;br /&gt;
        case &amp;quot;PlayerTurn&amp;quot;:&lt;br /&gt;
          if (isCurrentPlayerActive) {&lt;br /&gt;
            const playableCardsIds = args.playableCardsIds; // returned by the PlayerTurn::getArgs&lt;br /&gt;
            const allCards = this.game.handStock.getCards();&lt;br /&gt;
            const playableCards = allCards.filter(&lt;br /&gt;
              (card) =&amp;gt; playableCardIds.includes(parseInt(card.id)) // never know if we get int or string, this method cares&lt;br /&gt;
            );&lt;br /&gt;
            this.game.handStock.setSelectionMode(&amp;quot;single&amp;quot;, playableCards);&lt;br /&gt;
          }&lt;br /&gt;
          break;&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
&amp;lt;/pre&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
==Fix first player with 2 of clubs==&lt;br /&gt;
&lt;br /&gt;
Find the comment &lt;br /&gt;
 // FIXME: first player one with 2 of clubs&lt;br /&gt;
and replace with this code:&lt;br /&gt;
 // first player one with 2 of clubs&lt;br /&gt;
 $first_player = $this-&amp;gt;game-&amp;gt;getUniqueValueFromDb(&amp;quot;SELECT card_location_arg FROM card WHERE card_location = &#039;hand&#039; AND card_type = 3 AND card_type_arg = 2&amp;quot;);// 2 of clubs&lt;br /&gt;
&lt;br /&gt;
 Then we can uncomment our code for getPlayableCards in Game.php:&lt;br /&gt;
        //Check whether the first card of the hand has been played or not&lt;br /&gt;
        $total_played = $this-&amp;gt;cards-&amp;gt;countCardInLocation(&#039;cardswon&#039;) + $this-&amp;gt;cards-&amp;gt;countCardInLocation(&#039;cardsontable&#039;);&lt;br /&gt;
        if ($total_played == 0) {&lt;br /&gt;
            // No cards have been played yet, find and return the starter card only&lt;br /&gt;
            foreach ($hand as $card) if ($card[&#039;type&#039;] == 3 &amp;amp;&amp;amp; $card[&#039;type_arg&#039;] == 2) return [$card[&#039;id&#039;]]; // 2 of clubs&lt;br /&gt;
            return $all_ids; // should not happen&lt;br /&gt;
        } else&lt;br /&gt;
&lt;br /&gt;
==Spectator support==&lt;br /&gt;
A spectator is not a real player but they can watch the game. Most games will require special spectator support, it&#039;s one of the steps in the alpha testing checklist.&lt;br /&gt;
In this game it&#039;s pretty simple, we just hide the hand control in the client&lt;br /&gt;
&lt;br /&gt;
In the .js file in the setup function add this code (after DOM is created):&lt;br /&gt;
      // Hide hand zone from spectators&lt;br /&gt;
      if (this.isSpectator)&lt;br /&gt;
        document.getElementById(&amp;quot;myhand_wrap&amp;quot;).style.display = &amp;quot;none&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
Click Test Spectator at the end of player&#039;s panels to test this.&lt;br /&gt;
&lt;br /&gt;
==Improve UI==&lt;br /&gt;
We need to fix a few things in the UI still.&lt;br /&gt;
&lt;br /&gt;
===Center Player Areas===&lt;br /&gt;
First let&#039;s fix the player tables - to make them centered.&lt;br /&gt;
In the .css file find #player-tables and change it to this:&lt;br /&gt;
&lt;br /&gt;
 #player-tables {&lt;br /&gt;
  position: relative;&lt;br /&gt;
  width: calc(var(--h-tableau-width) * 3.8);&lt;br /&gt;
  height: calc(var(--h-tableau-height) * 2.4);&lt;br /&gt;
  margin: auto; // that is a cheap way to make it centered&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Better Card Play Animations===&lt;br /&gt;
When another player plays a card it kind of just appears on the tableau, we want to make it look like it&#039;s coming from the player hand.&lt;br /&gt;
We don&#039;t actually have any sort of UI location to have a player hand - but we can either put it on the mini player panel or add it to the bottom of the player areas.&lt;br /&gt;
Let&#039;s try to put this on the mini player panels.&lt;br /&gt;
First we need to add a node in the DOM on the player panel and maybe add an icon to represent the hand.&lt;br /&gt;
We have access to some BGA icons and font awesome icons https://fontawesome.com/v4/icons, so we can pick one from there:&lt;br /&gt;
&lt;br /&gt;
In the .js file in the template for player tableau and add this at the end of the forEach body:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
         document.getElementById(`player_panel_content_${player.color}`).innerHTML = &lt;br /&gt;
         `&amp;lt;div id=&amp;quot;otherhand_${player.id}&amp;quot; class=&amp;quot;otherhand&amp;quot;&amp;gt;&amp;lt;i class=&amp;quot;fa fa-window-restore&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;&amp;lt;/div&amp;gt;`;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
In the .js file replace the notif handler for play with this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    async notif_playCard(args) {&lt;br /&gt;
      // Play a card on the table&lt;br /&gt;
      const playerId = args.player_id;&lt;br /&gt;
      let settings = {};&lt;br /&gt;
      if (playerId != this.player_id) {&lt;br /&gt;
        settings = {&lt;br /&gt;
          fromElement: $(`otherhand_${playerId}`),&lt;br /&gt;
          toPlaceholder: &amp;quot;grow&amp;quot;,&lt;br /&gt;
        };&lt;br /&gt;
      }&lt;br /&gt;
      await this.tableauStocks[playerId].addCard(args.card, settings);&lt;br /&gt;
    },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
What we did here is added a settings parameter for card placement - for cases where it&#039;s not our own card to move it from the &amp;quot;hand&amp;quot; area on the mini player board.&lt;br /&gt;
Reload and test (use the autoPlay feature to see the animation when the &amp;quot;other&amp;quot; player plays the card).&lt;br /&gt;
&lt;br /&gt;
Now we can also replace the void stock we create with animation to the same &amp;quot;otherhand&amp;quot; area:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    async notif_giveAllCardsToPlayer(args) {&lt;br /&gt;
      // Move all cards from notification to dedicated player area and fade out&lt;br /&gt;
      const playerId = args.player_id;&lt;br /&gt;
&lt;br /&gt;
      const cards = Array.from(Object.values(args.cards));&lt;br /&gt;
      await this.tableauStocks[playerId].addCards(cards);&lt;br /&gt;
      await this.tableauStocks[playerId].removeCards(cards, {&lt;br /&gt;
        fadeOut: true,&lt;br /&gt;
        slideTo: $(`otherhand_${playerId}`),&lt;br /&gt;
      });&lt;br /&gt;
    },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
And in this case we don&#039;t really need VoidStock anymore, we can remove it&lt;br /&gt;
Delete this&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // add void stock&lt;br /&gt;
        new BgaCards.VoidStock(&lt;br /&gt;
          this.cardsManager,&lt;br /&gt;
          document.getElementById(`cardswon_${playerId}`),&lt;br /&gt;
          {&lt;br /&gt;
            fadeOut: true, // not working&lt;br /&gt;
            toPlaceholder: &amp;quot;shrink&amp;quot;, // not working&lt;br /&gt;
            autoPlace: (card) =&amp;gt;&lt;br /&gt;
              card.location === &amp;quot;cardswon&amp;quot; &amp;amp;&amp;amp; card.location_arg == playerId,&lt;br /&gt;
          }&lt;br /&gt;
        );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Also can delete related css and DOM element cardswon.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We can also add this in .css to make this symbol centered:&lt;br /&gt;
 .otherhand {&lt;br /&gt;
   position: relative;&lt;br /&gt;
   margin: auto;&lt;br /&gt;
   text-align: center;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Card Sorting===&lt;br /&gt;
It would be nice to sort the cards in hand by suit and value.&lt;br /&gt;
You can add sorting&lt;br /&gt;
      // create the stock, in the game setup&lt;br /&gt;
      this.handStock = new BgaCards.HandStock(&lt;br /&gt;
        this.cardsManager,&lt;br /&gt;
        document.getElementById(&amp;quot;myhand&amp;quot;),&lt;br /&gt;
        {&lt;br /&gt;
             sort: BgaCards.sort(&#039;type&#039;, &#039;type_arg&#039;), // sort by suite then by value&lt;br /&gt;
        }&lt;br /&gt;
      ); &lt;br /&gt;
&lt;br /&gt;
but you will notice its not sorted right.&lt;br /&gt;
Its because of type mismatch. The server sends us strings and bga-cards expects integers.&lt;br /&gt;
We have to change this on client or server. I was already doing some ugly convertion on client, so lets just make it official.&lt;br /&gt;
We will add 2 functions in utility section that will do the convertions for us:&lt;br /&gt;
    ///////////////////////////////////////////////////&lt;br /&gt;
    //// Utility methods&lt;br /&gt;
&lt;br /&gt;
    remapToBgaCardList: function (cards) {&lt;br /&gt;
      if (!cards) return [];&lt;br /&gt;
      if (cards.type) {&lt;br /&gt;
        // actually one card&lt;br /&gt;
        return [this.remapToBgaCard(cards)];&lt;br /&gt;
      } else if (Array.isArray(cards)) {&lt;br /&gt;
        return cards.map((card) =&amp;gt; this.remapToBgaCard(card));&lt;br /&gt;
      } else {&lt;br /&gt;
        return Object.values(cards).map((card) =&amp;gt; this.remapToBgaCard(card));&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
    remapToBgaCard: function (card) {&lt;br /&gt;
      // proper casts&lt;br /&gt;
      return {&lt;br /&gt;
        id: parseInt(card.id),&lt;br /&gt;
        type: parseInt(card.type),&lt;br /&gt;
        type_arg: parseInt(card.type_arg),&lt;br /&gt;
        location: card.location,&lt;br /&gt;
        location_arg: parseInt(card.location_arg),&lt;br /&gt;
      };&lt;br /&gt;
    },&lt;br /&gt;
Then in setup change addCards to this&lt;br /&gt;
    this.handStock.addCards(this.remapToBgaCardList(this.gamedatas.hand));&lt;br /&gt;
and&lt;br /&gt;
      // Cards played on table&lt;br /&gt;
      for (i in this.gamedatas.cardsontable) {&lt;br /&gt;
        var card = this.gamedatas.cardsontable[i];&lt;br /&gt;
        var player_id = card.location_arg;&lt;br /&gt;
        this.tableauStocks[player_id].addCard(this.remapToBgaCard(card));&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
In notif_newHand replace addCards like this:&lt;br /&gt;
    this.handStock.addCards(this.remapToBgaCardList(args.hand));&lt;br /&gt;
&lt;br /&gt;
In notif_giveAllCardsToPlayer&lt;br /&gt;
      const cards = this.remapToBgaCardList(args.cards);&lt;br /&gt;
In notif_playCard&lt;br /&gt;
      await this.tableauStocks[playerId].addCard(this.remapToBgaCard(args.card), settings);&lt;br /&gt;
&lt;br /&gt;
Now sorting should work!&lt;br /&gt;
&lt;br /&gt;
An alternative the above is to not change card types and sub types but rather to sort the cards using something like this&lt;br /&gt;
   sortCards(cards) {&lt;br /&gt;
     return Array.from(cards).sort(function (a, b) {&lt;br /&gt;
       // sort by suit then rank&lt;br /&gt;
       // sort by suit then rank&lt;br /&gt;
       if (a.type === b.type) {&lt;br /&gt;
         // sub sort by rank&lt;br /&gt;
         return parseInt(a.type_arg) - parseInt(b.type_arg);&lt;br /&gt;
       } else {&lt;br /&gt;
         // sort by suit&lt;br /&gt;
         return parseInt(a.type) - parseInt(b.type);&lt;br /&gt;
       }&lt;br /&gt;
     });&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
===Tooltips===&lt;br /&gt;
We can add tooltips to cards to show their name.&lt;br /&gt;
In the .js file find where created silly tooltip with addTooltipHtml and replace with this:&lt;br /&gt;
          this.bga.gameui.addTooltipHtml(div.id, &lt;br /&gt;
             _(this.gamedatas.card_types.types[card.type_arg].name)+ &amp;quot; &amp;quot; +&lt;br /&gt;
             _(this.gamedatas.card_types.suites[card.type].name) &lt;br /&gt;
          );&lt;br /&gt;
Now what is this.gamedatas.card_types? Well that is our &amp;quot;material&amp;quot; of the game which is in our case variable in php, we have to send it to client for this to work.&lt;br /&gt;
Since it never changes we send it in getAllDatas method, add this at the end before return:&lt;br /&gt;
            $result[&#039;card_types&#039;] = $this-&amp;gt;card_types;&lt;br /&gt;
&lt;br /&gt;
Of course this is very basic tooltips and not even needed in this game, but in real game your want tooltips everywhere!!!&lt;br /&gt;
Lets add tooltip to our fake hand symbol also (the &amp;quot;otherhand&amp;quot;) (in setup method in .js somewhere in forEach loop over players)&lt;br /&gt;
        // add tooltips to player hand symbol&lt;br /&gt;
        this.bga.gameui.addTooltipHtml(&lt;br /&gt;
          `otherhand_${playerId}`,&lt;br /&gt;
          _(&amp;quot;Placeholder for player&#039;s hand&amp;quot;)&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
==Game progresstion==&lt;br /&gt;
In this game it should be easy, we just need to know if somebody close to -100 points!&lt;br /&gt;
Find getGameProgression in Game.php and replace with this:&lt;br /&gt;
    public function getGameProgression()&lt;br /&gt;
    {&lt;br /&gt;
        $min = $this-&amp;gt;playerScore-&amp;gt;getMin();&lt;br /&gt;
        return -1 * $min; // we get close to -100 we get close to 100% game completion&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Additional stuff==&lt;br /&gt;
&lt;br /&gt;
The following things were not implemented and you can add them yourself by looking at the code of the original hearts game:&lt;br /&gt;
&lt;br /&gt;
*Mark player who started the hand and add log about what is starting Suite of the trick&lt;br /&gt;
*Start scoring with 100 points each and end when &amp;lt;= 0&lt;br /&gt;
*Fix scoring rules with Q of spades and 26 point reverse scoring&lt;br /&gt;
*Add statistics&lt;br /&gt;
*Add card exchange states&lt;br /&gt;
*Add game option to start with 75 points instead of 100&lt;br /&gt;
&lt;br /&gt;
==After the tutorial==&lt;br /&gt;
You might want to check another tutorial, or start working on your first real project!&lt;br /&gt;
&lt;br /&gt;
[[Create a game in BGA Studio: Complete Walkthrough]]&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>RichardSPeters</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Tutorial_hearts&amp;diff=28972</id>
		<title>Tutorial hearts</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Tutorial_hearts&amp;diff=28972"/>
		<updated>2026-03-16T22:48:02Z</updated>

		<summary type="html">&lt;p&gt;RichardSPeters: Small changes to bring tutorial in line with current framework to make following the turorial easier&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Using this tutorial, you can build a complete working game on the BGA environment: Hearts.&lt;br /&gt;
&lt;br /&gt;
Before you read this tutorial, you must:&lt;br /&gt;
* Read the overall presentations of the BGA Framework ([[Studio|see here]]).&lt;br /&gt;
* Some-what know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript&lt;br /&gt;
* Set up your development environment [http://en.doc.boardgamearena.com/First_steps_with_BGA_Studio First Steps with BGA Studio]&lt;br /&gt;
* As part of setup you have to have access to your ftp home folder in studio, which would have the full &#039;hearts&#039; game source code. We will be using some resources of this game in this tutorial, so copy it over to local disk if you have not done so.&lt;br /&gt;
&lt;br /&gt;
If you are stuck or have question about this tutorial, post on [https://forum.boardgamearena.com/viewforum.php?f=12 BGA Developers forum]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Hearts Rules ==&lt;br /&gt;
&lt;br /&gt;
Hearts is a trick-taking card game for four players where the goal is to score the fewest points. &lt;br /&gt;
Players aim to avoid taking tricks with heart cards (1 point each) and the Queen of Spades (13 points). &lt;br /&gt;
Each round, 13 cards are dealt, players pass three cards, and the player with the 2 of Clubs starts the first trick. &lt;br /&gt;
Play continues clockwise, with players needing to follow suit if they can, and the highest card of the lead suit wins the trick. &lt;br /&gt;
Hearts cannot be played until they are &amp;quot;broken&amp;quot; by a player who can&#039;t follow suit and discards a heart, or by a player leading with a heart after they&#039;ve been broken. &lt;br /&gt;
&lt;br /&gt;
== Create your first game ==&lt;br /&gt;
&lt;br /&gt;
If you have not already, you have to create a project in BGA Studio. For this tutorial you can create a project heartsYOURNAME where&lt;br /&gt;
YOURNAME is your developer login name (or shorter version of thereof). You can also re-use the project you have created for the &amp;quot;First Steps&amp;quot; tutorial above.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;&lt;br /&gt;
Note: please do &#039;&#039;&#039;not&#039;&#039;&#039; use the hearts project code as a base. This tutorial assumes you started with a TEMPLATE project with no prior modifications. &lt;br /&gt;
Using the hearts project as a base will be very confusing and you won&#039;t be able to follow all the steps. Also it will not match exactly with this tutorial for different reasons.&lt;br /&gt;
&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
With the initial skeleton of code provided, you can already start a game from the BGA Studio. &lt;br /&gt;
&lt;br /&gt;
1. Find and express start the game in turn-based mode with 4 players. Make sure it works. If you want to see the game as 2nd player press red arrow button on the player panel to switch to that player. More details can be found in [[First_steps_with_BGA_Studio]]&lt;br /&gt;
&lt;br /&gt;
2. Modify the text in .js file (for example replace &amp;quot;Player zone content goes here&amp;quot; to &amp;quot;Hello&amp;quot;), reload the page in the browser and make sure your ftp sync works as expected.&lt;br /&gt;
Note: if you have not setup auto-sync do it now, manually copying files is a no-starter.&lt;br /&gt;
&lt;br /&gt;
3. Express stop from settings menu (the gear icon).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Attention!!!&amp;lt;/b&amp;gt; Very important note about reloading, if you don&#039;t remember this you may spend hours debugging. The browser caches images. If you change any of these files, you have to do &amp;quot;full reload&amp;quot; which is usually Ctrl+F5 (or Ctrl+reload button on browser) not just a regular reload.&lt;br /&gt;
&lt;br /&gt;
== Hook version control system ==&lt;br /&gt;
&lt;br /&gt;
For a real game, or even for this tutorial, we recommend committing the code to version control right from the start. You are going to find yourself in a situation where the game doesn&#039;t even start anymore and no way of debugging it, unless you have a way to revert. That is where version control becomes very handy. If you are not familiar with version control (e.g. [https://git-scm.com/docs/gittutorial git]) then at least back up your files after each major change. Start now.&lt;br /&gt;
&lt;br /&gt;
Code for this tutorial available is on github: https://github.com/elaskavaia/bga-heartsla&lt;br /&gt;
&lt;br /&gt;
Different revisions represent different steps along the process, starting from original template to a PARTIAL game.&lt;br /&gt;
&lt;br /&gt;
Note: the game was re-written using new template, the old code is in &amp;quot;oldframework&amp;quot; branch. The new template is in main branch.&lt;br /&gt;
&lt;br /&gt;
The real hearts game (that you can play on BGA) can be found in your FTP home folder, after getting read-only access, go to https://studio.boardgamearena.com/projects, select Already Published and find Hearts to get access&lt;br /&gt;
(It may not match this tutorial as framework diverged since this game was created and it may not have been updated)&lt;br /&gt;
&lt;br /&gt;
== Update game infos and box graphics ==&lt;br /&gt;
&lt;br /&gt;
Even it does nothing yet, always start by making sure the game looks decent in the game selector, meaning it has nice box graphics and its information is correct. For that we need to edit [[Game_meta-information: gameinfos.inc.php|gameinfos.inc.php]].&lt;br /&gt;
&lt;br /&gt;
For a real game, you would go to [http://boardgamegeek.com BoardGameGeek], find the game, and use the information from BGG to fill in the gameinfos.&lt;br /&gt;
&lt;br /&gt;
So let&#039;s do that. Find &amp;quot;hearts&amp;quot; on BoardGameGeek. (Hint: Original release 1850 :))&lt;br /&gt;
&lt;br /&gt;
You can fill in the year of publishing and bgg id, put &#039;&#039;Public Domain&#039;&#039; under publisher (for a real game, leave an empty string so it won&#039;t be displayed), and a publisher id of 171 for public domain. And as designer and author you can just put your own name just for fun. Set number of players to 4.&lt;br /&gt;
&lt;br /&gt;
  // Game publisher&lt;br /&gt;
    &#039;publisher&#039; =&amp;gt; &#039;Public Domain&#039;,&lt;br /&gt;
&lt;br /&gt;
  // Board Game Geek ID of the publisher&lt;br /&gt;
    &#039;publisher_bgg_id&#039; =&amp;gt; 171,&lt;br /&gt;
&lt;br /&gt;
  // Players configuration that can be played (ex: 2 to 4 players)&lt;br /&gt;
  &#039;players&#039; =&amp;gt; array( 4 ),  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important step&#039;&#039;&#039;: you have to refresh the information in the Studio website through the control panel. So go to Control Panel -&amp;gt; Manage Games -&amp;gt; heartsYOURNAME&lt;br /&gt;
and press Reload for &#039;Reload game informations&#039;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The next step would be to replace game box with nicer images. This can be done from the [[Game metadata manager]]. &lt;br /&gt;
&lt;br /&gt;
Now try to start the game again. If you somehow introduced a syntax error in the gameinfos file it may not work (the game won&#039;t start).&lt;br /&gt;
Always use the &amp;quot;Express Start&amp;quot; button to start the game. You should see a standard state prompt from the template. You should see 4 players on the right: testdude0 .. testdude3.&lt;br /&gt;
To switch between them press the red arrow button near their names, it will open another tab. This way you don&#039;t need to login and logout from multiple accounts!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Note: if you had run the game before with less than 4 players there is a bug that will prevent you from running it with 4 only (if you did not run it before or run it with 4 players as instructed stop reading this note), to workaround revert back to original players array (i.e. 1,2,3,4), reload game options, then create a table with 4 players, exit that game table, then change gameoptions to 4 only as above, reload game options, create table again.&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Layout and Graphics ==&lt;br /&gt;
&lt;br /&gt;
In this section we will do graphics of the game, and main layout of the game.&lt;br /&gt;
&lt;br /&gt;
First copy a sprite with cards image from [https://x.boardgamearena.net/data/others/cards/FULLREZ_CARDS_ORIGINAL_NORMAL.jpg]  into img/cards.jpg folder of your project. &lt;br /&gt;
&lt;br /&gt;
Details about images can be found here: [[Game art: img directory]]. If you did not setup auto-sync of files, sync the graphics manually with remote folder (re-sync with your workspace).&lt;br /&gt;
&lt;br /&gt;
Edit .js to add some divs to represent player table and hand area, at the beginning of the setup function&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  setup(gamedatas) {&lt;br /&gt;
    console.log(&amp;quot;Starting game setup&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    this.bga.gameArea.getElement().insertAdjacentHTML(&lt;br /&gt;
      &amp;quot;beforeend&amp;quot;,&lt;br /&gt;
      `&lt;br /&gt;
                &amp;lt;div id=&amp;quot;myhand_wrap&amp;quot; class=&amp;quot;whiteblock&amp;quot;&amp;gt;&lt;br /&gt;
                    &amp;lt;b id=&amp;quot;myhand_label&amp;quot;&amp;gt;${_(&amp;quot;My hand&amp;quot;)}&amp;lt;/b&amp;gt;&lt;br /&gt;
                        &amp;lt;div id=&amp;quot;myhand&amp;quot;&amp;gt;&lt;br /&gt;
                        &amp;lt;/div&amp;gt;&lt;br /&gt;
                    &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            `,&lt;br /&gt;
    );&lt;br /&gt;
    // ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you refresh you should see now white area with My Hand title.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Heartsla-tpl2.png]]&lt;br /&gt;
&lt;br /&gt;
Now lets add a card into the hand, just so you can feel it. Edit the html snippet we inserted earlier buy adding a line representing a card&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    &amp;lt;div id=&amp;quot;myhand&amp;quot;&amp;gt;&lt;br /&gt;
       &amp;lt;div class=&amp;quot;fakecard&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Edit .css file, add this code (.css file is empty now, only has comments, just tuck this at the end)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.fakecard {&lt;br /&gt;
    display: inline-block;&lt;br /&gt;
    position: relative;&lt;br /&gt;
    margin-top: 5px;&lt;br /&gt;
    border-radius: 5%;&lt;br /&gt;
    width: 100px;&lt;br /&gt;
    height: 135px;&lt;br /&gt;
    background-size: calc(100px * 15);&lt;br /&gt;
    background-image: url(&#039;img/cards.jpg&#039;); /* temp hack to see it */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When you change existing graphics files remember that you have to FORCE-reload page, i.e. Ctrl-F5, otherwise its cached.&lt;br /&gt;
&lt;br /&gt;
You should see this (more less):&lt;br /&gt;
&lt;br /&gt;
[[File:Heartsla-tpl3.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Note: If you don&#039;t see the card a) check it was synced to remote folder b) force reload page&lt;br /&gt;
&lt;br /&gt;
Awesome! Now lets do the rest of layout.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Let&#039;s complete the game template. You template should have this code, just leave it there&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      // Example to add a div on the game area&lt;br /&gt;
      this.bga.gameArea.getElement().insertAdjacentHTML(&amp;quot;beforeend&amp;quot;,&lt;br /&gt;
                            &amp;lt;div id=&amp;quot;player-tables&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
                        `&lt;br /&gt;
      );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then change the code following comment &amp;quot;// Setting up player boards&amp;quot; with this&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      // Setting up player boards&lt;br /&gt;
      const numPlayers = Object.keys(gamedatas.players).length;&lt;br /&gt;
      Object.values(gamedatas.players).forEach((player, index) =&amp;gt; {&lt;br /&gt;
        document.getElementById(&amp;quot;player-tables&amp;quot;).insertAdjacentHTML(&lt;br /&gt;
          &amp;quot;beforeend&amp;quot;,&lt;br /&gt;
          // we generate this html snippet for each player&lt;br /&gt;
          `&lt;br /&gt;
    &amp;lt;div class=&amp;quot;playertable whiteblock playertable_${DIRECTIONS[index]}&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;div class=&amp;quot;playertablename&amp;quot; style=&amp;quot;color:#${player.color};&amp;quot;&amp;gt;${player.name}&amp;lt;/div&amp;gt;&lt;br /&gt;
        &amp;lt;div id=&amp;quot;tableau_${player.id}&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
    `&lt;br /&gt;
        );&lt;br /&gt;
      });&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
What we did is we added a template for every players at the table.&lt;br /&gt;
Now try to reload you game.&lt;br /&gt;
Oops! it won&#039;t load. This is to teach you how it will look like when you have syntax error in your js file. The game will hang loading at 10% or so. How to know what happened?&lt;br /&gt;
Open dev tools in browser (usually F12) and navigate to Console tab. You will see a stack trace of where error is. In our case&lt;br /&gt;
  HeartsFIXME.js:68 Uncaught (in promise) ReferenceError: DIRECTIONS is not defined&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In real hearts game they use this direction array to map every player to direction (like North) but its not needed, we can just use player index. &lt;br /&gt;
Lets just replace DIRECTIONS[index] with index, i.e&lt;br /&gt;
  &amp;lt;div class=&amp;quot;playertable whiteblock playertable_${index}&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now delete the following section as we won&#039;t be using it&amp;lt;pre&amp;gt;&lt;br /&gt;
      // Add test action buttons in the action status bar, simulating a card click:&lt;br /&gt;
      playableCardsIds.forEach((cardId) =&amp;gt;&lt;br /&gt;
        this.bga.statusBar.addActionButton(&lt;br /&gt;
          _(&amp;quot;Play card with id ${card_id}&amp;quot;).replace(&amp;quot;${card_id}&amp;quot;, cardId),&lt;br /&gt;
          () =&amp;gt; this.onCardClick(cardId),&lt;br /&gt;
        ),&lt;br /&gt;
      );&lt;br /&gt;
&lt;br /&gt;
      this.bga.statusBar.addActionButton(&lt;br /&gt;
        _(&amp;quot;Pass&amp;quot;),&lt;br /&gt;
        () =&amp;gt; this.bga.actions.performAction(&amp;quot;actPass&amp;quot;),&lt;br /&gt;
        { color: &amp;quot;secondary&amp;quot; },&lt;br /&gt;
      );&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Reload. If everything went well you should see this:&lt;br /&gt;
&lt;br /&gt;
[[File:Heartsla-tpl4.png|alt=Display player space of all players]]&lt;br /&gt;
&lt;br /&gt;
These are &amp;quot;tableau&amp;quot; areas for 4 players plus My hand visible only to one player. They are not exactly how we wanted them to be because we did not edit .css yet.&lt;br /&gt;
&lt;br /&gt;
Now edit .css, add these lines after import before our previous definition&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
:root {&lt;br /&gt;
  --h-card-width: 100px;&lt;br /&gt;
  --h-card-height: 135px;&lt;br /&gt;
  --h-tableau-width: 220px;&lt;br /&gt;
  --h-tableau-height: 180px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#player-tables {&lt;br /&gt;
  position: relative;&lt;br /&gt;
  width: calc(var(--h-tableau-width) * 3.9);&lt;br /&gt;
  height: calc(var(--h-tableau-height) * 2.4);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.playertablename {&lt;br /&gt;
  font-weight: bold;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.playertable {&lt;br /&gt;
  position: absolute;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  width: var(--h-tableau-width);&lt;br /&gt;
  height: var(--h-tableau-height);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.playertable_0 {&lt;br /&gt;
  top: 0px;&lt;br /&gt;
  left: 50%;&lt;br /&gt;
  margin-left: calc(var(--h-tableau-width) / 2 * -1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.playertable_1 {&lt;br /&gt;
  left: 0px;&lt;br /&gt;
  top: 50%;&lt;br /&gt;
  margin-top: calc(var(--h-tableau-height) / 2 * -1);&lt;br /&gt;
}&lt;br /&gt;
.playertable_2 {&lt;br /&gt;
  right: 0px;&lt;br /&gt;
  top: 50%;&lt;br /&gt;
  margin-top: calc(var(--h-tableau-height) / 2 * -1);&lt;br /&gt;
}&lt;br /&gt;
.playertable_3 {&lt;br /&gt;
  bottom: 0px;&lt;br /&gt;
  left: 50%;&lt;br /&gt;
  margin-left: calc(var(--h-tableau-width) / 2 * -1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and delete the following section from game.js as we won&#039;t be using it&amp;lt;pre&amp;gt;&lt;br /&gt;
      // example of adding a div for each player&lt;br /&gt;
      document.getElementById(&amp;quot;player-tables&amp;quot;).insertAdjacentHTML(&lt;br /&gt;
        &amp;quot;beforeend&amp;quot;,&lt;br /&gt;
        `&lt;br /&gt;
                &amp;lt;div id=&amp;quot;player-table-${player.id}&amp;quot;&amp;gt;&lt;br /&gt;
                    &amp;lt;strong&amp;gt;${player.name}&amp;lt;/strong&amp;gt;&lt;br /&gt;
                    &amp;lt;div&amp;gt;Player zone content goes here&amp;lt;/div&amp;gt;&lt;br /&gt;
                &amp;lt;/div&amp;gt;&lt;br /&gt;
            `,&lt;br /&gt;
      );&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now you force Reload and you should see this:&lt;br /&gt;
&lt;br /&gt;
[[File:Heartsla-tpl5.png]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Note: if you did not see changes you may have not force reloaded, force means you use Ctrl+F5 or Ctrl+Shift-R, if you don&#039;t &amp;quot;force&amp;quot; browser will use cached version of images! Which is not what you just changed&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here is some explanations about CSS (if you know everything about css already skip this):&lt;br /&gt;
* At top we defined some variables for sizes of cards and player &amp;quot;mats&amp;quot; (which we call tableau)&lt;br /&gt;
* We trying to layout mats in kind of diamond shape&lt;br /&gt;
* We define positions of our elements using top/bottom/left/right style property&lt;br /&gt;
* We used standard technique of centering the element which is use 50% for lets say &amp;quot;left&amp;quot;, and then shift by half of size of object to actually center it (margin-left). You can remove margins to see how it look if we did not do that&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Another Note: In general if you have auto-sync you don&#039;t need to reload if you change Game.php file, you need normal reload if you change js, and force reload for images. If you changed state machine or database you likely need to restart the game.&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Game Interface with BGA Cards ==&lt;br /&gt;
&lt;br /&gt;
The BGA framework provides a few out of the box classes to deal with cards. The client side&lt;br /&gt;
contains a component called [[BgaCards]] and it can be used for any dynamic html &amp;quot;pieces&amp;quot; management and animation. &lt;br /&gt;
On the server side we will use the [[Deck]] class which we discuss later.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you open cards.jpg in an image viewer you will see that it is a &amp;quot;sprite&amp;quot; image - a 15x4 grid of images stitched together,&lt;br /&gt;
which is a very efficient way to transport images. So we will use the card manager class to mark up these images and create&lt;br /&gt;
&amp;quot;card&amp;quot; divs for us and place them on the board.&lt;br /&gt;
&lt;br /&gt;
First, we need to add dependencies in the Game.js file:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const BgaAnimations = await importEsmLib(&#039;bga-animations&#039;, &#039;1.x&#039;);&lt;br /&gt;
const BgaCards = await importEsmLib(&#039;bga-cards&#039;, &#039;1.x&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we will remove the fake card we added (in Game.js file) search and remove: &amp;lt;pre&amp;gt;&lt;br /&gt;
    &amp;lt;div id=&amp;quot;myhand&amp;quot;&amp;gt;&lt;br /&gt;
       &amp;lt;div class=&amp;quot;fakecard&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then we will add initialization code of bga cards and related component in setup method after the template code (i.e. where we defined the myhand div as this div is referenced by the following code) and before setupNotifications &amp;lt;pre&amp;gt;&lt;br /&gt;
      // create the animation manager, and bind it to the `game.bgaAnimationsActive()` function&lt;br /&gt;
      this.animationManager = new BgaAnimations.Manager({&lt;br /&gt;
        animationsActive: () =&amp;gt; this.bga.gameui.bgaAnimationsActive(),&lt;br /&gt;
      });&lt;br /&gt;
&lt;br /&gt;
      const cardWidth = 100;&lt;br /&gt;
      const cardHeight = 135;&lt;br /&gt;
&lt;br /&gt;
      // create the card manager&lt;br /&gt;
      this.cardsManager = new BgaCards.Manager({&lt;br /&gt;
        animationManager: this.animationManager,&lt;br /&gt;
        type: &amp;quot;ha-card&amp;quot;, // the &amp;quot;type&amp;quot; of our cards in css&lt;br /&gt;
        getId: (card) =&amp;gt; card.id,&lt;br /&gt;
&lt;br /&gt;
        cardWidth: cardWidth,&lt;br /&gt;
        cardHeight: cardHeight,&lt;br /&gt;
        cardBorderRadius: &amp;quot;5%&amp;quot;,&lt;br /&gt;
        setupFrontDiv: (card, div) =&amp;gt; {&lt;br /&gt;
          div.dataset.type = card.type; // suit 1..4&lt;br /&gt;
          div.dataset.typeArg = card.type_arg; // value 2..14&lt;br /&gt;
          div.style.backgroundPositionX = `calc(100% / 14 * (${card.type_arg} - 2))`; // 14 is number of columns in stock image minus 1&lt;br /&gt;
          div.style.backgroundPositionY = `calc(100% / 3 * (${card.type} - 1))`; // 3 is number of rows in stock image minus 1&lt;br /&gt;
          this.bga.gameui.addTooltipHtml(div.id, `tooltip of ${card.type}`);&lt;br /&gt;
        },&lt;br /&gt;
      });&lt;br /&gt;
&lt;br /&gt;
      // create the stock, in the game setup&lt;br /&gt;
      this.handStock = new BgaCards.HandStock(&lt;br /&gt;
        this.cardsManager,&lt;br /&gt;
        document.getElementById(&amp;quot;myhand&amp;quot;)&lt;br /&gt;
      );&lt;br /&gt;
          // TODO: fix handStock&lt;br /&gt;
      this.handStock.addCards([&lt;br /&gt;
        { id: 1, type: 2, type_arg: 4 }, // 4 of hearts&lt;br /&gt;
        { id: 2, type: 3, type_arg: 11 }, // Jack of clubs&lt;br /&gt;
      ]); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Also we need to add this .css (anywhere), that will map front face of the card to our image (1500% is because this image 15 times bigger than single card on X axis)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.ha-card-front {&lt;br /&gt;
  background-size: 1500% auto;&lt;br /&gt;
  background-image: url(&amp;quot;img/cards.jpg&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Explanations:&lt;br /&gt;
* First we created animation manager which will be used later&lt;br /&gt;
* Then we define constant with width and height of our cards in pixes&lt;br /&gt;
* Then we create the cards manager. We tell it how to get unique id of each card (getId), and how to setup the div representing the front of the card (setupFrontDiv). In this function we set data attributes for type and type_arg which we will use later, and we set background position to show correct part of sprite image.&lt;br /&gt;
* Then we create a hand stock component which will represent player&#039;s hand. It is attached to div with id &amp;quot;myhand&amp;quot;.&lt;br /&gt;
* Finally we add two cards into the hand stock just for testing.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now if you reload you should see two cards in your hand:&lt;br /&gt;
&lt;br /&gt;
[[File:Heartsla-tpl6.png|alt=Display two cards in player&#039;s hand]]   &lt;br /&gt;
&lt;br /&gt;
Now we will add the &amp;quot;stock&amp;quot; object that will control player tableau (add in setup function before setupNotification)&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      // map stocks&lt;br /&gt;
&lt;br /&gt;
      this.tableauStocks = [];&lt;br /&gt;
      Object.values(gamedatas.players).forEach((player, index) =&amp;gt; {&lt;br /&gt;
        // add player tableau stock&lt;br /&gt;
        this.tableauStocks[player.id] = new BgaCards.LineStock(&lt;br /&gt;
          this.cardsManager,&lt;br /&gt;
          document.getElementById(`tableau_${player.id}`)&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
        // TODO: fix tableauStocks&lt;br /&gt;
        this.tableauStocks[player.id].addCards([&lt;br /&gt;
          { id: index + 10, type: index + 1, type_arg: index + 2 },&lt;br /&gt;
        ]);&lt;br /&gt;
      });&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Heartsla-tpl7.png]]   &lt;br /&gt;
&lt;br /&gt;
Explanations:&lt;br /&gt;
* We go over each player and create component called LineStock to represent player tableau, it will hold a single card&lt;br /&gt;
* We assign this into tableauStocks map indexed by player id to use later&lt;br /&gt;
* Finally we add a fake card into that stock just to see something&lt;br /&gt;
&lt;br /&gt;
Stock control can handle clicking on items and forms the selection. You can immediately react to selection&lt;br /&gt;
or you can query it later; for example when user presses some other button.&lt;br /&gt;
&lt;br /&gt;
Let&#039;s hook it up. Add this in the setup method in .js file, before // TODO: fix handStock:&lt;br /&gt;
&lt;br /&gt;
      this.handStock.setSelectionMode(&amp;quot;single&amp;quot;);&lt;br /&gt;
      this.handStock.onCardClick = (card) =&amp;gt; {&lt;br /&gt;
        alert(&amp;quot;boom!&amp;quot;);&lt;br /&gt;
      };&lt;br /&gt;
&lt;br /&gt;
Reload the game and click on one of the two Cards in your hand. You should get &amp;quot;boom&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
We will stop for now with client because we need to code some server stuff.&lt;br /&gt;
&lt;br /&gt;
== Game Database and Game Initialization ==&lt;br /&gt;
&lt;br /&gt;
Next step, you want to design a game database and setup a new game (on the server side).&lt;br /&gt;
For that we need to a) modify the database schema to add our cards data b) add some global variables into&lt;br /&gt;
the existing globals table.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
When you develop a game you need to figure out how you store your game pieces in database. This should be maximum 2 tables (like one for cards, items, tokens and meeples and one for counters).&lt;br /&gt;
In this game we will be using two tables, the default Deck table (supported by Deck component) and default &amp;quot;state variables&amp;quot; tables which called globals, to store some of integers.&lt;br /&gt;
In you never dealt with web servers - the database stores all information about your game and your php (server) code does not exists in memory between users actions.&lt;br /&gt;
&lt;br /&gt;
To modify the schema, first exit your existing game(s). Open &#039;&#039;&#039;dbmodel.sql&#039;&#039;&#039; file and uncomment the card table creation.&lt;br /&gt;
&lt;br /&gt;
This is the &amp;quot;card&amp;quot; table which will be managed by the Deck php class.&lt;br /&gt;
&lt;br /&gt;
This is how we map the database to our game:&lt;br /&gt;
* card_id: unique id of each card, it will be auto-generated&lt;br /&gt;
* card_type: it will be suite of the card 1 to 4 (Spades,Hearts,Clubs,Diamonds).&lt;br /&gt;
* card_type_arg: will be &amp;quot;value&amp;quot; of the card, 2 to 14 (2 is 2,...,10 is 10, 11 is Jack, ...)&lt;br /&gt;
* card_location: will be location of the card, like &amp;quot;deck&amp;quot;, &amp;quot;hand&amp;quot;, &amp;quot;tableau&amp;quot; etc&lt;br /&gt;
* card_location_arg: will be additional argument for location - the player id if card is in player&#039;s hand or tableau.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Game State Variables ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE TABLE IF NOT EXISTS `card` (&lt;br /&gt;
  `card_id` int(10) unsigned NOT NULL AUTO_INCREMENT,&lt;br /&gt;
  `card_type` varchar(16) NOT NULL,&lt;br /&gt;
  `card_type_arg` int(11) NOT NULL,&lt;br /&gt;
  `card_location` varchar(16) NOT NULL,&lt;br /&gt;
  `card_location_arg` int(11) NOT NULL,&lt;br /&gt;
  PRIMARY KEY (`card_id`)&lt;br /&gt;
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;&lt;br /&gt;
&amp;lt;/pre&amp;gt;Next we finally get into Game.php class (in modules/php subdir), where the main logic and db interaction would be. Find php constructor which should be &lt;br /&gt;
  function __construct( )&lt;br /&gt;
This is first function in a file. Add this code to constructor (replace existing initGameStateLabel if any).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function __construct()&lt;br /&gt;
    {&lt;br /&gt;
        parent::__construct();&lt;br /&gt;
        $this-&amp;gt;initGameStateLabels(&lt;br /&gt;
            [&lt;br /&gt;
                &amp;quot;trick_color&amp;quot; =&amp;gt; 11,&lt;br /&gt;
            ]&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;cards = $this-&amp;gt;deckFactory-&amp;gt;createDeck(&#039;card&#039;); // card is the our database name&lt;br /&gt;
        // ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you see errors in IDE its because we also have to declared &amp;quot;cards&amp;quot; as class member, add &#039;&#039;public Deck $cards;&#039;&#039; before the contructor.&lt;br /&gt;
Also if you using IDE it will suggest to import Deck class, accept it. If you are using &#039;vi&#039; just add this import where other imports (use in php) at the begging of the file after namespace declaration.&lt;br /&gt;
&lt;br /&gt;
  use Bga\GameFramework\Components\Deck;&lt;br /&gt;
&lt;br /&gt;
Here we are initializing three &amp;quot;Game State Variables&amp;quot; which are variables stored in the database. They are integers.&lt;br /&gt;
It must start with values higher or equal to 10 since values lower than 10 are reserved. These values are stored by numeric ids&lt;br /&gt;
in the database, but in the php we associate them with string labels for convenience of access. &lt;br /&gt;
&lt;br /&gt;
The variables are:&lt;br /&gt;
&lt;br /&gt;
*&amp;quot;trick_color&amp;quot;: numbers from 1 to 4 that map to card suit (not sure why it&#039;s called color; maybe it&#039;s a translation from French);&lt;br /&gt;
&lt;br /&gt;
The next 2 lines are creating $this-&amp;gt;cards object and associating it with &amp;quot;card&amp;quot; table in the the database.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;If we called db table &#039;foo&#039; instead of &#039;card&#039; the last statement would have been  $this-&amp;gt;cards-&amp;gt;createDeck( &amp;quot;foo&amp;quot; )&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: if you have some other leftovers from template like playerEnergy, leave it for now as is.&lt;br /&gt;
&lt;br /&gt;
Since we changed the database schema, we cannot re-use our existing game, we have to do express stop (from burger menu).&lt;br /&gt;
&lt;br /&gt;
Then start a new game and make sure it starts, then exit.&lt;br /&gt;
&amp;lt;i&amp;gt;&lt;br /&gt;
If you made a mistake&lt;br /&gt;
in the .sql or php constructor the game won&#039;t start, and good luck debugging it. (That is why it&#039;s important to check&lt;br /&gt;
once in a while to make sure it still starts while you remember what you have changed.)&lt;br /&gt;
&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you game won&#039;t even load and you want to stop it:&lt;br /&gt;
https://en.doc.boardgamearena.com/Tools_and_tips_of_BGA_Studio#Stopping_Hanging_Game&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Game Setup ===&lt;br /&gt;
Now we can go to game initialization &#039;&#039;&#039;setupNewGame&#039;&#039;&#039; in Game.php. This method is called only once when the game is created.&lt;br /&gt;
&lt;br /&gt;
In your template project you should have code that deals with player table, just leave it as is. Start inserting the&lt;br /&gt;
other code after &amp;quot;// Init global values with their initial values&amp;quot; comment.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// Init global values with their initial values&lt;br /&gt;
&lt;br /&gt;
// Set current trick color to zero (= no trick color)&lt;br /&gt;
$this-&amp;gt;setGameStateInitialValue(&#039;trick_color&#039;, 0);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we initialize all the globals to 0.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Next is to create our cards in the database. We have one deck of cards so it&#039;s pretty simple.&lt;br /&gt;
Insert this after  // TODO: Setup the initial game situation here.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // Create cards&lt;br /&gt;
        $cards = [];&lt;br /&gt;
        foreach ($this-&amp;gt;card_types[&amp;quot;suites&amp;quot;] as $suit =&amp;gt; $suit_info) {&lt;br /&gt;
            // spade, heart, diamond, club&lt;br /&gt;
            foreach ($this-&amp;gt;card_types[&amp;quot;types&amp;quot;] as $value =&amp;gt; $info_value) {&lt;br /&gt;
                //  2, 3, 4, ... K, A&lt;br /&gt;
                $cards[] = [&#039;type&#039; =&amp;gt; $suit, &#039;type_arg&#039; =&amp;gt; $value, &#039;nbr&#039; =&amp;gt; 1];&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        $this-&amp;gt;cards-&amp;gt;createCards($cards, &#039;deck&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code that will create one of each card. But don&#039;t run it yet, because we missing &#039;&#039;card_types&#039;&#039;.&lt;br /&gt;
So we have state of the game in the database, but there is some static game information which never changes.&lt;br /&gt;
This information should be stored in .php and this way it can be accessed from all .php files (and .js if you send it via getAllDatas()).&lt;br /&gt;
&lt;br /&gt;
Note: originally it was stored in material.inc.php file which is no longer part of default template, when you have a lot of material it makes sence to get it out of Game.php&lt;br /&gt;
&lt;br /&gt;
We will edit  Game.php now by adding these lines in constructor (and replacing &#039;&#039;self::$CARD_TYPES&#039;&#039;)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $this-&amp;gt;card_types = [&lt;br /&gt;
            &amp;quot;suites&amp;quot; =&amp;gt; [&lt;br /&gt;
                1 =&amp;gt; [&lt;br /&gt;
                    &#039;name&#039; =&amp;gt; clienttranslate(&#039;Spade&#039;),&lt;br /&gt;
                ],&lt;br /&gt;
                2 =&amp;gt; [&lt;br /&gt;
                    &#039;name&#039; =&amp;gt; clienttranslate(&#039;Heart&#039;),&lt;br /&gt;
                ],&lt;br /&gt;
                3 =&amp;gt; [&lt;br /&gt;
                    &#039;name&#039; =&amp;gt; clienttranslate(&#039;Club&#039;),&lt;br /&gt;
                ],&lt;br /&gt;
                4 =&amp;gt; [&lt;br /&gt;
                    &#039;name&#039; =&amp;gt; clienttranslate(&#039;Diamond&#039;),&lt;br /&gt;
                ]&lt;br /&gt;
            ],&lt;br /&gt;
            &amp;quot;types&amp;quot; =&amp;gt; [&lt;br /&gt;
                2 =&amp;gt; [&#039;name&#039; =&amp;gt; &#039;2&#039;],&lt;br /&gt;
                3 =&amp;gt; [&#039;name&#039; =&amp;gt; &#039;3&#039;],&lt;br /&gt;
                4 =&amp;gt; [&#039;name&#039; =&amp;gt; &#039;4&#039;],&lt;br /&gt;
                5 =&amp;gt; [&#039;name&#039; =&amp;gt; &#039;5&#039;],&lt;br /&gt;
                6 =&amp;gt; [&#039;name&#039; =&amp;gt; &#039;6&#039;],&lt;br /&gt;
                7 =&amp;gt; [&#039;name&#039; =&amp;gt; &#039;7&#039;],&lt;br /&gt;
                8 =&amp;gt; [&#039;name&#039; =&amp;gt; &#039;8&#039;],&lt;br /&gt;
                9 =&amp;gt; [&#039;name&#039; =&amp;gt; &#039;9&#039;],&lt;br /&gt;
                10 =&amp;gt; [&#039;name&#039; =&amp;gt; &#039;10&#039;],&lt;br /&gt;
                11 =&amp;gt; [&#039;name&#039; =&amp;gt; clienttranslate(&#039;J&#039;)],&lt;br /&gt;
                12 =&amp;gt; [&#039;name&#039; =&amp;gt; clienttranslate(&#039;Q&#039;)],&lt;br /&gt;
                13 =&amp;gt; [&#039;name&#039; =&amp;gt; clienttranslate(&#039;K&#039;)],&lt;br /&gt;
                14 =&amp;gt; [&#039;name&#039; =&amp;gt; clienttranslate(&#039;A&#039;)]&lt;br /&gt;
            ]&lt;br /&gt;
        ];&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
If you pass a value to the client via notification you should always use untranslated strings, and the client will translate it. Function &#039;clienttranslate&#039; marks the value for translation but does not actually change it for php. For more about this wonderful translation stuff see [[Translations]].&lt;br /&gt;
&lt;br /&gt;
Can also have declared this in the class (replace $CARD_TYPES)&lt;br /&gt;
 public array $card_types;&lt;br /&gt;
&lt;br /&gt;
Reload to make it still works (no errors).&lt;br /&gt;
&lt;br /&gt;
==== Dealing Cards ====&lt;br /&gt;
After we have initialized our deck, we want to deal 13 at random for each player. Add this after createCards in setupNewGame function in the Game.php file:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// Shuffle deck&lt;br /&gt;
$this-&amp;gt;cards-&amp;gt;shuffle(&#039;deck&#039;);&lt;br /&gt;
// Deal 13 cards to each players&lt;br /&gt;
$players = $this-&amp;gt;loadPlayersBasicInfos();&lt;br /&gt;
foreach ($players as $player_id =&amp;gt; $player) {&lt;br /&gt;
    $this-&amp;gt;cards-&amp;gt;pickCards(13, &#039;deck&#039;, $player_id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
In the next section we are going to learn how to show those cards to the right players, without exposing other player hands. &lt;br /&gt;
&lt;br /&gt;
==Full Game Model Synchronization==&lt;br /&gt;
&lt;br /&gt;
Now at any point in the game we need to make sure that database information can be reflected back in the UI, so we must fix the &#039;&#039;&#039;getAllDatas&#039;&#039;&#039; function&lt;br /&gt;
to return all possible data we need to reconstruct the game. This is in the Game.php file. &lt;br /&gt;
&lt;br /&gt;
=== Player&#039;s Hand ===&lt;br /&gt;
The template for getAllDatas() already takes care of player info. Let&#039;s just&lt;br /&gt;
add hand and tableau data before we return a result.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// Cards in player hand&lt;br /&gt;
$result[&#039;hand&#039;] = $this-&amp;gt;cards-&amp;gt;getCardsInLocation(&#039;hand&#039;, $currentPlayerId);&lt;br /&gt;
&lt;br /&gt;
// Cards played on the table&lt;br /&gt;
$result[&#039;cardsontable&#039;] = $this-&amp;gt;cards-&amp;gt;getCardsInLocation(&#039;cardsontable&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now on the client side we should display this data, so in your game.js file in the setup function (which is the receiver of getAllDatas), find // TODO: fix handStock&lt;br /&gt;
and replace our hack of putting cards directly into the hand with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      // Cards in player&#039;s hand&lt;br /&gt;
      this.handStock.addCards(Array.from(Object.values(this.gamedatas.hand)));&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
So we added all cards from server to the hand stock. Have to do this ugly array convertion, hopefully they will fix addCards so we don&#039;t need to do this.&lt;br /&gt;
&lt;br /&gt;
At this point, you have to RESTART a game and each player should see their hand!&lt;br /&gt;
&lt;br /&gt;
At any point if code does not work on clinet side, add command &amp;quot;debugger;&amp;quot; in the code. In browser press F12 to get dev tools, then reload.&lt;br /&gt;
You will hit breakpoint and can you in browser debugger. Don&#039;t forget to remove debugger; code after.&lt;br /&gt;
&lt;br /&gt;
=== Cards on Table ===&lt;br /&gt;
Now lets fix out tableau, find comment in setup method of .js file  // TODO: fix tableau&lt;br /&gt;
and remove stock.addCards... from that loop. But right after this add&lt;br /&gt;
      // Cards played on table&lt;br /&gt;
      for (i in this.gamedatas.cardsontable) {&lt;br /&gt;
        var card = this.gamedatas.cardsontable[i];&lt;br /&gt;
        var player_id = card.location_arg;&lt;br /&gt;
        this.tableauStocks[player_id].addCards([card]);&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
If you reload now you can see nothing on the table.&lt;br /&gt;
&lt;br /&gt;
Next, we will hook-up clicking on card and test if our animation.&lt;br /&gt;
&lt;br /&gt;
Find the &amp;quot;boom&amp;quot; we put in the click handler. Replace with this&lt;br /&gt;
      this.handStock.onCardClick = (card) =&amp;gt; {&lt;br /&gt;
         this.tableauStocks[card.location_arg].addCards([card]);&lt;br /&gt;
      };&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now if you reload you should be able to click on card from your hand and see it moving,&lt;br /&gt;
you can click on few cards this way. When you done enjoying the animation, press F5 to get your hand back.&lt;br /&gt;
&lt;br /&gt;
[[File:Heartsla-sync.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==State Machine==&lt;br /&gt;
&lt;br /&gt;
Stop the game. We are about to work on the game logic.&lt;br /&gt;
&lt;br /&gt;
You already read [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine], so you know that this is the heart of your game logic.&lt;br /&gt;
Note: ignore all the source snippets in this presentation, as framework changed, just note the concepts. &lt;br /&gt;
&lt;br /&gt;
Here are the states we need to build (excluding two more states we will add later to handle the exchange of cards at the beginning of the rounds):&lt;br /&gt;
&lt;br /&gt;
*Cards are dealt to all players (lets call it &amp;quot;NewHand&amp;quot;)&lt;br /&gt;
*Player start or respond to played card (&amp;quot;PlayerTurn&amp;quot;)&lt;br /&gt;
*Game control is passed to next player or trick is ended (&amp;quot;NextPlayer&amp;quot;)&lt;br /&gt;
*End of hand processing (scoring and check for end of game) (&amp;quot;EndHand&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Note: if you find states.inc.php file in top level directory - delete it now.&lt;br /&gt;
&lt;br /&gt;
=== State Templates ===&lt;br /&gt;
We will create just barebones state files first:&lt;br /&gt;
&lt;br /&gt;
==== States/NewHand.php ====&lt;br /&gt;
Let&#039;s create our first state - &amp;quot;NewHand&amp;quot;. Create a new file under &amp;quot;module/php/States&amp;quot; and name it &amp;quot;&#039;&#039;&#039;NewHand.php&#039;&#039;&#039;&amp;quot;. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
namespace Bga\Games\HeartsFIXME\States;&lt;br /&gt;
&lt;br /&gt;
use Bga\Games\HeartsFIXME\Game;&lt;br /&gt;
use Bga\GameFramework\StateType;&lt;br /&gt;
use Bga\GameFramework\States\GameState;&lt;br /&gt;
&lt;br /&gt;
class NewHand extends GameState&lt;br /&gt;
{&lt;br /&gt;
  public function __construct(protected Game $game)&lt;br /&gt;
  {&lt;br /&gt;
    parent::__construct(&lt;br /&gt;
      $game,&lt;br /&gt;
      id: 2, // the idea of the state&lt;br /&gt;
      type: StateType::GAME, // This type means that no player is active, and the game will automatically progress&lt;br /&gt;
      updateGameProgression: true, // entering this state can update the progress bar of the game&lt;br /&gt;
    );&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // The action we do when entering the state&lt;br /&gt;
  public function onEnteringState()&lt;br /&gt;
  {&lt;br /&gt;
    return PlayerTurn::class;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can read more about it here: [[State classes: State directory]]&lt;br /&gt;
&lt;br /&gt;
If you use IDE you see few errors:&lt;br /&gt;
* First there is no Bga\Games\HeartsFIXME\Game - that is because you games is not called HeartsFIXME, is something like HeartsFooBar - so you change this and namespace to your game name.&lt;br /&gt;
* Second it will compain abot NewTrick class - it does not exist yet&lt;br /&gt;
&lt;br /&gt;
Let&#039;s implement the other states:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== States/PlayerTurn.php ====&lt;br /&gt;
If file exists replace its content.&lt;br /&gt;
This action is different because it has an action a player must take. Read the comments in the code below to understand the syntax:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
&lt;br /&gt;
namespace Bga\Games\HeartsFIXME\States;&lt;br /&gt;
&lt;br /&gt;
use Bga\Games\HeartsFIXME\Game;&lt;br /&gt;
use Bga\GameFramework\StateType;&lt;br /&gt;
use Bga\GameFramework\States\PossibleAction;&lt;br /&gt;
use Bga\GameFramework\States\GameState;&lt;br /&gt;
use Bga\GameFramework\UserException;&lt;br /&gt;
&lt;br /&gt;
class PlayerTurn extends GameState&lt;br /&gt;
{&lt;br /&gt;
    public function __construct(protected Game $game)&lt;br /&gt;
    {&lt;br /&gt;
        parent::__construct(&lt;br /&gt;
            $game,&lt;br /&gt;
            id: 31,&lt;br /&gt;
            type: StateType::ACTIVE_PLAYER, // This state type means that one player is active and can do actions&lt;br /&gt;
            description: clienttranslate(&#039;${actplayer} must play a card&#039;), // We tell OTHER players what they are waiting for&lt;br /&gt;
            descriptionMyTurn: clienttranslate(&#039;${you} must play a card&#039;), // We tell the ACTIVE player what they must do&lt;br /&gt;
            // We suround the code with clienttranslate() so that the text is sent to the client for translation (this will enable the game to support other languages)&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    #[PossibleAction] // a PHP attribute that tells BGA &amp;quot;this method describes a possible action that the player could take&amp;quot;, so that you can call that action from the front (the client)&lt;br /&gt;
    public function actPlayCard(int $cardId, int $activePlayerId)&lt;br /&gt;
    {&lt;br /&gt;
        // TODO: implement logic&lt;br /&gt;
        return NextPlayer::class; // after the action, we move to the next player&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function zombie(int $playerId)&lt;br /&gt;
    {&lt;br /&gt;
        // We must implement this so BGA can auto play in the case a player becomes a zombie, but for this tutorial we won&#039;t handle this case&lt;br /&gt;
        throw new UserException(&#039;Not implemented: zombie for player ${player_id}&#039;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
You would also notice the &amp;quot;zombie&amp;quot; method. This would allow BGA to auto-player for the player if they became inactive. This is mandatory, but we will implement this later.&lt;br /&gt;
&lt;br /&gt;
==== States/NextPlayer.php ====&lt;br /&gt;
This state have a couple of different options for what would be the next state:&lt;br /&gt;
&lt;br /&gt;
* If not all players played a card in the current trick - we need to go to &#039;&#039;&#039;PlayerTurn&#039;&#039;&#039; (for the next player)&lt;br /&gt;
* If all players finished the trick but still have cards in their hand - we need to go to &#039;&#039;&#039;PlayerTurn&#039;&#039;&#039;&lt;br /&gt;
* If this is the last trick (no more cards in end) and it&#039;s finished, we need to go to &#039;&#039;&#039;EndHand&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
We will implement this logic later. For now let&#039;s return &#039;&#039;&#039;NewHandPlayerTurn&#039;&#039;&#039; (see onEnteringState).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
namespace Bga\Games\HeartsFIXME\States;&lt;br /&gt;
&lt;br /&gt;
use Bga\GameFramework\StateType;&lt;br /&gt;
use Bga\Games\HeartsFIXME\Game;&lt;br /&gt;
use Bga\GameFramework\States\GameState;&lt;br /&gt;
&lt;br /&gt;
class NextPlayer extends GameState&lt;br /&gt;
{&lt;br /&gt;
  public function __construct(protected Game $game)&lt;br /&gt;
  {&lt;br /&gt;
    parent::__construct(&lt;br /&gt;
      $game,&lt;br /&gt;
      id: 32,&lt;br /&gt;
      type: StateType::GAME,&lt;br /&gt;
    );&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public function onEnteringState()&lt;br /&gt;
  {&lt;br /&gt;
    return PlayerTurn::class;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== States/EndHand.php ====&lt;br /&gt;
Here too we will have two options for transition, either we play another hand (&#039;&#039;&#039;NewHand&#039;&#039;&#039;) or we finish the game (a reserved id for finishing the game is &#039;&#039;&#039;99&#039;&#039;&#039;). &lt;br /&gt;
&lt;br /&gt;
We will implement this logic later. For now let&#039;s return &#039;&#039;&#039;NewHand&#039;&#039;&#039; (see onEnteringState).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
namespace Bga\Games\HeartsFIXME\States;&lt;br /&gt;
&lt;br /&gt;
use Bga\GameFramework\StateType;&lt;br /&gt;
use Bga\Games\HeartsFIXME\Game;&lt;br /&gt;
use Bga\GameFramework\States\GameState;&lt;br /&gt;
&lt;br /&gt;
class EndHand extends GameState&lt;br /&gt;
{&lt;br /&gt;
  public function __construct(protected Game $game)&lt;br /&gt;
  {&lt;br /&gt;
    parent::__construct(&lt;br /&gt;
      $game,&lt;br /&gt;
      id: 40,&lt;br /&gt;
      type: StateType::GAME,&lt;br /&gt;
      description: &amp;quot;&amp;quot;,&lt;br /&gt;
    );&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public function onEnteringState()&lt;br /&gt;
  {&lt;br /&gt;
    // TODO: implement logic&lt;br /&gt;
    return NewHand::class;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Check again that no HeartsFIXME left in the code, if yes replace with game name.&lt;br /&gt;
&lt;br /&gt;
Remove EndScore.php - don&#039;t need it.&lt;br /&gt;
&lt;br /&gt;
Don&#039;t start the game yet, it won&#039;t load, we have to clean up bunch of template code in the .js&lt;br /&gt;
&lt;br /&gt;
=== Test Your Game is not broken ===&lt;br /&gt;
We changed state related logic, so we need to restart the game. If the game starts without error we are good. We won&#039;t be able to test the interactions yet because we need to implement the client side. &lt;br /&gt;
&lt;br /&gt;
Think the following is from the old framework and no longer relevant.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;s&amp;gt;Since we added bunch of different states we need to remove some more templace code, in .js file find onUpdateActionButtons, and remove all functional code, leaving just this&amp;lt;/s&amp;gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
    onUpdateActionButtons: function (stateName, args) {&lt;br /&gt;
      console.log(&amp;quot;onUpdateActionButtons: &amp;quot; + stateName, args);&lt;br /&gt;
&lt;br /&gt;
      if (this.isCurrentPlayerActive()) {&lt;br /&gt;
        switch (stateName) {&lt;br /&gt;
          case &amp;quot;playerTurn&amp;quot;:&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== State Logic ===&lt;br /&gt;
Now if you RESTART the game, it should not crash and you see 13 cards in your hand&lt;br /&gt;
&lt;br /&gt;
==== New Hand ====&lt;br /&gt;
We need to:&lt;br /&gt;
&lt;br /&gt;
# Move all cards to the deck&lt;br /&gt;
# Shuffle the cards&lt;br /&gt;
# Deal the cards to the players&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Here&#039;s the code insert infro NewHand.php state:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // The action we do when entering the state&lt;br /&gt;
    public function onEnteringState()&lt;br /&gt;
    {&lt;br /&gt;
        $game = $this-&amp;gt;game;&lt;br /&gt;
        // Take back all cards (from any location =&amp;gt; null) to deck&lt;br /&gt;
        $game-&amp;gt;cards-&amp;gt;moveAllCardsInLocation(null, &amp;quot;deck&amp;quot;);&lt;br /&gt;
        $game-&amp;gt;cards-&amp;gt;shuffle(&#039;deck&#039;);&lt;br /&gt;
        // Deal 13 cards to each players&lt;br /&gt;
        // Create deck, shuffle it and give 13 initial cards&lt;br /&gt;
        $players = $game-&amp;gt;loadPlayersBasicInfos();&lt;br /&gt;
        foreach ($players as $player_id =&amp;gt; $player) {&lt;br /&gt;
            $cards = $game-&amp;gt;cards-&amp;gt;pickCards(13, &#039;deck&#039;, $player_id);&lt;br /&gt;
            // Notify player about his cards&lt;br /&gt;
            $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;player($player_id, &#039;newHand&#039;, &#039;&#039;, array(&#039;cards&#039; =&amp;gt; $cards));&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // reset trick color&lt;br /&gt;
        $this-&amp;gt;game-&amp;gt;setGameStateInitialValue(&#039;trick_color&#039;, 0);&lt;br /&gt;
&lt;br /&gt;
        // FIXME: first player one with 2 of clubs&lt;br /&gt;
        $first_player = (int) $this-&amp;gt;game-&amp;gt;getActivePlayerId();&lt;br /&gt;
        $this-&amp;gt;game-&amp;gt;gamestate-&amp;gt;changeActivePlayer($first_player);&lt;br /&gt;
        return PlayerTurn::class;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Next Player ====&lt;br /&gt;
Here we can handle the logic of what is the next state we need to move to:&amp;lt;pre&amp;gt;&lt;br /&gt;
public function onEnteringState()&lt;br /&gt;
  {&lt;br /&gt;
    $game = $this-&amp;gt;game;&lt;br /&gt;
    // Active next player OR end the trick and go to the next trick OR end the hand&lt;br /&gt;
    if ($game-&amp;gt;cards-&amp;gt;countCardInLocation(&#039;cardsontable&#039;) == 4) {&lt;br /&gt;
      // This is the end of the trick&lt;br /&gt;
      // Select the winner&lt;br /&gt;
      $best_value_player_id = $game-&amp;gt;activeNextPlayer(); // TODO figure out winner of trick&lt;br /&gt;
&lt;br /&gt;
      // Move all cards to &amp;quot;cardswon&amp;quot; of the given player&lt;br /&gt;
      $game-&amp;gt;cards-&amp;gt;moveAllCardsInLocation(&#039;cardsontable&#039;, &#039;cardswon&#039;, null, $best_value_player_id);&lt;br /&gt;
&lt;br /&gt;
      if ($game-&amp;gt;cards-&amp;gt;countCardInLocation(&#039;hand&#039;) == 0) {&lt;br /&gt;
        // End of the hand&lt;br /&gt;
        return EndHand::class;&lt;br /&gt;
      } else {&lt;br /&gt;
        // End of the trick&lt;br /&gt;
        // Reset trick suite to 0 &lt;br /&gt;
        $this-&amp;gt;game-&amp;gt;setGameStateInitialValue(&#039;trick_color&#039;, 0);&lt;br /&gt;
        return PlayerTurn::class;&lt;br /&gt;
      }&lt;br /&gt;
    } else {&lt;br /&gt;
      // Standard case (not the end of the trick)&lt;br /&gt;
      // =&amp;gt; just active the next player&lt;br /&gt;
      $player_id = $game-&amp;gt;activeNextPlayer();&lt;br /&gt;
      $game-&amp;gt;giveExtraTime($player_id);&lt;br /&gt;
      return PlayerTurn::class;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&#039;&#039;&#039;Important&#039;&#039;&#039;: All state actions game or player must return the next state transition (or thrown exception). &lt;br /&gt;
&lt;br /&gt;
==== Player Turn ====&lt;br /&gt;
We will not implement this yet, but we can throw an exception to check that the interaction is working properly. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#[PossibleAction] // a PHP attribute that tells BGA &amp;quot;this method describes a possible action that the player could take&amp;quot;, so that you can call that action from the front (the client)&lt;br /&gt;
  public function actPlayCard(int $cardId, int $activePlayerId)&lt;br /&gt;
  {&lt;br /&gt;
    throw new UserException(&amp;quot;Not implemented: $activePlayerId played card $cardId&amp;quot;);    &lt;br /&gt;
    return NextPlayer::class; // after the action, we move to the next player&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Client - Server Interactions==&lt;br /&gt;
&lt;br /&gt;
Now to implement things for real we have hook UI actions to ajax calls, and process notifications sent by the server.&lt;br /&gt;
So previously we hooked playCardOnTable right into js handler which caused client animation, in real game its a two&lt;br /&gt;
step operation. When user clicks on game element js client sends an ajax call to server, server processes it and updates database, server sends&lt;br /&gt;
notification in response, client hooks animations to server notification.&lt;br /&gt;
&lt;br /&gt;
So in .js code replace find out handStock.onCardClick and replace  the handler to&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    this.handStock.onCardClick = (card) =&amp;gt; {&lt;br /&gt;
      {&lt;br /&gt;
        console.log(&amp;quot;onCardClick : card &amp;quot;, card);&lt;br /&gt;
        console.log(&amp;quot;onCardClick : namestate &amp;quot;, this.gamedatas.gamestate.name);&lt;br /&gt;
        if (!card) return; // hmm - should never happen&lt;br /&gt;
        switch (this.gamedatas.gamestate.name) {&lt;br /&gt;
          case &amp;quot;PlayerTurn&amp;quot;:&lt;br /&gt;
            // Can play a card&lt;br /&gt;
            this.bga.actions.performAction(&amp;quot;actPlayCard&amp;quot;, { cardId: card.id });&lt;br /&gt;
&lt;br /&gt;
            break;&lt;br /&gt;
          case &amp;quot;GiveCards&amp;quot;:&lt;br /&gt;
            // Can give cards TODO&lt;br /&gt;
            break;&lt;br /&gt;
          default: {&lt;br /&gt;
            this.handStock.unselectAll();&lt;br /&gt;
            break;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now reload and when you click on card you should get a server response: Not implemented...&lt;br /&gt;
&lt;br /&gt;
Lets implement it, in &#039;&#039;&#039;PlayerTurn.php&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
We need to:&lt;br /&gt;
&lt;br /&gt;
# Move the card&lt;br /&gt;
# Notify all player on the the move&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#[PossibleAction]&lt;br /&gt;
  public function actPlayCard(int $cardId, int $activePlayerId)&lt;br /&gt;
  {&lt;br /&gt;
    $game = $this-&amp;gt;game;&lt;br /&gt;
    $game-&amp;gt;cards-&amp;gt;moveCard($cardId, &#039;cardsontable&#039;, $activePlayerId);&lt;br /&gt;
    // TODO: check rules here&lt;br /&gt;
    $currentCard = $game-&amp;gt;cards-&amp;gt;getCard($cardId);&lt;br /&gt;
    // And notify&lt;br /&gt;
        $game-&amp;gt;notify-&amp;gt;all(&#039;playCard&#039;, clienttranslate(&#039;${player_name} plays ${value_displayed} ${color_displayed}&#039;), [&lt;br /&gt;
            &#039;i18n&#039; =&amp;gt; array(&#039;color_displayed&#039;, &#039;value_displayed&#039;),&lt;br /&gt;
            &#039;card&#039; =&amp;gt; $currentCard,&lt;br /&gt;
            &#039;player_id&#039; =&amp;gt; $activePlayerId,&lt;br /&gt;
            &#039;player_name&#039; =&amp;gt; $game-&amp;gt;getActivePlayerName(),&lt;br /&gt;
            &#039;value_displayed&#039; =&amp;gt; $game-&amp;gt;card_types[&#039;types&#039;][$currentCard[&#039;type_arg&#039;]][&#039;name&#039;],&lt;br /&gt;
            &#039;color_displayed&#039; =&amp;gt; $game-&amp;gt;card_types[&#039;suites&#039;][$currentCard[&#039;type&#039;]][&#039;name&#039;]&lt;br /&gt;
        ]&lt;br /&gt;
        );&lt;br /&gt;
    return NextPlayer::class;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We get the card from client, we move it to the table (moveCard is hooked to database directly, its part of deck class),&lt;br /&gt;
we notify all players and we change state. What we are missing here is bunch of checks (rule enforcements), we will add it later.&lt;br /&gt;
&lt;br /&gt;
Interesting part about this notify is that we use i18n array for strings that needs to be translated by client, so&lt;br /&gt;
they are sent as English text in notification, then client has to know which parameters needs translating.&lt;br /&gt;
&lt;br /&gt;
On the client side .js we have to implement a notification handler to do the animation. Below the &#039;&#039;&#039;setupNotification&#039;&#039;&#039; method (which you don&#039;t need to touch) &lt;br /&gt;
after &amp;lt;code&amp;gt;// TODO: from this point and below, you can write your game notifications handling methods&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
you can put the following code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  async notif_newHand(args) {&lt;br /&gt;
    // We received a new full hand of 13 cards.&lt;br /&gt;
    this.handStock.removeAll();&lt;br /&gt;
    this.handStock.addCards(Array.from(Object.values(args.hand)));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  async notif_playCard(args) {&lt;br /&gt;
    // Play a card on the table&lt;br /&gt;
    this.tableauStocks[args.player_id].addCards([args.card]);&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
BGA will automatically bind the event to the &#039;&#039;&#039;notif_{eventName} handler&#039;&#039;&#039; which will receive the &amp;quot;args&amp;quot; you passed from php.&lt;br /&gt;
&lt;br /&gt;
Refresh the page and try to play a card from the correct player. The card should move to the played area. When you refresh - you should still see the card there.&lt;br /&gt;
Swicth to next player using the arrows near player name and play next card.&lt;br /&gt;
Just before last card save the game state in &amp;quot;Save 1&amp;quot; slot (buttons in the bottom). These saves game states and you can reload it using &amp;quot;Load 1&amp;quot; later.&lt;br /&gt;
It is very handy.&lt;br /&gt;
Finish playing the trick. You will notice&lt;br /&gt;
after trick is done all cards remains on the table, but if you press F5 they would disappear, this is because&lt;br /&gt;
we updated database to pick-up the cards but did not send notification about it.&lt;br /&gt;
&lt;br /&gt;
So in &#039;&#039;&#039;NextPlayer.php&#039;&#039;&#039; file add notification after // Move all cards to &amp;quot;cardswon&amp;quot; of the given player:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            // Move all win cards to cardswon location&lt;br /&gt;
            $moved_cards = $game-&amp;gt;cards-&amp;gt;getCardsInLocation(&#039;cardsontable&#039;); // remember for notification what we moved&lt;br /&gt;
            $game-&amp;gt;cards-&amp;gt;moveAllCardsInLocation(&#039;cardsontable&#039;, &#039;cardswon&#039;, null, $best_value_player_id);&lt;br /&gt;
&lt;br /&gt;
            // Note: we use 2 notifications here in order we can pause the display during the first notification&lt;br /&gt;
            //  before we move all cards to the winner (during the second)&lt;br /&gt;
            $players = $game-&amp;gt;loadPlayersBasicInfos();&lt;br /&gt;
            $game-&amp;gt;notify-&amp;gt;all(&#039;trickWin&#039;, clienttranslate(&#039;${player_name} wins the trick&#039;), array(&lt;br /&gt;
                &#039;player_id&#039; =&amp;gt; $best_value_player_id,&lt;br /&gt;
                &#039;player_name&#039; =&amp;gt; $players[$best_value_player_id][&#039;player_name&#039;],&lt;br /&gt;
&lt;br /&gt;
            ));&lt;br /&gt;
&lt;br /&gt;
            $game-&amp;gt;notify-&amp;gt;all(&#039;giveAllCardsToPlayer&#039;, &#039;&#039;, array(&lt;br /&gt;
                &#039;player_id&#039; =&amp;gt; $best_value_player_id,&lt;br /&gt;
                &#039;cards&#039; =&amp;gt; $game-&amp;gt;cards-&amp;gt;getCards(array_keys($moved_cards))&lt;br /&gt;
            ));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You notice that we passes player_name in notification because its used in the message but its pretty redundant,&lt;br /&gt;
as we should be able to figure out player_name by player_id.&lt;br /&gt;
&lt;br /&gt;
There is a way to fix it.&lt;br /&gt;
&lt;br /&gt;
in constructor of Game.php uncomment the decorator and remove second part that related to cards, so you will end up with&lt;br /&gt;
        /* notification decorator */&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;notify-&amp;gt;addDecorator(function(string $message, array $args) {&lt;br /&gt;
            if (isset($args[&#039;player_id&#039;]) &amp;amp;&amp;amp; !isset($args[&#039;player_name&#039;]) &amp;amp;&amp;amp; str_contains($message, &#039;${player_name}&#039;)) {&lt;br /&gt;
                $args[&#039;player_name&#039;] = $this-&amp;gt;getPlayerNameById($args[&#039;player_id&#039;]);&lt;br /&gt;
            }&lt;br /&gt;
    &lt;br /&gt;
            return $args;&lt;br /&gt;
        });&lt;br /&gt;
Now we can remove player_name as notification argument in NextPlayer.php (and other states) and $players variable because its not used anymore.&lt;br /&gt;
&lt;br /&gt;
            $game-&amp;gt;notify-&amp;gt;all(&#039;trickWin&#039;, clienttranslate(&#039;${player_name} wins the trick&#039;), array(&lt;br /&gt;
                &#039;player_id&#039; =&amp;gt; $best_value_player_id,&lt;br /&gt;
            ));&lt;br /&gt;
&lt;br /&gt;
Now lets add these handlers in the .js file to handle our notifications:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  async notif_trickWin(args) {&lt;br /&gt;
    // We do nothing here (just wait in order players can view the 4 cards played before they&#039;re gone)&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  async notif_giveAllCardsToPlayer(args) {&lt;br /&gt;
    // Move all cards on table to given table, then destroy them&lt;br /&gt;
    const winner_id = args.player_id;&lt;br /&gt;
    const cards = Array.from(Object.values(args.cards));&lt;br /&gt;
    await this.tableauStocks[winner_id].addCards(cards);&lt;br /&gt;
  }&lt;br /&gt;
  // TODO: cards has to dissapear after&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ok we notice that cards that was won bunched up in ugly column and stay on tableau, but they should dissaper after trick is taken.&lt;br /&gt;
&lt;br /&gt;
Now lets fix the ugly stock. We can make tableau a bit bigger to fit 4 cards or we should make cards overlap, later makes more sense since making tableau too big will be ugly.&lt;br /&gt;
&lt;br /&gt;
I could not figure out how to do overlap in LineStock,  AI thinks that there is attribute cardOverlap that I can set when creatingt stock, but it does not work on LineStock (as on 1.7), &lt;br /&gt;
so lets just add css for this in .css file&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.playertable .ha-card ~ .ha-card {&lt;br /&gt;
    margin-left: calc(var(--h-card-width) * -0.8);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This uses tilda operator that target the sibling, which is essentially all cards except first.&lt;br /&gt;
&lt;br /&gt;
If you want to test that it works you can reload you test state using Load 1 button to see the finishing of a trick.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now reload to test the trick taking - it is pretty now .&lt;br /&gt;
&lt;br /&gt;
Final touch, we need card to dissapear into the void. The void we have to create first.&lt;br /&gt;
We need to add another node in the dom for that void stock, on server we called location &amp;quot;cardswon&amp;quot; so lets use same name, change the tableau template in .js file to this&amp;lt;pre&amp;gt;&lt;br /&gt;
            &amp;lt;div id=&amp;quot;tableau_${player.id}&amp;quot;/&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;div id=&amp;quot;cardswon_${player.id}&amp;quot;/&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We added cardswon (and class for tableau just in case we need it later).&lt;br /&gt;
&lt;br /&gt;
Now in setup method of .js file we need to create stock for this location, in the loop where we adding tableau stock and the end of loop add this code:&lt;br /&gt;
        // add void stock&lt;br /&gt;
        new BgaCards.VoidStock(&lt;br /&gt;
          this.cardsManager,&lt;br /&gt;
          document.getElementById(`cardswon_${player.id}`),&lt;br /&gt;
          {&lt;br /&gt;
            autoPlace: (card) =&amp;gt;&lt;br /&gt;
              card.location === &amp;quot;cardswon&amp;quot; &amp;amp;&amp;amp; card.location_arg == player.id,&lt;br /&gt;
          }&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
If you notice we did not assign this to any variable, this is because we won&#039;t need to refer to it, we will use autoPlace feature, where cardManager will know where to place it based on the location from server.&lt;br /&gt;
Finally we just have to modify notification handler to add this animation,  this is final version (in .js file)&lt;br /&gt;
&lt;br /&gt;
    notif_giveAllCardsToPlayer: async function (args) {&lt;br /&gt;
      // Move all cards on table to given table, then destroy them&lt;br /&gt;
      const winner_id = args.player_id;&lt;br /&gt;
&lt;br /&gt;
      const cards = Array.from(Object.values(args.cards));&lt;br /&gt;
      await this.tableauStocks[winner_id].addCards(cards);&lt;br /&gt;
      await this.cardsManager.placeCards(cards); // auto-placement&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
So the function is async means it will return Promise. We are doing it so we can wait other animations to complete.&lt;br /&gt;
First we adding cards to player tableau, waiting for animation, then adding to our void stock where they are dissapear.&lt;br /&gt;
&lt;br /&gt;
Now after the trick you see all cards move towards the &amp;quot;player&#039;s stash&amp;quot;.&lt;br /&gt;
The animation is not ideal, so lets at void stock settings to see if can improve it: https://x.boardgamearena.net/data/game-libs/bga-cards/1.0.7/docs/classes/stocks_void-stock.VoidStock.html&lt;br /&gt;
Ok, well I could not figure it out, but now you know where docs for these components are.&lt;br /&gt;
We will do our CSS hack, in .css add:&lt;br /&gt;
&lt;br /&gt;
 .cardswon &amp;gt; .ha-card {&lt;br /&gt;
   position: absolute;&lt;br /&gt;
   top: 0 !important;&lt;br /&gt;
 } &lt;br /&gt;
&lt;br /&gt;
==Zombie turn==&lt;br /&gt;
&lt;br /&gt;
We will implement a zombie function now because a) we have to do it at some point&lt;br /&gt;
b) playing 13 cards from 4 players manually to test this game is super annoying - but we can actually re-use this feature to &amp;quot;auto-play&amp;quot;&lt;br /&gt;
&lt;br /&gt;
In &#039;&#039;&#039;PlayerTurn.php&#039;&#039;&#039; file, replace the zombie function with this code:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function zombie(int $playerId)&lt;br /&gt;
    {&lt;br /&gt;
        $game = $this-&amp;gt;game;&lt;br /&gt;
        // Auto-play a random card from player&#039;s hand&lt;br /&gt;
        $cards_in_hand = $game-&amp;gt;cards-&amp;gt;getCardsInLocation(&#039;hand&#039;, $playerId);&lt;br /&gt;
        if (count($cards_in_hand) &amp;gt; 0) {&lt;br /&gt;
            $card_to_play = $cards_in_hand[array_rand($cards_in_hand)];&lt;br /&gt;
            $game-&amp;gt;cards-&amp;gt;moveCard($card_to_play[&#039;id&#039;], &#039;cardsontable&#039;, $playerId);&lt;br /&gt;
            // Notify&lt;br /&gt;
            $game-&amp;gt;notify-&amp;gt;all(&lt;br /&gt;
                &#039;playCard&#039;,&lt;br /&gt;
                clienttranslate(&#039;${player_name} auto plays ${value_displayed} ${color_displayed}&#039;),&lt;br /&gt;
                [&lt;br /&gt;
                    &#039;i18n&#039; =&amp;gt; array(&#039;color_displayed&#039;, &#039;value_displayed&#039;),&lt;br /&gt;
                    &#039;card&#039; =&amp;gt; $card_to_play,&lt;br /&gt;
                    &#039;player_id&#039; =&amp;gt; $playerId,&lt;br /&gt;
                    &#039;value_displayed&#039; =&amp;gt; $game-&amp;gt;card_types[&#039;types&#039;][$card_to_play[&#039;type_arg&#039;]][&#039;name&#039;],&lt;br /&gt;
                    &#039;color_displayed&#039; =&amp;gt; $game-&amp;gt;card_types[&#039;suites&#039;][$card_to_play[&#039;type&#039;]][&#039;name&#039;]&lt;br /&gt;
                ]&lt;br /&gt;
            );&lt;br /&gt;
        }&lt;br /&gt;
        return NextPlayer::class;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, watch this! Click Debug symbol on top bar (bug) and select function &amp;quot;playAutomatically&amp;quot; (this is actually function in your php file! it starts with debug_),&lt;br /&gt;
and select number of moves, i.e. 4.&lt;br /&gt;
If your zombie function works correctly you will see player play automatically. To play whole hand it will be 52 moves (13*4).&lt;br /&gt;
&lt;br /&gt;
==Scoring and End of game handling==&lt;br /&gt;
&lt;br /&gt;
Now we should calculate scoring and for that we need to actually track who wins the trick.&lt;br /&gt;
Trick is won by the player with highest card (no trump). We just need to remember what is trick suite.&lt;br /&gt;
For which we will use state variable &#039;trick_color&#039; which we already conveniently created.&lt;br /&gt;
&lt;br /&gt;
In &#039;&#039;&#039;PlayerTurn.php&#039;&#039;&#039; state, add this before notification&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$currenttrick_color = $game-&amp;gt;getGameStateValue(&#039;trick_color&#039;);&lt;br /&gt;
if ($currenttrick_color == 0) $game-&amp;gt;setGameStateValue(&#039;trick_color&#039;, $currentCard[&#039;type&#039;]);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will make sure we remember the first suit being played, now we will use it. Modify the &#039;&#039;&#039;NextPlayer.php&#039;&#039;&#039; state to fix our TODO comment in onEnteringState&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // Active next player OR end the trick and go to the next trick OR end the hand&lt;br /&gt;
        if ($game-&amp;gt;cards-&amp;gt;countCardInLocation(&#039;cardsontable&#039;) == 4) {&lt;br /&gt;
            // This is the end of the trick&lt;br /&gt;
            $cards_on_table = $game-&amp;gt;cards-&amp;gt;getCardsInLocation(&#039;cardsontable&#039;);&lt;br /&gt;
            $best_value = 0;&lt;br /&gt;
            $best_value_player_id = $this-&amp;gt;game-&amp;gt;getActivePlayerId(); // fallback &lt;br /&gt;
            $currenttrick_color = $game-&amp;gt;getGameStateValue(&#039;trick_color&#039;);&lt;br /&gt;
            foreach ($cards_on_table as $card) {&lt;br /&gt;
                if ($card[&#039;type&#039;] == $currenttrick_color) {   // type is card suite&lt;br /&gt;
                    if ($best_value_player_id === null || $card[&#039;type_arg&#039;] &amp;gt; $best_value) {&lt;br /&gt;
                        $best_value_player_id = $card[&#039;location_arg&#039;]; // location_arg is player who played this card on table&lt;br /&gt;
                        $best_value = $card[&#039;type_arg&#039;]; // type_arg is value of the card (2 to 14)&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // Active this player =&amp;gt; he&#039;s the one who starts the next trick&lt;br /&gt;
            $this-&amp;gt;gamestate-&amp;gt;changeActivePlayer($best_value_player_id);&lt;br /&gt;
&lt;br /&gt;
            // Move all win cards to cardswon location&lt;br /&gt;
            $win_location = &#039;cardswon&#039;;&lt;br /&gt;
            $game-&amp;gt;cards-&amp;gt;moveAllCardsInLocation(&#039;cardsontable&#039;, $win_location, null, $best_value_player_id);&lt;br /&gt;
            // ... notification is the same as before&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The scoring rule in the studio example code is huge multi-page function, for this tutorial we will make simplier.&lt;br /&gt;
Lets score -1 point per heart and call it a day. And game will end when somebody goes -100 or below.&lt;br /&gt;
&lt;br /&gt;
As UI goes for scoring, the main thing to update is:&lt;br /&gt;
&lt;br /&gt;
* The scoring on the mini boards represented by stars&lt;br /&gt;
* Show that in the log. &lt;br /&gt;
&lt;br /&gt;
For a real game, you might consider showing the scoring in a [[Game_interface_logic:_yourgamename.js#Scoring_dialogs|Scoring Dialog]] using tableWindow notification, but this is out of scope of this tutorial. You can do that as homework. &lt;br /&gt;
&lt;br /&gt;
In &#039;&#039;&#039;EndHand.php&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use Bga\GameFramework\NotificationMessage; // add this to the top of the file, together with the other &amp;quot;use&amp;quot; statements&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
public function onEnteringState()&lt;br /&gt;
{&lt;br /&gt;
  $game = $this-&amp;gt;game;&lt;br /&gt;
  // Count and score points, then end the game or go to the next hand.&lt;br /&gt;
  $players = $game-&amp;gt;loadPlayersBasicInfos();&lt;br /&gt;
  // Gets all &amp;quot;hearts&amp;quot; + queen of spades&lt;br /&gt;
&lt;br /&gt;
  $player_to_points = array();&lt;br /&gt;
  foreach ($players as $player_id =&amp;gt; $player) {&lt;br /&gt;
    $player_to_points[$player_id] = 0;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  $cards = $game-&amp;gt;cards-&amp;gt;getCardsInLocation(&amp;quot;cardswon&amp;quot;);&lt;br /&gt;
  foreach ($cards as $card) {&lt;br /&gt;
    $player_id = $card[&#039;location_arg&#039;];&lt;br /&gt;
    // Note: 2 = heart&lt;br /&gt;
    if ($card[&#039;type&#039;] == 2) {&lt;br /&gt;
      $player_to_points[$player_id]++;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Apply scores to player&lt;br /&gt;
  foreach ($player_to_points as $player_id =&amp;gt; $points) {&lt;br /&gt;
    if ($points != 0) {&lt;br /&gt;
      $game-&amp;gt;playerScore-&amp;gt;inc(&lt;br /&gt;
        $player_id,&lt;br /&gt;
        -$points,&lt;br /&gt;
        new NotificationMessage(&lt;br /&gt;
          clienttranslate(&#039;${player_name} gets ${absInc} hearts and looses ${absInc} points&#039;),&lt;br /&gt;
        )&lt;br /&gt;
      );&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  ///// Test if this is the end of the game&lt;br /&gt;
  if ($game-&amp;gt;playerScore-&amp;gt;getMin() &amp;lt;= -100) {&lt;br /&gt;
    // Trigger the end of the game !&lt;br /&gt;
    return 99; // end game&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  return NewHand::class;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The game should work now. Try to play it!&lt;br /&gt;
==Clean Up==&lt;br /&gt;
We left some code that comes from template and our first code, we should remove it now.&lt;br /&gt;
&lt;br /&gt;
* In .js file remove debugger; statements if any&lt;br /&gt;
* Remove debug code from setupNewGame to deal cards, cards are now dealt in stNewHand state handler&lt;br /&gt;
        // Shuffle deck&lt;br /&gt;
        $this-&amp;gt;cards-&amp;gt;shuffle(&#039;deck&#039;);&lt;br /&gt;
        // Deal 13 cards to each players&lt;br /&gt;
        $players = $this-&amp;gt;loadPlayersBasicInfos();&lt;br /&gt;
        foreach ($players as $player_id =&amp;gt; $player) {&lt;br /&gt;
            $this-&amp;gt;cards-&amp;gt;pickCards(13, &#039;deck&#039;, $player_id);&lt;br /&gt;
        }&lt;br /&gt;
* Find and remove $playerEnergy variable and it&#039;s uses from Game.php (was part of template)&lt;br /&gt;
&lt;br /&gt;
==Rule Enforcements==&lt;br /&gt;
Now we have a working game, but there is no rule enforcement.&lt;br /&gt;
You can implement these rules in the &#039;&#039;&#039;actPlayCard&#039;&#039;&#039; function of &#039;&#039;&#039;PlayerTurn.php&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
    public function actPlayCard(int $cardId, int $activePlayerId)&lt;br /&gt;
    {&lt;br /&gt;
        $game = $this-&amp;gt;game;&lt;br /&gt;
        $card = $game-&amp;gt;cards-&amp;gt;getCard($cardId);&lt;br /&gt;
        if (!$card) {&lt;br /&gt;
            throw new \BgaSystemException(&amp;quot;Invalid move&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        // Rule checks&lt;br /&gt;
&lt;br /&gt;
        // Check that player has this card in hand&lt;br /&gt;
        if ($card[&#039;location&#039;] != &amp;quot;hand&amp;quot;) {&lt;br /&gt;
            throw new \BgaUserException(&lt;br /&gt;
                clienttranslate(&#039;You do not have this card in your hand&#039;)&lt;br /&gt;
            );&lt;br /&gt;
        }&lt;br /&gt;
        $currenttrick_color = $game-&amp;gt;getGameStateValue(&#039;trick_color&#039;);&lt;br /&gt;
        // Check that player follows suit if possible&lt;br /&gt;
        if ($currenttrick_color != 0) {&lt;br /&gt;
            $has_suit = false;&lt;br /&gt;
            $hand_cards = $game-&amp;gt;cards-&amp;gt;getCardsInLocation(&#039;hand&#039;, $activePlayerId);&lt;br /&gt;
            foreach ($hand_cards as $hand_card) {&lt;br /&gt;
                if ($hand_card[&#039;type&#039;] == $currenttrick_color) {&lt;br /&gt;
                    $has_suit = true;&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            if ($has_suit &amp;amp;&amp;amp; $card[&#039;type&#039;] != $currenttrick_color) {&lt;br /&gt;
                throw new \BgaUserException(&lt;br /&gt;
                    clienttranslate(&#039;You must follow suit&#039;)&lt;br /&gt;
                );&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can try to play wrong card now and you will see error!&lt;br /&gt;
But you noticed we broke the zombie mode as you cannot play random card anymore and now uThe user cannot play ANY card, there are only some cards they can play but we don&#039;t show this information which is annoying.&lt;br /&gt;
To fix this we can add a helper function that will return a list of playable cards for a given player.&lt;br /&gt;
Add these functions in &#039;&#039;&#039;Game.php&#039;&#039;&#039; file:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function getPlayableCards($player_id): array&lt;br /&gt;
    {&lt;br /&gt;
        // Get all data needed to check playable cards at the moment&lt;br /&gt;
        $currentTrickColor = $this-&amp;gt;getGameStateValue(&#039;trick_color&#039;);&lt;br /&gt;
        $broken_heart = $this-&amp;gt;brokenHeart();&lt;br /&gt;
        $total_played = $this-&amp;gt;cards-&amp;gt;countCardInLocation(&#039;cardswon&#039;) + $this-&amp;gt;cards-&amp;gt;countCardInLocation(&#039;cardsontable&#039;);&lt;br /&gt;
        $hand = $this-&amp;gt;cards-&amp;gt;getPlayerHand($player_id);&lt;br /&gt;
&lt;br /&gt;
        $playable_card_ids = [];&lt;br /&gt;
        $all_ids = array_keys($hand);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        if ($this-&amp;gt;cards-&amp;gt;getCardsInLocation(&#039;cardsontable&#039;, $player_id)) return []; // Already played a card&lt;br /&gt;
&lt;br /&gt;
        // Check whether the first card of the hand has been played or not&lt;br /&gt;
        // if ($total_played == 0) {&lt;br /&gt;
        //     // No cards have been played yet, find and return the starter card only&lt;br /&gt;
        //     foreach ($hand as $card) if ($card[&#039;type&#039;] == 3 &amp;amp;&amp;amp; $card[&#039;type_arg&#039;] == 2) return [$card[&#039;id&#039;]]; // 2 of clubs&lt;br /&gt;
        //     return [];&lt;br /&gt;
        // } else&lt;br /&gt;
        if (!$currentTrickColor) { // First card of the trick&lt;br /&gt;
            if ($broken_heart) return $all_ids; // Broken Heart or no limitation, can play any card&lt;br /&gt;
            else {&lt;br /&gt;
                // Exclude Heart as Heart hasn&#039;t been broken yet&lt;br /&gt;
                foreach ($hand as $card) if ($card[&#039;type&#039;] != 2) $playable_card_ids[] = $card[&#039;id&#039;];&lt;br /&gt;
                if (!$playable_card_ids) return $all_ids; // All Heart cards!&lt;br /&gt;
                else return $playable_card_ids;&lt;br /&gt;
            }&lt;br /&gt;
        } else {&lt;br /&gt;
            // Must follow the lead suit if possible&lt;br /&gt;
            $same_suit = false;&lt;br /&gt;
            foreach ($hand as $card)&lt;br /&gt;
                if ($card[&#039;type&#039;] == $currentTrickColor) {&lt;br /&gt;
                    $same_suit = true;&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
            if ($same_suit) return $this-&amp;gt;getObjectListFromDB(&amp;quot;SELECT card_id FROM card WHERE card_type = $currentTrickColor AND card_location = &#039;hand&#039; AND card_location_arg = $player_id&amp;quot;, true); // Has at least 1 card of the same suit&lt;br /&gt;
&lt;br /&gt;
            else return $all_ids;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function brokenHeart(): bool&lt;br /&gt;
    {&lt;br /&gt;
        // Check Heart in the played card piles&lt;br /&gt;
        return (bool)$this-&amp;gt;getUniqueValueFromDB(&amp;quot;SELECT count(*) FROM card WHERE card_location = &#039;cardswon&#039; AND card_type = 2&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function tableHeart(): bool&lt;br /&gt;
    {&lt;br /&gt;
        // Check Heart in the current trick&lt;br /&gt;
        return (bool)$this-&amp;gt;getUniqueValueFromDB(&amp;quot;SELECT count(*) FROM card WHERE card_location = &#039;cardsontable&#039; AND card_type = 2&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we can use this function in the zombie function of &#039;&#039;&#039;PlayerTurn.php&#039;&#039;&#039; to pick a random playable card:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function zombie(int $playerId)&lt;br /&gt;
    {&lt;br /&gt;
        $playable_cards = $this-&amp;gt;game-&amp;gt;getPlayableCards($playerId);&lt;br /&gt;
        $zombieChoice = $this-&amp;gt;getRandomZombieChoice($playable_cards); // random choice over possible moves&lt;br /&gt;
        return $this-&amp;gt;actPlayCard((int)$zombieChoice, $playerId);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And we can significantly simplify our rule check at actPlayCard&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $game = $this-&amp;gt;game;&lt;br /&gt;
        $currentCard = $game-&amp;gt;cards-&amp;gt;getCard($cardId);&lt;br /&gt;
        if (!$currentCard) {&lt;br /&gt;
            throw new \BgaSystemException(&amp;quot;Invalid move&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        // Rule checks&lt;br /&gt;
        $playable_cards = $game-&amp;gt;getPlayableCards($activePlayerId);&lt;br /&gt;
        if (!in_array($cardId, $playable_cards)) {&lt;br /&gt;
            throw new \BgaUserException(clienttranslate(&amp;quot;You cannot play this card now&amp;quot;));&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Finally we can send this to the client via state args so the user can see what moves are valid:&lt;br /&gt;
In &#039;&#039;&#039;PlayerTurn.php&#039;&#039;&#039; add this method:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function getArgs(int $activePlayerId): array&lt;br /&gt;
    {&lt;br /&gt;
        // Send playable card ids of the active player privately&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;_private&#039; =&amp;gt; [&lt;br /&gt;
                $activePlayerId =&amp;gt; [&lt;br /&gt;
                    &#039;playableCards&#039; =&amp;gt; $this-&amp;gt;game-&amp;gt;getPlayableCards($activePlayerId)&lt;br /&gt;
                ],&lt;br /&gt;
            ],&lt;br /&gt;
        ];&lt;br /&gt;
    }    &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On the client side in the .js file replace the onEnteringState method with this code:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    onEnteringState: function (stateName, args) {&lt;br /&gt;
      console.log(&amp;quot;Entering state: &amp;quot; + stateName, args);&lt;br /&gt;
      switch (stateName) {&lt;br /&gt;
        case &amp;quot;PlayerTurn&amp;quot;:&lt;br /&gt;
          if (this.isCurrentPlayerActive()) {&lt;br /&gt;
            // Check playable cards received from argPlayerTurn() in php&lt;br /&gt;
&lt;br /&gt;
            const playableCardIds = args.args._private.playableCards.map((x) =&amp;gt;&lt;br /&gt;
              parseInt(x)&lt;br /&gt;
            ); &lt;br /&gt;
&lt;br /&gt;
            const allCards = this.handStock.getCards();&lt;br /&gt;
            const playableCards = allCards.filter(&lt;br /&gt;
              (card) =&amp;gt; playableCardIds.includes(parseInt(card.id)) // never know if we get int or string, this method cares&lt;br /&gt;
            );&lt;br /&gt;
            this.handStock.setSelectionMode(&amp;quot;single&amp;quot;, playableCards);&lt;br /&gt;
          }&lt;br /&gt;
          break;&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
&amp;lt;/pre&amp;gt;  &lt;br /&gt;
And ALSO remove the this.handStock.setSelectionMode(&amp;quot;single&amp;quot;) line from the setup method. If you leave it there it won&#039;t work as this is async function that collide with our other async&lt;br /&gt;
functions that we are using in the onEnteringState&lt;br /&gt;
&lt;br /&gt;
==Fix first player with 2 of clubs==&lt;br /&gt;
&lt;br /&gt;
Find the comment &lt;br /&gt;
 // FIXME: first player one with 2 of clubs&lt;br /&gt;
and replace with this code:&lt;br /&gt;
 // first player one with 2 of clubs&lt;br /&gt;
 $first_player = $this-&amp;gt;game-&amp;gt;getUniqueValueFromDb(&amp;quot;SELECT card_location_arg FROM card WHERE card_location = &#039;hand&#039; AND card_type = 3 AND card_type_arg = 2&amp;quot;);// 2 of clubs&lt;br /&gt;
&lt;br /&gt;
 Then we can uncomment our code for getPlayableCards in Game.php:&lt;br /&gt;
        //Check whether the first card of the hand has been played or not&lt;br /&gt;
        $total_played = $this-&amp;gt;cards-&amp;gt;countCardInLocation(&#039;cardswon&#039;) + $this-&amp;gt;cards-&amp;gt;countCardInLocation(&#039;cardsontable&#039;);&lt;br /&gt;
        if ($total_played == 0) {&lt;br /&gt;
            // No cards have been played yet, find and return the starter card only&lt;br /&gt;
            foreach ($hand as $card) if ($card[&#039;type&#039;] == 3 &amp;amp;&amp;amp; $card[&#039;type_arg&#039;] == 2) return [$card[&#039;id&#039;]]; // 2 of clubs&lt;br /&gt;
            return $all_ids; // should not happen&lt;br /&gt;
        } else&lt;br /&gt;
&lt;br /&gt;
==Spectator support==&lt;br /&gt;
A spectator is not a real player but they can watch the game. Most games will require special spectator support, it&#039;s one of the steps in the alpha testing checklist.&lt;br /&gt;
In this game it&#039;s pretty simple, we just hide the hand control in the client&lt;br /&gt;
&lt;br /&gt;
In the .js file in the setup function add this code (after DOM is created):&lt;br /&gt;
      // Hide hand zone from spectators&lt;br /&gt;
      if (this.isSpectator)&lt;br /&gt;
        document.getElementById(&amp;quot;myhand_wrap&amp;quot;).style.display = &amp;quot;none&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
Click Test Spectator at the end of player&#039;s panels to test this.&lt;br /&gt;
&lt;br /&gt;
==Improve UI==&lt;br /&gt;
We need to fix a few things in the UI still.&lt;br /&gt;
&lt;br /&gt;
===Center Player Areas===&lt;br /&gt;
First let&#039;s fix the player tables - to make them centered.&lt;br /&gt;
In the .css file find #player-tables and change it to this:&lt;br /&gt;
&lt;br /&gt;
 #player-tables {&lt;br /&gt;
  position: relative;&lt;br /&gt;
  width: calc(var(--h-tableau-width) * 3.8);&lt;br /&gt;
  height: calc(var(--h-tableau-height) * 2.4);&lt;br /&gt;
  margin: auto; // that is a cheap way to make it centered&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Better Card Play Animations===&lt;br /&gt;
When another player plays a card it kind of just appears on the tableau, we want to make it look like it&#039;s coming from the player hand.&lt;br /&gt;
We don&#039;t actually have any sort of UI location to have a player hand - but we can either put it on the mini player panel or add it to the bottom of the player areas.&lt;br /&gt;
Let&#039;s try to put this on the mini player panels.&lt;br /&gt;
First we need to add a node in the DOM on the player panel and maybe add an icon to represent the hand.&lt;br /&gt;
We have access to some BGA icons and font awesome icons https://fontawesome.com/v4/icons, so we can pick one from there:&lt;br /&gt;
&lt;br /&gt;
In the .js file in the template for player tableau and add this at the end of the forEach body:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
         document.getElementById(`player_panel_content_${player.color}`).innerHTML = &lt;br /&gt;
         `&amp;lt;div id=&amp;quot;otherhand_${player.id}&amp;quot; class=&amp;quot;otherhand&amp;quot;&amp;gt;&amp;lt;i class=&amp;quot;fa fa-window-restore&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;&amp;lt;/div&amp;gt;`;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
In the .js file replace the notif handler for play with this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    notif_playCard: async function (args) {&lt;br /&gt;
      // Play a card on the table&lt;br /&gt;
      const playerId = args.player_id;&lt;br /&gt;
      let settings = {};&lt;br /&gt;
      if (playerId != this.player_id) {&lt;br /&gt;
        settings = {&lt;br /&gt;
          fromElement: $(`otherhand_${playerId}`),&lt;br /&gt;
          toPlaceholder: &amp;quot;grow&amp;quot;,&lt;br /&gt;
        };&lt;br /&gt;
      }&lt;br /&gt;
      await this.tableauStocks[playerId].addCard(args.card, settings);&lt;br /&gt;
    },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
What we did here is added a settings parameter for card placement - for cases where it&#039;s not our own card to move it from the &amp;quot;hand&amp;quot; area on the mini player board.&lt;br /&gt;
Reload and test (use the autoPlay feature to see the animation when the &amp;quot;other&amp;quot; player plays the card).&lt;br /&gt;
&lt;br /&gt;
Now we can also replace the void stock we create with animation to the same &amp;quot;otherhand&amp;quot; area:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    notif_giveAllCardsToPlayer: async function (args) {&lt;br /&gt;
      // Move all cards from notification to dedicated player area and fade out&lt;br /&gt;
      const playerId = args.player_id;&lt;br /&gt;
&lt;br /&gt;
      const cards = Array.from(Object.values(args.cards));&lt;br /&gt;
      await this.tableauStocks[playerId].addCards(cards);&lt;br /&gt;
      await this.tableauStocks[playerId].removeCards(cards, {&lt;br /&gt;
        fadeOut: true,&lt;br /&gt;
        slideTo: $(`otherhand_${playerId}`),&lt;br /&gt;
      });&lt;br /&gt;
    },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
And in this case we don&#039;t really need VoidStock anymore, we can remove it&lt;br /&gt;
Delete this&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // add void stock&lt;br /&gt;
        new BgaCards.VoidStock(&lt;br /&gt;
          this.cardsManager,&lt;br /&gt;
          document.getElementById(`cardswon_${playerId}`),&lt;br /&gt;
          {&lt;br /&gt;
            fadeOut: true, // not working&lt;br /&gt;
            toPlaceholder: &amp;quot;shrink&amp;quot;, // not working&lt;br /&gt;
            autoPlace: (card) =&amp;gt;&lt;br /&gt;
              card.location === &amp;quot;cardswon&amp;quot; &amp;amp;&amp;amp; card.location_arg == playerId,&lt;br /&gt;
          }&lt;br /&gt;
        );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Also can delete related css and DOM element cardswon.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We can also add this in .css to make this symbol centered:&lt;br /&gt;
 .otherhand {&lt;br /&gt;
   position: relative;&lt;br /&gt;
   margin: auto;&lt;br /&gt;
   text-align: center;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Card Sorting===&lt;br /&gt;
It would be nice to sort the cards in hand by suit and value.&lt;br /&gt;
You can add sorting&lt;br /&gt;
      // create the stock, in the game setup&lt;br /&gt;
      this.handStock = new BgaCards.HandStock(&lt;br /&gt;
        this.cardsManager,&lt;br /&gt;
        document.getElementById(&amp;quot;myhand&amp;quot;),&lt;br /&gt;
        {&lt;br /&gt;
             sort: BgaCards.sort(&#039;type&#039;, &#039;type_arg&#039;), // sort by suite then by value&lt;br /&gt;
        }&lt;br /&gt;
      ); &lt;br /&gt;
&lt;br /&gt;
but you will notice its not sorted right.&lt;br /&gt;
Its because of type mismatch. The server sends us strings and bga-cards expects integers.&lt;br /&gt;
We have to change this on client or server. I was already doing some ugly convertion on client, so lets just make it official.&lt;br /&gt;
We will add 2 functions in utility section that will do the convertions for us:&lt;br /&gt;
    ///////////////////////////////////////////////////&lt;br /&gt;
    //// Utility methods&lt;br /&gt;
&lt;br /&gt;
    remapToBgaCardList: function (cards) {&lt;br /&gt;
      if (!cards) return [];&lt;br /&gt;
      if (cards.type) {&lt;br /&gt;
        // actually one card&lt;br /&gt;
        return [this.remapToBgaCard(cards)];&lt;br /&gt;
      } else if (Array.isArray(cards)) {&lt;br /&gt;
        return cards.map((card) =&amp;gt; this.remapToBgaCard(card));&lt;br /&gt;
      } else {&lt;br /&gt;
        return Object.values(cards).map((card) =&amp;gt; this.remapToBgaCard(card));&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
    remapToBgaCard: function (card) {&lt;br /&gt;
      // proper casts&lt;br /&gt;
      return {&lt;br /&gt;
        id: parseInt(card.id),&lt;br /&gt;
        type: parseInt(card.type),&lt;br /&gt;
        type_arg: parseInt(card.type_arg),&lt;br /&gt;
        location: card.location,&lt;br /&gt;
        location_arg: parseInt(card.location_arg),&lt;br /&gt;
      };&lt;br /&gt;
    },&lt;br /&gt;
Then in setup change addCards to this&lt;br /&gt;
    this.handStock.addCards(this.remapToBgaCardList(this.gamedatas.hand));&lt;br /&gt;
and&lt;br /&gt;
      // Cards played on table&lt;br /&gt;
      for (i in this.gamedatas.cardsontable) {&lt;br /&gt;
        var card = this.gamedatas.cardsontable[i];&lt;br /&gt;
        var player_id = card.location_arg;&lt;br /&gt;
        this.tableauStocks[player_id].addCard(this.remapToBgaCard(card));&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
In notif_newHand replace addCards like this:&lt;br /&gt;
    this.handStock.addCards(this.remapToBgaCardList(args.hand));&lt;br /&gt;
&lt;br /&gt;
In notif_giveAllCardsToPlayer&lt;br /&gt;
      const cards = this.remapToBgaCardList(args.cards);&lt;br /&gt;
In notif_playCard&lt;br /&gt;
      await this.tableauStocks[playerId].addCard(this.remapToBgaCard(args.card), settings);&lt;br /&gt;
&lt;br /&gt;
Now sorting should work!&lt;br /&gt;
&lt;br /&gt;
===Tooltips===&lt;br /&gt;
We can add tooltips to cards to show their name.&lt;br /&gt;
In the .js file find where created silly tooltip with addTooltipHtml and replace with this:&lt;br /&gt;
          this.bga.gameui.addTooltipHtml(div.id, &lt;br /&gt;
             _(this.gamedatas.card_types.types[card.type_arg].name)+ &amp;quot; &amp;quot; +&lt;br /&gt;
             _(this.gamedatas.card_types.suites[card.type].name) &lt;br /&gt;
          );&lt;br /&gt;
Now what is this.gamedatas.card_types? Well that is our &amp;quot;material&amp;quot; of the game which is in our case variable in php, we have to send it to client for this to work.&lt;br /&gt;
Since it never changes we send it in getAllDatas method, add this at the end before return:&lt;br /&gt;
            $result[&#039;card_types&#039;] = $this-&amp;gt;card_types;&lt;br /&gt;
&lt;br /&gt;
Of course this is very basic tooltips and not even needed in this game, but in real game your want tooltips everywhere!!!&lt;br /&gt;
Lets add tooltip to our fake hand symbol also (the &amp;quot;otherhand&amp;quot;) (in setup method in .js somewhere in forEach loop over players)&lt;br /&gt;
        // add tooltips to player hand symbol&lt;br /&gt;
        this.bga.gameui.addTooltipHtml(&lt;br /&gt;
          `otherhand_${playerId}`,&lt;br /&gt;
          _(&amp;quot;Placeholder for player&#039;s hand&amp;quot;)&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
==Game progresstion==&lt;br /&gt;
In this game it should be easy, we just need to know if somebody close to -100 points!&lt;br /&gt;
Find getGameProgression in Game.php and replace with this:&lt;br /&gt;
    public function getGameProgression()&lt;br /&gt;
    {&lt;br /&gt;
        $min = $this-&amp;gt;playerScore-&amp;gt;getMin();&lt;br /&gt;
        return -1 * $min; // we get close to -100 we get close to 100% game completion&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Additional stuff==&lt;br /&gt;
&lt;br /&gt;
The following things were not implemented and you can add them yourself by looking at the code of the original hearts game:&lt;br /&gt;
&lt;br /&gt;
*Mark player who started the hand and add log about what is starting Suite of the trick&lt;br /&gt;
*Start scoring with 100 points each and end when &amp;lt;= 0&lt;br /&gt;
*Fix scoring rules with Q of spades and 26 point reverse scoring&lt;br /&gt;
*Add statistics&lt;br /&gt;
*Add card exchange states&lt;br /&gt;
*Add game option to start with 75 points instead of 100&lt;br /&gt;
&lt;br /&gt;
==After the tutorial==&lt;br /&gt;
You might want to check another tutorial, or start working on your first real project!&lt;br /&gt;
&lt;br /&gt;
[[Create a game in BGA Studio: Complete Walkthrough]]&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>RichardSPeters</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=File:HeartsTutorialImage1.png&amp;diff=28970</id>
		<title>File:HeartsTutorialImage1.png</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=File:HeartsTutorialImage1.png&amp;diff=28970"/>
		<updated>2026-03-16T20:56:47Z</updated>

		<summary type="html">&lt;p&gt;RichardSPeters: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;screen shot of hearts tutorial&lt;/div&gt;</summary>
		<author><name>RichardSPeters</name></author>
	</entry>
</feed>