Solidity Exercise - Inheritance/NestedArray/...

Posted by Bourne's Blog - A Full-stack & Web3 Developer on May 15, 2024

Inheritance Override

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

// You may not modify this contract
contract Number {
    function getNumber() public view virtual returns (uint256) {
        return 3;
    }
}

contract InheritanceOverride is Number {
    /*
        This exercise assumes you know how overriding a function works.
        1. Override `getNumber` function in the parent contract.
        2. `getNumber` function should return uint 10.
    */
    
    // prettier-ignore
    // your code here
}

Coding

Copy the getNumber function, and change the virtual to override, then return the new number.

1
2
3
    function getNumber() public view override returns(uint256){
        return 10;
    }

Test

1
2
3
4
5
6
7
8
9
➜  InheritanceOverride git:(main) ✗ forge test -vvv
[⠊] Compiling...
No files changed, compilation skipped

Ran 1 test for test/InheritanceOverride.t.sol:InheritanceOverrideTest
[PASS] testGetNumber() (gas: 5436)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 5.37ms (388.04µs CPU time)

Ran 1 test suite in 191.73ms (5.37ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)

Reference: Solidity Exercise - InheritanceOverride

Insert In Array

This function is to be used to set/override the value stored at particular index in the array above (named “arr”)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

contract InsertInArray {
    uint256[10] public arr = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19];

    /**
     * This function is to be used to set/override the value stored at particular index in the array above (named "arr")
     */
    function replaceAtIndex(uint256 index, uint256 newValue) public {
        // your code here
        arr[index] = newValue;
    }
}

Test

➜  InsertInArray git:(main) ✗ forge test -vvv    
[⠊] Compiling...
[⠘] Compiling 19 files with Solc 0.8.25
[⠃] Solc 0.8.25 finished in 818.30ms
Compiler run successful!

Ran 1 test for test/InsertInArray.t.sol:InsertInArrayTest
[PASS] testReplaceAtIndex() (gas: 24936)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 5.37ms (278.08µs CPU time)

Ran 1 test suite in 195.97ms (5.37ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)

Reference: Solidity Exercise - InsertInArray

Is Prime

The goal of this exercise is to return if “number” is prime or not (true or false)

1
2
3
4
5
6
7
8
9
10
11
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

contract IsPrime {
    /**
     * The goal of this exercise is to return if "number" is prime or not (true or false)
     */
    function isPrime(uint256 number) public view returns (bool) {
        // your code here
    }
}

Problem Analysis

Let check the definition of prime number. “a whole number greater than 1 that cannot be exactly divided by any whole number other than itself and 1 (e.g. 2, 3, 5, 7, 11).”

For a given number n, we should check each number i between 2 - (n-1), divide the n by i, if the mod is 0, it’s not a prime number. otherwise, it’s a prime number.

Coding

1
2
3
4
5
6
7
8
9
    function isPrime(uint256 number) public pure returns (bool) {
        // your code here
        if (number < 2) return false;
        if (number == 2) return true;
        for (uint i = 2; i < number; i++) {
            if (number % i == 0) return false;
        }
        return true;
    }

Test

1
2
3
4
5
6
7
8
9
➜  IsPrime git:(main) ✗ forge test -vvv
[⠊] Compiling...
No files changed, compilation skipped

Ran 1 test for test/IsPrime.t.sol:IsPrimeTest
[PASS] testIsPrime() (gas: 28500)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 5.59ms (483.04µs CPU time)

Ran 1 test suite in 189.02ms (5.59ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)

Reference: Solidity Exercise - IsPrime

Is Sorted

The goal of this exercise is to return true if the members of “arr” is sorted (in ascending order) or false if its not.

1
2
3
4
5
6
7
8
9
10
11
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

contract IsSorted {
    /**
     * The goal of this exercise is to return true if the members of "arr" is sorted (in ascending order) or false if its not.
     */
    function isSorted(uint256[] calldata arr) public view returns (bool) {
        // your code here
    }
}

Coding

1
2
3
4
5
6
7
    function isSorted(uint256[] calldata arr) public view returns (bool) {
        // your code here
        for (uint256 i = 0; i < arr.length; i++) {
            if ((i+1) < arr.length && arr[i] > arr[i+1]) return false;
        }
        return true;
    }

Test

1
2
3
4
5
6
7
8
9
➜  IsSorted git:(main) ✗ forge test -vvv
[⠊] Compiling...
No files changed, compilation skipped

Ran 1 test for test/IsSorted.t.sol:IsSortedTest
[PASS] testIsSorted() (gas: 15044)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 411.33µs (91.33µs CPU time)

Ran 1 test suite in 178.44ms (411.33µs CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)

Reference: Solidity Exercise - IsSorted

Mean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

contract Mean {
    /**
     * The goal of this exercise is to return the mean of the numbers in "arr"
     */
    function mean(uint256[] calldata arr) public view returns (uint256) {
        // your code here
        uint256 sum = 0;
        for(uint32 i=0; i<arr.length; i++){
            sum += arr[i];
        }
        return sum / arr.length;
    }
}

Reference: Solidity Exercise - Mean

MultiInheritance

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

contract X {
    uint256 public constant x = 42;
}

contract Y {
    uint256 public constant y = 24;
}

contract MultiInheritance {
    /**
     * The goal of this exercise is to use the functionality of contracts X and Y without pasting their code here or making an external call or delegate call
     */
    uint256 public x;
    uint256 public y;
    constructor(){
        X contractX = new X();
        Y contractY = new Y();
        x = contractX.x();
        y = contractY.y();
    }
}

Reference: Solidity Exercise - MultiInheritance

NestedArray

  • This function is expected to get the sum of all members of each nested array and finally return the sum of all the nested sums
  • Example: [[1,2], [3,4]] this should return 1 + 2 + 3 + 4 = 10

Coding

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

contract NestedArray {
    uint256[][] public arr;

    function setArr(uint256[][] memory newArr) public {
        arr = newArr;
    }

    /**
     * This function is expected to get the sum of all members of each nested array and finally return the sum of all the nested sums
     * Example: [[1,2], [3,4]] this should return 1 + 2 + 3 + 4 = 10
     */
    function getNestedSum() public view returns (uint256) {
        // your code here
        uint256 sum = 0;
        for (uint256 i=0; i < arr.length; i++){
            for (uint256 j=0; j < arr[i].length; j++){
                sum += arr[i][j];
            }
        }

        return sum;
    }
}

Test

1
2
3
4
5
6
7
8
9
10
11
➜  NestedArray git:(main) ✗ forge test -vvv
[⠊] Compiling...
[⠃] Compiling 19 files with Solc 0.8.25
[⠊] Solc 0.8.25 finished in 894.59ms
Compiler run successful!

Ran 1 test for test/NestedArray.t.sol:NestedArrayTest
[PASS] testGetNestedSum() (gas: 1530132)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.56ms (409.38µs CPU time)

Ran 1 test suite in 179.81ms (1.56ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)

Reference: Solidity Exercise - NestedArray

NestedMapping

This exercise assumes you know how nested mappings work.

  • Create a public nested mapping of (address => uint256 => bool).
  • The name of the mapping must be nestedBool
  • Set the boolean value of the arguments to true in the ‘setNestedDetails’ function
  • use the ‘returnNestedDetails’ function to return the values of a nested data
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

contract NestedMapping {
    /* This exercise assumes you know how nested mappings work.
        1. Create a public nested mapping of (address => uint256 => bool).
        2. The name of the mapping must be `nestedBool`
        3. Set the boolean value of the arguments to `true` in the 'setNestedDetails' function
        4. use the 'returnNestedDetails' function to return the values of a nested data
    */

    function setNestedDetails(address _addr, uint256 _num) public {
        // your code here
    }

    function returnNestedDetails(
        address _addr,
        uint256 _num
    ) public view returns (bool) {
        // your code here
    }
}

Coding

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    mapping(address => mapping(uint256 => bool)) nestedBool;

    function setNestedDetails(address _addr, uint256 _num) public {
        // your code here
        nestedBool[_addr][_num] = true;
    }

    function returnNestedDetails(
        address _addr,
        uint256 _num
    ) public view returns (bool) {
        // your code here
        return nestedBool[_addr][_num];
    }

Test

1
2
3
4
5
6
7
8
9
10
11
➜  NestedMapping git:(main) ✗ forge test -vvv    
[⠊] Compiling...
[⠘] Compiling 19 files with Solc 0.8.25
[⠃] Solc 0.8.25 finished in 804.34ms
Compiler run successful!

Ran 1 test for test/NestedMapping.t.sol:NestedMappingTest
[PASS] testNestedDetails() (gas: 77580)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 5.18ms (294.58µs CPU time)

Ran 1 test suite in 199.34ms (5.18ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)

Reference: Solidity Exercise - NestedMapping

Require

This exercise assumes you know how require statements works.

1
2
3
4
5
6
7
8
9
10
11
12
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

contract NotEnough {
    /* This exercise assumes you know how require statements works.
    The function below should only be called with a number greater than 10,
    else it reverts. */
    function largeEnough(uint256 _number) external pure {
        // add code here
        require(_number > 10, "_number should greater than 10");
    }
}

OneWeekLockUp

  • In this exercise you are expected to create functions that let users deposit ether
  • Users can also withdraw their ether (not more than their deposit) but should only be able to do a week after their last deposit
  • Consider edge cases by which users might utilize to deposit ether

Coding

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;
import "forge-std/console.sol";

contract OneWeekLockup {
    /**
     * In this exercise you are expected to create functions that let users deposit ether
     * Users can also withdraw their ether (not more than their deposit) but should only be able to do a week after their last deposit
     * Consider edge cases by which users might utilize to deposit ether
     *
     * Required function
     * - depositEther()
     * - withdrawEther(uint256 )
     * - balanceOf(address )
     */

    mapping(address => uint256) userBalance;
    mapping(address => uint256) lastDeposit;

    function balanceOf(address user) public view returns (uint256) {
        // return the user's balance in the contract
        return userBalance[user];
    }

    function depositEther() external payable {
        /// add code here
        userBalance[msg.sender] += msg.value;
        lastDeposit[msg.sender] = block.timestamp;
    }

    function withdrawEther(uint256 amount) external {
        /// add code here
        console.log("last deposit: ", lastDeposit[msg.sender]);
        console.log("last deposit + 1 weeks: ", lastDeposit[msg.sender] + 1 weeks);
        console.log("timestamp: ", block.timestamp);
        require(amount <= userBalance[msg.sender], "widthdraw amount should not greater than balance");
        require(block.timestamp >= (lastDeposit[msg.sender] + 1 weeks), "should only be able to widthdraw a week after their last deposit");

        payable(msg.sender).transfer(amount);
    }
}

Reference: Solidity Exercise - OneWeekLockup

Price is Right

1
2
3
4
5
6
7
8
9
10
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

contract PriceIsRight {
    /// @dev make this function revert unless exactly 1 ether is sent as value in the function call
    function buy() public payable {
        /// your code here
        require(msg.value == 1 ether);
    }
}

Receive

1
2
3
4
5
6
7
8
9
10
11
12
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

contract Receive {
    // solidity smart contracts cannot receive
    // ether by default. They need a receive
    // function. See here:
    // https://docs.soliditylang.org/en/v0.8.17/contracts.html#receive-ether-function
    function receive() external payable{

    }
}

Test

1
2
3
4
5
6
7
8
9
10
11
➜  Receive git:(main) ✗ forge test -vvv
[⠊] Compiling...
[⠘] Compiling 19 files with Solc 0.8.25
[⠃] Solc 0.8.25 finished in 803.68ms
Compiler run successful with warnings:
Warning (3445): This function is named "receive" but is not the receive function of the contract. If you intend this to be a receive function, use "receive(...) { ... }" without the "function" keyword to define it.
 --> src/Receive.sol:9:14:
  |
9 |     function receive() external payable{
  |              ^^^^^^^

The receive doesn’t need a function prefix.

1
2
3
    receive() external payable{

    }

Test again.

1
2
3
4
5
6
7
8
9
10
11
➜  Receive git:(main) ✗ forge test -vvv
[⠊] Compiling...
[⠘] Compiling 19 files with Solc 0.8.25
[⠃] Solc 0.8.25 finished in 782.91ms
Compiler run successful!

Ran 1 test for test/Receive.t.sol:DonationsTest
[PASS] testDonate() (gas: 15044)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 5.54ms (660.25µs CPU time)

Ran 1 test suite in 192.03ms (5.54ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)

Reference: Solidity Exercise - Receive

Self Destroyer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

contract SelfDestroyer {
    /* This exercise assumes you know the selfdestruct function works.
        1. The contract has some ether in it, 
        destroy the contract and send all ether to an address 
        2. The name of the function must be `destroy`,
        that takes a non-payable address input
    */

    constructor() payable {}

    function destroy(address taker) external {
        // your code here
        selfdestruct(payable(taker));
    }

    function getBalance() public view returns (uint256 balance) {
        balance = address(this).balance;
    }
}

Test

1
2
3
4
5
6
7
8
9
10
11
12
13
➜  SelfDestroyer git:(main) ✗ forge test -vvv
[⠊] Compiling...
No files changed, compilation skipped

Ran 1 test for test/SelfDestroyer.t.sol:SelfDestroyerTest
[PASS] testSelfDestroyer() (gas: 15205)
Logs:
  Self Destroyer balance before: 10000000000000000000
  Self Destroyer balance after: 0

Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 280.04µs (41.75µs CPU time)

Ran 1 test suite in 135.32ms (280.04µs CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)

Stack

This exercise assumes you understand what a stack is and know how to manipulate it. Implement a stack using an array stack with the following functions:

  • a. push function appends a new value to the end of the array.
  • b. peek function returns the last element of the array without removing it, but first checks if the stack is not empty.
  • c. pop function returns the last element of the array and removes it from the stack, but also checks if the stack is not empty.
  • d. size function returns the length of the array, which corresponds to the number of elements in the stack.
  • e. getStack function returns the stack.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;
import "forge-std/console.sol";
contract Stack {
    /*
        This exercise assumes you understand what a stack is and know how to manipulate it.
        1. Implement a stack using an array `stack` with the following functions:
            a. `push` function appends a new value to the end of the array.
            b. `peek` function returns the last element of the array without removing it, 
               but first checks if the stack is not empty.
            c. `pop` function returns the last element of the array and removes it from 
               the stack, but also checks if the stack is not empty.
            d. `size` function returns the length of the array, which corresponds to
               the number of elements in the stack.
            e. `getStack` function returns the stack.
    */

    uint256[] stack;

    constructor(uint256[] memory _stack) {
        stack = _stack;
    }

    // your code here
}

Coding

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
    function push(uint256 n) public {
        stack.push(n);
        console.log("length of array: ", stack.length);
    }

    function peek() public returns(uint256 e){
        e = stack[stack.length-1];
        console.log("length of array: ", stack.length);
    }

    function pop() public returns(uint256 e){
        e = stack[stack.length-1];
        stack.pop();
    }

    function size() public returns(uint256 s) {
        s = stack.length;
    }

    function getStack() public returns(uint256[] memory){
        return stack;
    }

Test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
➜  Stack git:(main) ✗ forge test -vvv
[⠊] Compiling...
[⠑] Compiling 19 files with Solc 0.8.25
[⠘] Solc 0.8.25 finished in 662.92ms
Compiler run successful with warnings:
Warning (2018): Function state mutability can be restricted to view
  --> src/Stack.sol:30:5:
   |
30 |     function peek() public returns(uint256 e){
   |     ^ (Relevant source part starts here and spans across multiple lines).

Warning (2018): Function state mutability can be restricted to view
  --> src/Stack.sol:40:5:
   |
40 |     function size() public returns(uint256 s) {
   |     ^ (Relevant source part starts here and spans across multiple lines).

Warning (2018): Function state mutability can be restricted to view
  --> src/Stack.sol:44:5:
   |
44 |     function getStack() public returns(uint256[] memory){
   |     ^ (Relevant source part starts here and spans across multiple lines).


Ran 4 tests for test/Stack.t.sol:StackTest
[PASS] testPeek() (gas: 22071)
Logs:
  length of array:  4

[PASS] testPop() (gas: 22704)
[PASS] testPush() (gas: 47119)
Logs:
  length of array:  5

[PASS] testSize() (gas: 7614)
Suite result: ok. 4 passed; 0 failed; 0 skipped; finished in 5.05ms (2.47ms CPU time)

Ran 1 test suite in 156.85ms (5.05ms CPU time): 4 tests passed, 0 failed, 0 skipped (4 total tests)

Student

This exercise assumes you know how structs works.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

contract StudentDB {
    /* This exercise assumes you know how structs works.
   create a struct `Student` for a student who's name is `John` and age is `12`.
   Return John's age in the function below */

    struct Student {
        string name;
        uint256 age;
    }
    Student student;

    constructor() {
        createStudent("John", 12);
    }

    // create Student's data
    function createStudent(string memory _name, uint256 _age) public {
        // your code here
        student = Student(_name, _age);
    }

    // return struct data
    function getEntireStruct() public view returns (Student memory) {
        // your code here
        return student;
    }
}

Sum Array

Return the sum of an array.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

contract SumArray {
    function sumArray(uint256[] calldata arr) public pure returns (uint256) {
        // your code here
        // arr is a list of unsigned integers
        // return the sum of them. If the array
        // is empty, return 0
        if (arr.length < 1) return 0;
        uint256 sum = 0;
        for (uint256 i = 0; i < arr.length; i++) {
            sum += arr[i];
        }
        return sum;
    }
}

Test

1
2
3
4
5
6
7
8
9
10
11
➜  SumArray git:(main) ✗ forge test -vvv
[⠊] Compiling...
[⠒] Compiling 19 files with Solc 0.8.25
[⠑] Solc 0.8.25 finished in 598.69ms
Compiler run successful!

Ran 1 test for test/SumArray.t.sol:SumArrayTest
[PASS] testSumArray() (gas: 10630)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 4.69ms (274.79µs CPU time)

Ran 1 test suite in 142.47ms (4.69ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)

TicTacToe

This exercise assumes you know how to manipulate nested array.

  1. This contract checks if TicTacToe board is winning or not.
  2. Write your code in isWinning function that returns true if a board is winning or false if not.
  3. Board contains 1’s and 0’s elements and it is also a 3x3 nested array.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.13;

contract TicTacToe {
    /* 
        This exercise assumes you know how to manipulate nested array.
        1. This contract checks if TicTacToe board is winning or not.
        2. Write your code in `isWinning` function that returns true if a board is winning
           or false if not.
        3. Board contains 1's and 0's elements and it is also a 3x3 nested array.
    */

    function isWinning(uint8[3][3] memory board) public pure returns (bool) {
        // your code here
    }
}

Problem Analysis

Every element in a row or a column or a diagonal are same, you win. First we define a private function to check if a row/column/diagonal wins(has three 1 in it). Then we check if each row/column/diagonal wins.

Coding

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
    function isWinning(uint8[3][3] memory board) public pure returns (bool) {
        // your code here
        if (checkWin(board[0]) ||
            checkWin(board[1]) ||
            checkWin(board[2]) ||

            checkWin([board[0][0], board[1][0], board[2][0]]) ||
            checkWin([board[0][1], board[1][1], board[2][1]]) || 
            checkWin([board[0][2], board[1][2], board[2][2]]) ||

            checkWin([board[0][0], board[1][1], board[2][2]]) ||
            checkWin([board[0][2], board[1][1], board[2][0]]) 
            ) {
                return true;
        }

        return false;

    }

    function checkWin(uint8[3] memory arr) private pure returns(bool) {
        uint8 sum = 0;
        for (uint8 i =0; i<arr.length; i++) {
            sum += arr[i];
        }

        return sum == 3;
    }

Test

1
2
3
4
5
6
7
8
9
10
11
12
➜  TicTacToe git:(main) ✗ forge test -vvv
[⠊] Compiling...
[⠒] Compiling 19 files with Solc 0.8.25
[⠘] Solc 0.8.25 finished in 610.56ms
Compiler run successful!

Ran 2 tests for test/TicTacToe.t.sol:TicTacToeTest
[PASS] testIsLosing() (gas: 17766)
[PASS] testIsWinning() (gas: 16646)
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 4.50ms (596.79µs CPU time)

Ran 1 test suite in 145.24ms (4.50ms CPU time): 2 tests passed, 0 failed, 0 skipped (2 total tests)

Reference: Solidity Exercise - TicTacToe