How we made the game “Rock - Scissors - Paper” on the Ethereum blockchain. Part 2 Technical

    Taking into account the comments on my previous article, I decided to write the second part, where the technical component of the game will be considered in more detail.

    So, let's begin. We made the client part in javascript using the nodejs Meteor framework. The only server solution in the game is chat on mongoDB. Look at the algorithm of the game we had in mind before starting work:



    Description of the smart contract. There are no blanks or template options for creating the game “Stone, scissors, paper” on the blockchain. To do this, we conducted our own research and development. Our smart contract allows you to create and close gaming tables. Information about all the tables is contained in the memory of the smart contract.

    struct room
    {
        address player1;
        address player2;
        bytes32 bit1;
        bytes32 bit2;
        uint8 res1;
        uint8 res2;
        uint256 bet;
        uint8 counter;
        uint8 c1;
        uint8 c2;
        uint roomTime;
        uint startTime;
        bool open;
        bool close;
    }
    mapping (uint => room) rooms;

    As you can see, we are creating a mapping (associative array) of rooms, the keys of which are the table numbers, and the values ​​are the table object. In the table object, we declare a space for the addresses of the players and their moves. The table also stores the total score of the match, the number of victories on each side, and the time stamps for creating the table and the start of the game. The table number is randomly generated by the client part. This number is from 0 to 99999999. The same number can be used many times, provided that the created table is closed normally. Overlays with table numbers can occur extremely rarely, but even in this case, this will not interfere with the operation of the smart contract, the transaction to create a duplicate simply cannot take place. If the table is not closed normally, the used number for some time will fall out of the possible options for creating the next table. According to the logic of a smart contract, an open table has a period of relevance of one knock. After that, any action on the table leads to its closure, including an attempt to connect anyone to it. Closing the table is due to the revertRoom function and then closeRoom, which removes information about the table from the associative array of rooms.

    function revertRoom(uint id) internal {
        if( rooms[id].bet > 0 ) {
            rooms[id].player1.transfer(rooms[id].bet);
            if( rooms[id].player2 != 0x0 ) rooms[id].player2.transfer(rooms[id].bet);
        }
        RevertRoom(id);
        closeRoom(id);
    }
    function closeRoom(uint id) internal {
        rooms[id].close = true;
        RoomClosed( id );
        delete rooms[id];
    }

    Functions of a smart contract that begin with a capital letter are events. The gameplay in the client part relies heavily on listening to certain events related to the current table. We have created 14 events:

    event RoomOpened( uint indexed room, address indexed player1, uint256 bet, uint8 counter, uint openedTime, bool indexed privat );
    event RoomClosed( uint indexed room );
    event JoinPlayer1( uint indexed room, address indexed player1 );
    event JoinPlayer2( uint indexed room, address indexed player2, uint countdownTime );
    event BetsFinished(uint indexed room );
    event BetsAdd(address indexed from, uint indexed room );
    event OneMoreGame(uint indexed room );
    event SeedOpened( uint indexed room );
    event RoundFinished( uint indexed room, uint8 res1, uint8 res2 );
    event Revard(address win, uint256 amount, uint indexed room );
    event Winner(address win, uint indexed room );
    event Result(address indexed player, uint8 r,  uint indexed room );
    event RevertRoom(uint indexed room);
    event ScoreChanged(uint indexed room, uint8 score1, uint8 score2);

    Marking indexed after declaring a variable type allows you to filter events by the specified field. Listening to events on the blockchain from the client side is as follows (We use coffee script, it can be converted in the javascript service https://js2.coffee ):

    #Слушаем победителя
    this.autorun =>
    filter5 = contractInstance.Winner {room: Number(FlowRouter.getParam('id')),  }, {fromBlock:0, toBlock: 'latest', address: contrAdress}
    filter5.watch (error, result) ->
    console.log result
    if result
        instance.winner.set result.args.win
    console.log result.args.win
    UIkit.modal("#modal-winner").show()

    As you can see, we are referring to an event on the blockchain called Winner, having previously prepared the image of the smart contract constactInstance. Web3 setup and image preparation is carried out by the following script:

    Longcode
    if (typeof web3 !== 'undefined') {
        web3 = new Web3(web3.currentProvider);
        var contrAdress = '0x80dd7334a28579a9e96601573555db15b7fe523a';
        var contrInterface = [
            {
                "anonymous": false,
                "inputs": [
                    {
                        "indexed": true,
                        "name": "room",
                        "type": "uint256"
                    },
                    {
                        "indexed": false,
                        "name": "score1",
                        "type": "uint8"
                    },
                    {
                        "indexed": false,
                        "name": "score2",
                        "type": "uint8"
                    }
                ],
                "name": "ScoreChanged",
                "type": "event"
            },
            {
                "constant": false,
                "inputs": [],
                "name": "deleteContract",
                "outputs": [],
                "payable": false,
                "stateMutability": "nonpayable",
                "type": "function"
            },
            {
                "constant": false,
                "inputs": [
                    {
                        "name": "id",
                        "type": "uint256"
                    }
                ],
                "name": "exitRoom",
                "outputs": [],
                "payable": false,
                "stateMutability": "nonpayable",
                "type": "function"
            },
            {
                "constant": false,
                "inputs": [
                    {
                        "name": "id",
                        "type": "uint256"
                    }
                ],
                "name": "fixResults",
                "outputs": [],
                "payable": false,
                "stateMutability": "nonpayable",
                "type": "function"
            },
            {
                "constant": false,
                "inputs": [
                    {
                        "name": "id",
                        "type": "uint256"
                    }
                ],
                "name": "fixTimerResults",
                "outputs": [],
                "payable": false,
                "stateMutability": "nonpayable",
                "type": "function"
            },
            {
                "constant": false,
                "inputs": [
                    {
                        "name": "id",
                        "type": "uint256"
                    }
                ],
                "name": "joinRoom",
                "outputs": [
                    {
                        "name": "",
                        "type": "uint256"
                    }
                ],
                "payable": true,
                "stateMutability": "payable",
                "type": "function"
            },
            {
                "constant": false,
                "inputs": [
                    {
                        "name": "id",
                        "type": "uint256"
                    },
                    {
                        "name": "count",
                        "type": "uint8"
                    },
                    {
                        "name": "privat",
                        "type": "bool"
                    }
                ],
                "name": "newRoom",
                "outputs": [
                    {
                        "name": "",
                        "type": "uint256"
                    }
                ],
                "payable": true,
                "stateMutability": "payable",
                "type": "function"
            },
            {
                "constant": false,
                "inputs": [
                    {
                        "name": "id",
                        "type": "uint256"
                    },
                    {
                        "name": "bet",
                        "type": "bytes32"
                    }
                ],
                "name": "setBet",
                "outputs": [],
                "payable": false,
                "stateMutability": "nonpayable",
                "type": "function"
            },
            {
                "anonymous": false,
                "inputs": [
                    {
                        "indexed": true,
                        "name": "room",
                        "type": "uint256"
                    }
                ],
                "name": "OneMoreGame",
                "type": "event"
            },
            {
                "anonymous": false,
                "inputs": [
                    {
                        "indexed": true,
                        "name": "player",
                        "type": "address"
                    },
                    {
                        "indexed": false,
                        "name": "r",
                        "type": "uint8"
                    },
                    {
                        "indexed": true,
                        "name": "room",
                        "type": "uint256"
                    }
                ],
                "name": "Result",
                "type": "event"
            },
            {
                "anonymous": false,
                "inputs": [
                    {
                        "indexed": true,
                        "name": "room",
                        "type": "uint256"
                    }
                ],
                "name": "SeedOpened",
                "type": "event"
            },
            {
                "anonymous": false,
                "inputs": [
                    {
                        "indexed": false,
                        "name": "win",
                        "type": "address"
                    },
                    {
                        "indexed": true,
                        "name": "room",
                        "type": "uint256"
                    }
                ],
                "name": "Winner",
                "type": "event"
            },
            {
                "anonymous": false,
                "inputs": [
                    {
                        "indexed": true,
                        "name": "room",
                        "type": "uint256"
                    },
                    {
                        "indexed": false,
                        "name": "res1",
                        "type": "uint8"
                    },
                    {
                        "indexed": false,
                        "name": "res2",
                        "type": "uint8"
                    }
                ],
                "name": "RoundFinished",
                "type": "event"
            },
            {
                "anonymous": false,
                "inputs": [
                    {
                        "indexed": false,
                        "name": "win",
                        "type": "address"
                    },
                    {
                        "indexed": false,
                        "name": "amount",
                        "type": "uint256"
                    },
                    {
                        "indexed": true,
                        "name": "room",
                        "type": "uint256"
                    }
                ],
                "name": "Revard",
                "type": "event"
            },
            {
                "constant": false,
                "inputs": [
                    {
                        "name": "mreic",
                        "type": "uint256"
                    }
                ],
                "name": "setMaxReic",
                "outputs": [],
                "payable": false,
                "stateMutability": "nonpayable",
                "type": "function"
            },
            {
                "anonymous": false,
                "inputs": [
                    {
                        "indexed": true,
                        "name": "room",
                        "type": "uint256"
                    }
                ],
                "name": "BetsFinished",
                "type": "event"
            },
            {
                "anonymous": false,
                "inputs": [
                    {
                        "indexed": true,
                        "name": "room",
                        "type": "uint256"
                    },
                    {
                        "indexed": true,
                        "name": "player2",
                        "type": "address"
                    },
                    {
                        "indexed": false,
                        "name": "countdownTime",
                        "type": "uint256"
                    }
                ],
                "name": "JoinPlayer2",
                "type": "event"
            },
            {
                "anonymous": false,
                "inputs": [
                    {
                        "indexed": true,
                        "name": "room",
                        "type": "uint256"
                    }
                ],
                "name": "RevertRoom",
                "type": "event"
            },
            {
                "anonymous": false,
                "inputs": [
                    {
                        "indexed": true,
                        "name": "room",
                        "type": "uint256"
                    },
                    {
                        "indexed": true,
                        "name": "player1",
                        "type": "address"
                    }
                ],
                "name": "JoinPlayer1",
                "type": "event"
            },
            {
                "anonymous": false,
                "inputs": [
                    {
                        "indexed": true,
                        "name": "room",
                        "type": "uint256"
                    }
                ],
                "name": "RoomClosed",
                "type": "event"
            },
            {
                "anonymous": false,
                "inputs": [
                    {
                        "indexed": true,
                        "name": "room",
                        "type": "uint256"
                    },
                    {
                        "indexed": true,
                        "name": "player1",
                        "type": "address"
                    },
                    {
                        "indexed": false,
                        "name": "bet",
                        "type": "uint256"
                    },
                    {
                        "indexed": false,
                        "name": "counter",
                        "type": "uint8"
                    },
                    {
                        "indexed": false,
                        "name": "openedTime",
                        "type": "uint256"
                    },
                    {
                        "indexed": true,
                        "name": "privat",
                        "type": "bool"
                    }
                ],
                "name": "RoomOpened",
                "type": "event"
            },
            {
                "anonymous": false,
                "inputs": [
                    {
                        "indexed": true,
                        "name": "from",
                        "type": "address"
                    },
                    {
                        "indexed": true,
                        "name": "room",
                        "type": "uint256"
                    }
                ],
                "name": "BetsAdd",
                "type": "event"
            },
            {
                "constant": false,
                "inputs": [
                    {
                        "name": "id",
                        "type": "uint256"
                    },
                    {
                        "name": "seed",
                        "type": "uint256"
                    }
                ],
                "name": "setSeed",
                "outputs": [],
                "payable": false,
                "stateMutability": "nonpayable",
                "type": "function"
            },
            {
                "constant": false,
                "inputs": [],
                "name": "transferOutAll",
                "outputs": [],
                "payable": false,
                "stateMutability": "nonpayable",
                "type": "function"
            },
            {
                "constant": false,
                "inputs": [
                    {
                        "name": "newOwner",
                        "type": "address"
                    }
                ],
                "name": "transferOwnership",
                "outputs": [],
                "payable": false,
                "stateMutability": "nonpayable",
                "type": "function"
            },
            {
                "inputs": [],
                "payable": false,
                "stateMutability": "nonpayable",
                "type": "constructor"
            },
            {
                "constant": true,
                "inputs": [
                    {
                        "name": "id",
                        "type": "uint256"
                    }
                ],
                "name": "checkRoomBet",
                "outputs": [
                    {
                        "name": "",
                        "type": "uint256"
                    }
                ],
                "payable": false,
                "stateMutability": "view",
                "type": "function"
            },
            {
                "constant": true,
                "inputs": [
                    {
                        "name": "id",
                        "type": "uint256"
                    }
                ],
                "name": "checkRoomBet1",
                "outputs": [
                    {
                        "name": "",
                        "type": "bytes32"
                    }
                ],
                "payable": false,
                "stateMutability": "view",
                "type": "function"
            },
            {
                "constant": true,
                "inputs": [
                    {
                        "name": "id",
                        "type": "uint256"
                    }
                ],
                "name": "checkRoomBet2",
                "outputs": [
                    {
                        "name": "",
                        "type": "bytes32"
                    }
                ],
                "payable": false,
                "stateMutability": "view",
                "type": "function"
            },
            {
                "constant": true,
                "inputs": [
                    {
                        "name": "id",
                        "type": "uint256"
                    }
                ],
                "name": "checkRoomCounter",
                "outputs": [
                    {
                        "name": "",
                        "type": "uint8"
                    }
                ],
                "payable": false,
                "stateMutability": "view",
                "type": "function"
            },
            {
                "constant": true,
                "inputs": [
                    {
                        "name": "id",
                        "type": "uint256"
                    },
                    {
                        "name": "player",
                        "type": "address"
                    }
                ],
                "name": "checkRoomIsBet",
                "outputs": [
                    {
                        "name": "",
                        "type": "bytes32"
                    }
                ],
                "payable": false,
                "stateMutability": "view",
                "type": "function"
            },
            {
                "constant": true,
                "inputs": [
                    {
                        "name": "id",
                        "type": "uint256"
                    }
                ],
                "name": "checkRoomNotClosed",
                "outputs": [
                    {
                        "name": "",
                        "type": "bool"
                    }
                ],
                "payable": false,
                "stateMutability": "view",
                "type": "function"
            },
            {
                "constant": true,
                "inputs": [
                    {
                        "name": "id",
                        "type": "uint256"
                    }
                ],
                "name": "checkRoomOpened",
                "outputs": [
                    {
                        "name": "",
                        "type": "bool"
                    }
                ],
                "payable": false,
                "stateMutability": "view",
                "type": "function"
            },
            {
                "constant": true,
                "inputs": [
                    {
                        "name": "id",
                        "type": "uint256"
                    }
                ],
                "name": "checkRoomPlayer1",
                "outputs": [
                    {
                        "name": "",
                        "type": "address"
                    }
                ],
                "payable": false,
                "stateMutability": "view",
                "type": "function"
            },
            {
                "constant": true,
                "inputs": [
                    {
                        "name": "id",
                        "type": "uint256"
                    }
                ],
                "name": "checkRoomPlayer2",
                "outputs": [
                    {
                        "name": "",
                        "type": "address"
                    }
                ],
                "payable": false,
                "stateMutability": "view",
                "type": "function"
            },
            {
                "constant": true,
                "inputs": [
                    {
                        "name": "id",
                        "type": "uint256"
                    }
                ],
                "name": "checkRoomRes1",
                "outputs": [
                    {
                        "name": "",
                        "type": "uint8"
                    }
                ],
                "payable": false,
                "stateMutability": "view",
                "type": "function"
            },
            {
                "constant": true,
                "inputs": [
                    {
                        "name": "id",
                        "type": "uint256"
                    }
                ],
                "name": "checkRoomRes2",
                "outputs": [
                    {
                        "name": "",
                        "type": "uint8"
                    }
                ],
                "payable": false,
                "stateMutability": "view",
                "type": "function"
            },
            {
                "constant": true,
                "inputs": [
                    {
                        "name": "id",
                        "type": "uint256"
                    }
                ],
                "name": "checkRoomScore1",
                "outputs": [
                    {
                        "name": "",
                        "type": "uint8"
                    }
                ],
                "payable": false,
                "stateMutability": "view",
                "type": "function"
            },
            {
                "constant": true,
                "inputs": [
                    {
                        "name": "id",
                        "type": "uint256"
                    }
                ],
                "name": "checkRoomScore2",
                "outputs": [
                    {
                        "name": "",
                        "type": "uint8"
                    }
                ],
                "payable": false,
                "stateMutability": "view",
                "type": "function"
            },
            {
                "constant": true,
                "inputs": [
                    {
                        "name": "id",
                        "type": "uint256"
                    }
                ],
                "name": "checkRoomStartTime",
                "outputs": [
                    {
                        "name": "",
                        "type": "uint256"
                    }
                ],
                "payable": false,
                "stateMutability": "view",
                "type": "function"
            },
            {
                "constant": true,
                "inputs": [
                    {
                        "name": "id",
                        "type": "uint256"
                    }
                ],
                "name": "checkSenderBet",
                "outputs": [
                    {
                        "name": "",
                        "type": "bytes32"
                    }
                ],
                "payable": false,
                "stateMutability": "view",
                "type": "function"
            },
            {
                "constant": true,
                "inputs": [],
                "name": "owner",
                "outputs": [
                    {
                        "name": "",
                        "type": "address"
                    }
                ],
                "payable": false,
                "stateMutability": "view",
                "type": "function"
            }
        ];
        var contr =  web3.eth.contract(contrInterface);
        var contractInstance = contr.at(contrAdress);
        var address = web3.eth.defaultAccount;
        var block = 0;
        web3.eth.getBlockNumber( function(er, res){ if(res) block = res });
    }


    ContractInterface is an abi of a smart contract - a set of function names, variables and arguments for accessing and interacting with a smart contract. If you use remix - ide to work with ethereum smart contracts in a browser, then it can be easily found in the Compite -> Details -> Abi tab.

    The big problem was, and still partially remains, the determination of the game state at a single point in time, when page reloads or when switching between tables. Do not forget that we are working not with a server on which such a problem is solved elementarily, but with a blockchain; getting information from there is quite exotic. We constantly listen to all these 14 game events: the opponent’s joining, the beginning of the moves, the completion of the moves and others. We also constantly send requests for some game information, for example, the current score, the start time of the round and others. Moreover, most of the game states are determined not by one event, and not by one variable, but by superimposing several events at once with some variables - the results of direct data acquisition from the blockchain. For example, a situation when the game goes to two or more victories, and when in the third round the time has come to send private keys. We have to keep track of the fact that the game has started, the opponent has connected, three rounds have passed, and encrypted moves have been sent from both sides. Try to reload the page at this moment and you have to restore all the relationships with the blockchain again. Each request or receipt of information occurs asynchronously. The delays overlap one another and as a result, the application runs significantly slower than the server version. Each request or receipt of information occurs asynchronously. The delays overlap one another and as a result, the application runs significantly slower than the server version. Each request or receipt of information occurs asynchronously. The delays overlap one another and as a result, the application runs significantly slower than the server version.

    But back to the smart contract of our game. We have already talked about how we store game data, what events we use to interact with the client side, and how to create tables. Now I would like to describe the encryption of moves. We have only three possible travel options, 1 - stone, 2 - scissors, 3 - paper. It all starts on the client, when the player chooses his move. The script that the card selection fulfills generates a random number - we call it seed (hereinafter referred to as Sid). Sid is stored in cookies in relation to the table number, and is stored until subsequent use in them and in the game session. For the first time, seed is used when a player determines his move: select a stone, scissors or paper. The number 1, 2 or 3 is added to it, which the player has chosen. The result is hashed by the sha3 method. An important point: the web3.sha3 () method only works with strings.

    function setBet(uint id, bytes32 bet) public {
    	    require(msg.sender == rooms[id].player1 || msg.sender == rooms[id].player2 );
    	    //таймер не вышел
    	    if(rooms[id].startTime + 5 minutes > now) {
    	        if(msg.sender == rooms[id].player1) { rooms[id].res2 = 5; rooms[id].bit1 = bet; }
    	        else if(msg.sender == rooms[id].player2) { rooms[id].res1 = 5; rooms[id].bit2 = bet; }
    	        if(rooms[id].bit1 != 0x0 && rooms[id].bit2 != 0x0) {
    	            SeedOpened(id);
    	            BetsFinished(id);
    	        }
    	        BetsAdd(msg.sender , id);
    	    } else {
    	        Result(rooms[id].player1, rooms[id].res1, id); Result(rooms[id].player2, rooms[id].res2, id);
    	    }
    	}

    The setBet function provides mainly the guarantee of taking moves on both sides. Until both players make their moves and they are recorded, the gameplay cannot continue. At the same time, she monitors the observance of timers and carries out the necessary initial checks of the very possibility of taking the move. When the moves are made, the function sends signals to the client: SeedOpened and BetsFinisfed. Having received these signals, the client offers the players how we say “show cards”, that is, send to the smart contact the very first part of the number before the summation, which he randomly generated, before hashing the move. Having confirmed his consent to the “card disclosure”, the player sends this number to another function of the smart contract - setSeed

    function setSeed(uint256 id, uint256 seed) public {
        require( rooms[id].bit2 != 0x0 && rooms[id].bit1 != 0x0  );
        require(msg.sender == rooms[id].player1 || msg.sender == rooms[id].player2 );
        //таймер не вышел
        if(rooms[id].startTime + 5 minutes > now) {
            if(msg.sender == rooms[id].player1) decodeHash1(id, seed);
            else if(msg.sender == rooms[id].player2) decodeHash2(id, seed);
        } else {
            Result(rooms[id].player1, rooms[id].res1, id); Result(rooms[id].player2, rooms[id].res2, id);
        }
    }

    Which in turn transfers the received seed to the internal functions of the smart contract decodeHash1 and decodeHash2

    function decodeHash1(uint id, uint seed) internal {
        uint e1 = seed + 1;
        bytes32 bitHash1a = keccak256(uintToString(e1));
        uint e2 = seed + 2;
        bytes32 bitHash1b = keccak256(uintToString(e2));
        uint e3 = seed + 3;
        bytes32 bitHash1c = keccak256(uintToString(e3));
        if(rooms[id].bit1 == bitHash1a) rooms[id].res1 = 1;
        if(rooms[id].bit1 == bitHash1b) rooms[id].res1 = 2;
        if(rooms[id].bit1 == bitHash1c) rooms[id].res1 = 3;
        Result(rooms[id].player1, rooms[id].res1, id);
        // return res1;
    }

    This completes the encryption cycle. The function reproduces the hashing procedure already described for the client only inside ethereum and does this three times for each player - in three possible moves. Then, the usual comparison of the hash result in ethereum with the existing hash result from the client occurs. Correspondence makes it clear to us what move the player made. We do not declare or store the keys that come from the players during the expansion, instead of them we immediately record the decoding result in the res variables for each player as a number from 0 to 4.

    At this stage, there is another interesting nuance. The standard function solidity keccak256, which corresponds to the sha3 method in the web3 js library, as it turned out, gives an adequate result only when the input is exactly a string, not a number. Keccak256 allows you to work with numbers, but since the client web3.sha3 () only accepts strings, keccak256 should also receive a string in the input. And the conversion of numbers into strings on solidity is not as simple as on javascript. To do this, write an additional internal function: uintToString (). Marking pure means that this function does not have the right to somehow affect the state of the memory of the smart contract: read and write. Here is such a nuance.

    function uintToString(uint i) internal pure returns (string){
        // bytes memory bstr = new bytes;
        if (i == 0) return "0";
        uint j = i;
        uint length;
        while (j != 0){
            length++;
            j /= 10;
        }
        bytes memory bstr = new bytes(length);
        uint k = length - 1;
        while (i != 0){
            bstr[k--] = byte(48 + i % 10);
            i /= 10;
        }
        return string(bstr);
    }

    Finally, the game loop is completed by the winRes () internal winner determination function. It contains all the possible outcomes of the game, namely - with a draw, the replay of the current round begins. Determining the winner of a round or match. Accordingly, the concept of final and intermediate victory appears. The function understands situations when one of the players did not have time to take an action and counts him losing in the current round. In a situation where both players have been inactive for longer than they should, the table closes regardless of the current account. In this case, the game stops and everything returns to its original state.

    Final code
    function winRes(uint id) internal {
        require(rooms[id].res1 > 0 || rooms[id].res2 > 0);
        address win = 0x0;
        if(rooms[id].res1 == 1 && rooms[id].res2 == 2) win = rooms[id].player1;
        if(rooms[id].res1 == 1 && rooms[id].res2 == 3) win = rooms[id].player2;
        if(rooms[id].res1 == 2 && rooms[id].res2 == 1) win = rooms[id].player2;
        if(rooms[id].res1 == 2 && rooms[id].res2 == 3) win = rooms[id].player1;
        if(rooms[id].res1 == 3 && rooms[id].res2 == 1) win = rooms[id].player1;
        if(rooms[id].res1 == 3 && rooms[id].res2 == 2) win = rooms[id].player2;
        if(rooms[id].res1 == 4 && rooms[id].res2 != 4 ) win = rooms[id].player2;
        if(rooms[id].res2 == 4 && rooms[id].res1 != 4 ) win = rooms[id].player1;
        if(rooms[id].res1 == 5 && rooms[id].res2 != 5 ) win = rooms[id].player2;
        if(rooms[id].res2 == 5 && rooms[id].res1 != 5 ) win = rooms[id].player1;
        if((rooms[id].res2 == 4 && rooms[id].res1 == 4 ) || (rooms[id].res2 == 5 && rooms[id].res1 == 5 )) revertRoom(id);
        else
        {
            //Ничья - начинаем игру заново
            if(win == 0x0) {
                replay(id);
                OneMoreGame(id);
            } else {
                //Кто то победил
                if( win == rooms[id].player1 ) rooms[id].c1 += 1;
                if( win == rooms[id].player2 ) rooms[id].c2 += 1;
                //Если игра длится до n-побед и счетчик не достиг лимита
                if( rooms[id].counter > 1 && rooms[id].c1 < rooms[id].counter && rooms[id].c2 < rooms[id].counter ) {
                    ScoreChanged(id, rooms[id].c1, rooms[id].c2);
                    replay(id);
                    OneMoreGame(id);
                } else {
                    //Тут мы точно знаем что наш победитель одержал окончательную победу
                    ScoreChanged(id, rooms[id].c1, rooms[id].c2);
                    if( rooms[id].bet > 0 ) {
                        rewardWin(win, id);
                    }
                    Winner(win, id);
                    closeRoom(id);
                }
            }
        }
    }


    The game algorithm was created from scratch, so this option is a successful result of a series of experiments and adjustments. It is difficult to say how effective and optimal it is, since we can only compare it with ourselves. Relative to the previous 4 versions of our algorithm, this one is several times more effective. There are probably many more possible optimization solutions, but this is a matter of the future.

    Also popular now: