Skip to content

Commit

Permalink
Merge pull request #115 from massalabs/114-update-tictactoe-contracts
Browse files Browse the repository at this point in the history
update tictactoe contracts
  • Loading branch information
Ben-Rey authored Jul 6, 2023
2 parents fe2b51c + fe23c9e commit 8863b9e
Show file tree
Hide file tree
Showing 28 changed files with 18,487 additions and 187 deletions.
26 changes: 26 additions & 0 deletions .github/workflows/tictactoe-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Tictactoe CI

on: [push]

defaults:
run:
working-directory: ./games/tictactoe/contracts

jobs:
unit-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- uses: actions/setup-node@v3
with:
node-version: 18

- name: Install
run: npm ci

- name: Code quality
run: npm run fmt:check

- name: Test
run: npm run test
43 changes: 43 additions & 0 deletions .github/workflows/tictactoe-daily-deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Tictactoe Daily Deploy

on:
push:
paths:
- games/tictactoe/**
workflow_dispatch:
schedule:
- cron: "0 7 * * *"

jobs:
deploy-age:
defaults:
run:
working-directory: ./games/tictactoe/contracts/

runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Install node
uses: actions/setup-node@v3
with:
node-version: "18"
cache: "npm"
cache-dependency-path: ./games/tictactoe/contracts/package-lock.json

- name: Install dependencies
run: npm install

- name: Deploy
run: |
if npm run deploy | grep -q "Contract deployed"; then
echo "Contract successfully deployed!"
else
echo "Failed to deploy contract ..."
exit 1
fi
env:
JSON_RPC_URL_PUBLIC: ${{ secrets.JSON_RPC_URL_PUBLIC }}
WALLET_PRIVATE_KEY: ${{ secrets.WALLET_PRIVATE_KEY }}
2 changes: 1 addition & 1 deletion games/tictactoe/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

This repository contains a basic example of a dApp game, namely a decentralized Tictactoe.

You'll find in [smart-contract](https://github.com/massalabs/massa-sc-examples/tree/main/games/tictactoe/smart-contract)
You'll find in [contracts](https://github.com/massalabs/massa-sc-examples/tree/main/games/tictactoe/contracts)
the smart-contract logic used to implement the game and in the
[html](https://github.com/massalabs/massa-sc-examples/tree/main/games/tictactoe/html) folder the front-end used to
interact with the smart-contract developed using the [massa-web3](https://github.com/massalabs/massa-web3) library.
3 changes: 3 additions & 0 deletions games/tictactoe/contracts/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
WALLET_PRIVATE_KEY=""

JSON_RPC_URL_PUBLIC=https://buildnet.massa.net/api/v2:33035
6 changes: 6 additions & 0 deletions games/tictactoe/contracts/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
extends: [
'@massalabs',
],
};

File renamed without changes.
18 changes: 18 additions & 0 deletions games/tictactoe/contracts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Tic-tac-toe smart-contract

You'll find in this repository the tic-tac-toe smart contracts.

## Smart contracts

- [contracts/assembly/contract/tictactoe](https://github.com/massalabs/massa-sc-examples/tree/main/games/tictactoe/contracts/assembly/contracts/tictactoe.ts)
contains the tic-tac-toe logic ("game engine").
- [contracts/assembly/contract/main](https://github.com/massalabs/massa-sc-examples/tree/main/games/tictactoe/contracts/assembly/contracts/main.ts)
is used to deploy the tic-tac-toe smart contract for each game.

## Build:

The following command will build your contract. It assumes your contract entrypoint is `assembly/main.ts`

```shell
npm run build
```
24 changes: 24 additions & 0 deletions games/tictactoe/contracts/as-pect.asconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"targets": {
"coverage": {
"lib": ["./node_modules/@as-covers/assembly/index.ts"],
"transform": ["@as-covers/transform", "@as-pect/transform"]
},
"noCoverage": {
"transform": ["@as-pect/transform"]
}
},
"options": {
"exportMemory": true,
"outFile": "output.wasm",
"textFile": "output.wat",
"bindings": "raw",
"exportStart": "_start",
"exportRuntime": true,
"use": ["RTRACE=1"],
"debug": true,
"exportTable": true
},
"extends": "./asconfig.json",
"entries": ["./node_modules/@as-pect/assembly/assembly/index.ts"]
}
28 changes: 28 additions & 0 deletions games/tictactoe/contracts/as-pect.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import createMockedABI from '@massalabs/massa-as-sdk/vm-mock';

export default {
/**
* A set of globs passed to the glob package that qualify typescript files for testing.
*/
entries: ['assembly/__tests__/**/*.spec.ts'],
/**
* A set of globs passed to the glob package that quality files to be added to each test.
*/
include: ['assembly/__tests__/**/*.include.ts'],
/**
* A set of regexp that will disclude source files from testing.
*/
disclude: [/node_modules/],
/**
* Add your required AssemblyScript imports here.
*/
async instantiate(memory, createImports, instantiate, binary) {
return createMockedABI(memory, createImports, instantiate, binary);
},
/** Enable code coverage. */
// coverage: ["assembly/**/*.ts"],
/**
* Specify if the binary wasm file should be written to the file system.
*/
outputBinary: false,
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
{
"entries": ["assembly/main.ts"],
"targets": {
"debug": {
"outFile": "build/main.wasm",
Expand All @@ -14,5 +13,9 @@
"converge": false,
"noAssert": false
}
},
"options": {
"exportRuntime": true,
"bindings": "esm"
}
}
1 change: 1 addition & 0 deletions games/tictactoe/contracts/assembly/__tests__/as-pect.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/// <reference types="@as-pect/assembly/types/as-pect" />
37 changes: 37 additions & 0 deletions games/tictactoe/contracts/assembly/__tests__/tictactoe.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Args } from '@massalabs/as-types';
import { play } from '../contracts/tictactoe';
import { resetStorage, Storage } from '@massalabs/massa-as-sdk';

describe('Check tictactoe', () => {
beforeEach(() => {
resetStorage(); // We make sure that the contract's storage is empty before each test.
});

afterAll(() => {
resetStorage(); // We make sure that the contract's storage is reset.
});

test('Check if the game is won', () => {
// mock constructor
Storage.set('currentPlayer', 'X');
Storage.set('gameState', 'n,n,n,n,n,n,n,n,n');
Storage.set('gameWinner', 'n');

const args = new Args().add(0).serialize();
play(args);

const args2 = new Args().add(8).serialize();
play(args2);

const args3 = new Args().add(1).serialize();
play(args3);

const args4 = new Args().add(7).serialize();
play(args4);

const args5 = new Args().add(2).serialize();
play(args5); // X should win here

expect(Storage.get('gameWinner').toString()).toBe('X');
});
});
39 changes: 39 additions & 0 deletions games/tictactoe/contracts/assembly/contracts/run.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {
Address,
call,
callerHasWriteAccess,
Storage,
} from '@massalabs/massa-as-sdk';
import { Args } from '@massalabs/as-types';

/**
* This function is meant to be called only one time: when the contract is deployed.
*
* @param tictactoeAddress -The address of the smart contract deployed in the previous step
*/
export function constructor(
tictactoeAddress: StaticArray<u8>,
): StaticArray<u8> {
// This line is important. It ensures that this function can't be called in the future.
// If you remove this check, someone could call your constructor function and reset your smart contract.
if (!callerHasWriteAccess()) {
return [];
}
const args = new Args(tictactoeAddress);
Storage.set(
'address',
args.nextString().expect('Argument address is missing or invalid'),
);
main([]);
return [];
}

/**
* @param _ - not used
* @returns empty array
*/
export function main(_: StaticArray<u8>): StaticArray<u8> {
const address = new Address(Storage.get('address'));
call(address, 'play', new Args().add(u32(3)), 500000);
return [];
}
86 changes: 86 additions & 0 deletions games/tictactoe/contracts/assembly/contracts/tictactoe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { Storage, generateEvent } from '@massalabs/massa-as-sdk';
import { Args } from '@massalabs/as-types';
import { isDeployingContract } from '@massalabs/massa-as-sdk/assembly/std/context';

/**
* This function is meant to be called only one time: when the contract is deployed.
*
* @param _ - unused
*/
export function constructor(_: StaticArray<u8>): StaticArray<u8> {
// This line is important. It ensures that this function can't be called in the future.
// If you remove this check, someone could call your constructor function and reset your smart contract.
if (!isDeployingContract()) {
return [];
}
Storage.set('currentPlayer', 'X');
Storage.set('gameState', 'n,n,n,n,n,n,n,n,n');
Storage.set('gameWinner', 'n');
return [];
}

export function play(binaryArgs: StaticArray<u8>): void {
let args = new Args(binaryArgs);
let index = args.nextU32().unwrap();
let gameWinner = Storage.get('gameWinner');
if (gameWinner == 'n') {
let player = Storage.get('currentPlayer');
let gameState = Storage.get('gameState');
let vecGameState = gameState.split(',');

if (vecGameState[index] == 'n') {
vecGameState[index] = player;
Storage.set('gameState', vecGameState.join());
if (player == 'X') {
Storage.set('currentPlayer', 'O');
} else {
Storage.set('currentPlayer', 'X');
}
_checkWin(player);
} else {
generateEvent('Invalid move or case already played');
}
}
}

function _checkWin(player: string): void {
const winningConditions = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6],
];

let gameState = Storage.get('gameState');
let vecGameState = gameState.split(',');

let roundWon = false;
for (let i = 0; i <= 7; i++) {
const winCondition = winningConditions[i];
let a = vecGameState[winCondition[0]];
let b = vecGameState[winCondition[1]];
let c = vecGameState[winCondition[2]];
if (a == 'n' || b == 'n' || c == 'n') {
continue;
}
if (a == b && b == c) {
roundWon = true;
break;
}
}

if (roundWon) {
generateEvent(player + ' player has won round');
Storage.set('gameWinner', player);
}

let roundDraw = !vecGameState.includes('n');
if (roundDraw) {
generateEvent('round resulted in a draw');
Storage.set('gameWinner', 'draw');
}
}
14 changes: 14 additions & 0 deletions games/tictactoe/contracts/assembly/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"extends": "assemblyscript/std/assembly.json",
"include": [
"**/*.ts"
],
"typedocOptions": {
"exclude": "assembly/**/__tests__",
"excludePrivate": true,
"excludeProtected": true,
"excludeExternals": true,
"includeVersion": true,
"skipErrorChecking": true
}
}
Loading

0 comments on commit 8863b9e

Please sign in to comment.