Training

The largest bit of functionality added was the ability to train each player. Training doesn’t come free. Each player needs XP (which is gained through playing matches with other players) and reduces their condition. As long as the player’s attributes are equal to or greater than the cost of those, they can train.

The first step was to retrieve those costs and gains, which are stored in the TrainableTennisPlayer contract as public state variables. This is likely to change in future. Ideally, the higher an attribute gets, the more it would cost to train it higher, making it more and more expensive as the player gets better. For now, however, all these values are static.

Retrieving Costs and Gains

Note: I’m using the IARS project code structure. Don’t google that, I made the acronym up.

Public state variables in Solidity are accessed as if they are methods, so calling them from web3 requires the same syntax.

When a player is loaded, I have a function in my interactions.js file called loadTrainingCosts() which calls all of the public state variables representing costs and gains for training and resting.

loadTrainingCosts() in interactions.js

Training costs XP and condition (retrieved on lines 86 and 87), whilst increases an attribute (retrieved on line 88); Resting costs XP (retrieved on line 89) but increases condition (retrieved on line 90).

The final line in that method is a call to dispatch . I haven’t yet gone through what this looks like in the previous articles in the series, so I’ll walk through what happens here.

Dispatch on line 91 kicks off an action in actions.js called trainingDetailsLoaded() with all of the information retrieved from the Smart Contract. This is telling Redux that an action has just occurred and this information is the result. It’s the responsibility of the trainingDetailsLoaded() function to direct the information on to the correct reducer so that the state of the DApp can be updated.

trainingDetailsLoaded() in actions.js

The action-type here is TRAINING_DETAILS_LOADED which the dispatcher directs to the following reducer:

Snippet from reducers.js

The state is updated with all of the new information retrieved from the Smart Contract. The interface can then retrieve this data using a selector, which looks like this:

Snippet from selectors.js

All that the component needs to do is import each of these selectors and it now has full access to all costs and gains for training and resting!

Dropdown

We need users to be able to select which attribute they want to train on their player. React-bootstrap provides a nice component for us here called Dropdown .

Snippet from Train.js component

When clicked, the attributeSelected() function is called with dispatch , the name of the attribute and the id of the attribute. This is important because Solidity enums are externalised in the form of digit arrays. Even though the enum declaration in Solidity looks like this:

enum Attribute { agility, power, stamina, technique }

Interacting with it requires us to use integers 0, 1, 2 and 3.

The attributeSelected() function calls an interaction which follows the same sequence described in Retrieving Costs and Gains to update the currently selected attribute to train.

Training

Once the player is loaded and the desired attribute is selected, submitting the result uses all of this information to send a transaction to the ledger to train the player. The interaction function looks like this:

Snippet from interactions.js for training a player

tennisPlayer is a reference to the Smart Contract instance. At the moment, the DApp logs to the console at each stage of the transaction. I use subscriptions to the Train event instead of acting on receipt or confirmation events for now, but this may need to change in future.

The call on line 95 sends a transaction to this function in our Smart Contract:

train() function in TrainableTennisPlayer.sol

The Train() event is emitted when the player is successfully trained. To ensure the page is reloaded when this event is emitted, the DApp listens out for it using this subscription function:

Subscribing to Train event

When the player is successfully trained, this fires, causing the player to be reloaded and the information on the screen to be updated.

Result

I used the same workflow for resting players, without the ability to select an attribute through a dropdown, since resting only ever increases condition.

I added a few front-end features like badges to display the attribute values and colour coded them depending on the value. The current state of the DApp looks like this:

Using the game

Here’s what happens: