Symbol Mosaic Restrictions in Game

## Introduction

The [mosaic restriction](https://docs.symbolplatform.com/concepts/mosaic-restriction.html) is one of the coolest features in Symbol.

> Mosaic restrictions allow mosaic creators to decide which accounts can transact with the asset.

To use mosaic restrictions, there are two types of transactions needed.

- Global Restrictions ([MosaicGlobalRestrictionTransaction](https://docs.symbolplatform.com/serialization/#mosaicglobalrestrictiontransaction)): Define rules for the mosaics.
- Address Restrictions ([MosaicAddressRestrictionTransaction](https://docs.symbolplatform.com/serialization/#mosaicaddressrestrictiontransaction)): Define which accounts are able to use the mosaic.

I wanted to write a use case about it, but I didn't have the idea until recently, when I saw some community members share in Twitter that they were building a game on Symbol. So I wondered, it may be a good idea to use the mosaic restriction feature to create a simple concept of the Dungeons & Dragons game.

## Game Rules
- Players have a **level**, and **skill stat**.
- Players are rewarded 1 skill point if they win a round.
- There are some skills available such as **Punch**, **Kick**, **Fire Spell**
- Skill points can upgrade a player's skill stat, to unlock new skills.

## How it works

In the D&D games, we always have players and Dungeon masters.

#### Create Mosaics
We use [Mosaics](https://docs.symbolplatform.com/concepts/mosaic.html) to represent game assets, such as **level**, **punch**, **kick**, and **fire_spell**.

> Note: Mosaic Restrictions can only be used if `isRestrictable` is set to `true` on a mosaic.

```js
const dungeon_master = Account.createFromPrivateKey(privateKey, networkType);

const isSupplyMutable = true; // Easily adjust the supply
const isTransferable = false; // Players cannot transfer skills to other players
const isRestrictable = true; // Enable mosaic restrictions
const isRevokable = true;  // Creator can collect back mosaics

const nonce = MosaicNonce.createRandom();
const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create(
  Deadline.create(epochAdjustment),
  nonce,
  MosaicId.createFromNonce(nonce, dungeon_master.address),
  MosaicFlags.create(isSupplyMutable, isTransferable, isRestrictable, isRevokable),
  0, // divisibility
  UInt64.fromUint(0), // unlimited
  networkType,
);
```

#### Create Mosaic Global Restriction Transaction

Up to this stage, we can set up mosaic global restrictions for each mosaic (asset).

In global restrictions, you are allowed to add **reference mosaic** to restrictions, which I think is more advanced.

But do not worry, I will break it down in a table so you have a better view:

| MosaicId | Key | Value | Type| Reference Mosaic Id| Reference Mosaic Key | Reference Value| Reference Type|
|----------|-----|-------|-----|--------------------|----------------|----------------|---------------|
| [6AACF58D95F016C0](http://sym-test.opening-line.jp:3000/restrictions/mosaic/3DB6976E935F164CAE26129D3CAAF71E130E7719C603B924F8E4E66F2B4AC955) | level | 0  | GT | null | null | null | null
| [73E60A6DD1D16FDD](http://sym-test.opening-line.jp:3000/restrictions/mosaic/0DB820B7DA9D57963F8DBCE9D23341B2F10745BA36D515CC9DF94EB1A9E9CF8E) | punch | 1  | EQ | null | null | null | null
| [0015F52218DA969C](http://sym-test.opening-line.jp:3000/restrictions/mosaic/57029B25CF7B9E9A7A390E5E5378EF7D76CFD7FCF172B3CCACC1DDF9B72933FB) | kick | 2  | EQ | null | null | null | null
| [6A53A4FB36203CCE](http://sym-test.opening-line.jp:3000/restrictions/mosaic/ACAE9E027BDD31F0DD1820339366F00610DB26F6368797E6B209A41A24AB2893) | fire_spell | 2  | EQ | 1 | level | 5 | GE

Check mosaic Ids `6AACF58D95F016C0`, `73E60A6DD1D16FDD`, and `0015F52218DA969C`. It's very straightforward to understand.

Mosaic ID `6A53A4FB36203CCE` is different from the others because it adds a reference mosaic. This means that to unlock the `fire_spell` the player needs to fulfill 2 requirements.

Example code

```js
const dungeon_master = Account.createFromPrivateKey(privateKey, networkType);

// (non-reference) mosaic global restriction for mosaic ID: 6AACF58D95F016C0 
const mosaicIdHex = '6AACF58D95F016C0'; 
const mosaicId = new Symbol.MosaicId(mosaicIdHex);
const key = Symbol.KeyGenerator.generateUInt64Key('level'.toLowerCase());

const mosaicGlobalRestrictionTransaction = Symbol.MosaicGlobalRestrictionTransaction.create(
    deadline,
    mosaicId, // mosaicId
    key, // restrictionKey
    Symbol.UInt64.fromUint(0), // previousRestrictionValue
    Symbol.MosaicRestrictionType.NONE, // previousRestrictionType
    Symbol.UInt64.fromUint(0), // newRestrictionValue
    Symbol.MosaicRestrictionType.GT, // newRestrictionType
    networkType,
    undefined, // reference Mosaic Id
    maxFee,
  );

// (non-reference) mosaic global restriction for mosaic ID: 73E60A6DD1D16FDD 
... 
...
...

// (non-reference) mosaic global restriction for mosaic ID: 0015F52218DA969C
...
...
...


// (non-reference) mosaic global restriction for mosaic ID: 6A53A4FB36203CCE
const mosaicIdHex = '6A53A4FB36203CCE'; 
const mosaicId = new Symbol.MosaicId(mosaicIdHex);
const key = Symbol.KeyGenerator.generateUInt64Key('fire_spell'.toLowerCase());

const mosaicGlobalRestrictionTransaction = Symbol.MosaicGlobalRestrictionTransaction.create(
    deadline,
    mosaicId, // mosaicId
    key, // restrictionKey
    Symbol.UInt64.fromUint(0), // previousRestrictionValue
    Symbol.MosaicRestrictionType.NONE, // previousRestrictionType
    Symbol.UInt64.fromUint(2), // newRestrictionValue
    Symbol.MosaicRestrictionType.GE, // newRestrictionType
    networkType,
    undefined, // reference Mosaic Id
    maxFee,
  );

// (reference) mosaic global restriction for mosaic ID: 4 with reference mosaic
const mosaicIdHex = '6A53A4FB36203CCE'; 
const mosaicId = new Symbol.MosaicId(mosaicIdHex);
const level_mosaicId = new Symbol.MosaicId('6AACF58D95F016C0');
const key = Symbol.KeyGenerator.generateUInt64Key('level'.toLowerCase());

const mosaicGlobalRestrictionTransaction = Symbol.MosaicGlobalRestrictionTransaction.create(
    deadline,
    mosaicId, // mosaicId
    key, // restrictionKey
    Symbol.UInt64.fromUint(0), // previousRestrictionValue
    Symbol.MosaicRestrictionType.NONE, // previousRestrictionType
    Symbol.UInt64.fromUint(5), // newRestrictionValue
    Symbol.MosaicRestrictionType.EQ, // newRestrictionType
    networkType,
    level_mosaicId, // reference Mosaic Id
    maxFee,
  );
```

#### Create Mosaic Address Restriction Transaction

Phew! We have done the mosaic global restriction, now we need to specify which accounts can transact those mosaics. In our case, there are 2 accounts: the dungeon master and the player.

The dungeon master account must have all qualified the global restriction because it needs to transact mosaic to the player account.

```js
const dungeon_master = Account.createFromPrivateKey(privateKey, networkType);

// Assign mosaic ID: 6AACF58D95F016C0, key: level, value: 10
const mosaicIdHex = '6AACF58D95F016C0';
const mosaicId = new Symbol.MosaicId(mosaicIdHex);
const key = Symbol.KeyGenerator.generateUInt64Key('level'.toLowerCase());

const mosaicAddressRestrictionTransaction = Symbol.MosaicAddressRestrictionTransaction.create(
    deadline,
    mosaicId,
    key, // restrictionKey
    dungeon_master.address,
    Symbol.UInt64.fromUint(10), // newRestrictionValue
    networkType,
    Symbol.UInt64.fromHex('FFFFFFFFFFFFFFFF'), // previousRestrictionValue
    maxFee
);

// Assign mosaic ID: 73E60A6DD1D16FDD, key: punch, value: 1
...
...

// Assign mosaic ID: 0015F52218DA969C, key: kick, value: 2
...
...

// Assign mosaic ID: 6A53A4FB36203CCE, key: fire_spell, value: 2
...
...

```

Let's continue with the player account:

```js
const player = Account.createFromPublicKey(publicKey, networkType);

// Default player level 1
// Assign mosaic ID: 6AACF58D95F016C0, key: level, value: 1
const mosaicIdHex = '6AACF58D95F016C0';
const mosaicId = new Symbol.MosaicId(mosaicIdHex);
const key = Symbol.KeyGenerator.generateUInt64Key('level'.toLowerCase());

const mosaicAddressRestrictionTransaction = Symbol.MosaicAddressRestrictionTransaction.create(
    deadline,
    mosaicId,
    key, // restrictionKey
    player, // Address
    Symbol.UInt64.fromUint(1), // newRestrictionValue
    networkType,
    Symbol.UInt64.fromHex('FFFFFFFFFFFFFFFF'), // previousRestrictionValue
    maxFee
);

// Default player skill
// Assign mosaic ID: 73E60A6DD1D16FDD, key: punch, value: 1
...
...

// Assign mosaic ID: 0015F52218DA969C, key: kick, value: 0
...
...

// Required upgrade
// Assign mosaic ID: 6A53A4FB36203CCE, key: fire_spell, value: 0
...
...

```

#### Initial game

Finally, we have all assets and restrictions set up on the chain.

Dungeon master address stat : [TBRNNSHKSOSSIH3ZUGW3MFVAUZ2TBIHL7NE2FVA](https://sym-test-09.opening-
line.jp:3001/restrictions/mosaic?entryType=0&targetAddress=TBRNNSHKSOSSIH3ZUGW3MFVAUZ2TBIHL7NE2FVA)
Player address stat : [TC7FDHJZ53SK5OFUIL2C6X25KQYBXHBCCWCSJGA](https://sym-test-09.opening-line.jp:3001/restrictions/mosaic?entryType=0&targetAddress=TC7FDHJZ53SK5OFUIL2C6X25KQYBXHBCCWCSJGA)


Let's transfer assets to the player. The system will detect from a player account on-chain and set players `level: 1, punch: active`

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/539995/bd189f94-b7d6-f02e-2eb4-5094d40d6081.png)

#### Game play

Let's imagine that many possibilities will happen in the game, such as players getting affected by `poison`, `exhausted` or `power boosted`. It could affect player stat in the next round.

Players will be rewarded 1 skill point if they win a round, and they can use it to upgrade skills.

There are a lot of things you can think about to make the game more fun!

#### How to upgrade and downgrade skills?

The dungeon master can always update the mosaic/address restrictions to control player stats.

Example: player using skill point to upgrade `kick`.
- Dungeon master can update player address restriction `value 0 -> value 2`
- Dungeon master transfer `mosaicId: 0015F52218DA969C, amount: 1` to player.

Example: player gets the `poison` status and the `punch` skill is disabled.
- Dungeon master revokes `mosaicId: 73E60A6DD1D16FDD, amount: 1` to the player.

##Summary

The mosaics restriction feature is really amazing, there are a lot more things we can do with it.

Setting up on-chain restrictions is such an easy thing on Symbol.

In the beginning it may feel like the restriction feature is complicated, but after you try setting up some of the mosaic restrictions, you will feel it's actually straightforward and easy to understand.

I hope this article will help you understand more about mosaic restriction, and how to use it in your game.

I'm not a game developer, but I definitely love to play games! If you found this idea is cool and you would like to build on it, let me know and we can discuss more in Discord.

If you would like to know more about Symbol, please join the [Discord](https://discord.com/invite/xymcity).

Special thanks to Captain [Jaguar](https://twitter.com/Jaguar0625) [Gimre](https://twitter.com/NCOSIGIMCITYNRE) [Hatchet](https://twitter.com/0x6861746366574) for provided feedback and help.
Special thanks to [Xavi](https://twitter.com/sigsegvx) for reviewing this article.