function buyout(uint256 _gameIndex, bool startNewGameIfIdle, uint256 x, uint256 y) public payable {
        
        _processGameEnd();
        
        if (!gameStates[gameIndex].gameStarted) {
            
            require(!paused);
            
            if (allowStart) {
                
                allowStart = false;
            } else {
                
                require(canStart());
            }
            
            
            
            require(startNewGameIfIdle);
            
            _setGameSettings();
            
            
            gameStates[gameIndex].gameStarted = true;
            
            
            gameStates[gameIndex].gameStartTimestamp = block.timestamp;
            
            
            gameStates[gameIndex].penultimateTileTimeout = block.timestamp + gameSettings.initialActivityTimer;
            
            Start(
                gameIndex,
                msg.sender,
                block.timestamp,
                gameStates[gameIndex].prizePool
            );
            
            PenultimateTileTimeout(gameIndex, gameStates[gameIndex].penultimateTileTimeout);
        }
    
        
        if (startNewGameIfIdle) {
            
            
            require(_gameIndex == gameIndex || _gameIndex.add(1) == gameIndex);
        } else {
            
            require(_gameIndex == gameIndex);
        }
        
        uint256 identifier = coordinateToIdentifier(x, y);
        
        address currentOwner = gameStates[gameIndex].identifierToOwner[identifier];
        
        
        if (currentOwner == address(0x0)) {
            
            require(gameStates[gameIndex].gameStartTimestamp.add(gameSettings.initialActivityTimer) >= block.timestamp);
        } else {
            
            require(gameStates[gameIndex].identifierToTimeoutTimestamp[identifier] >= block.timestamp);
        }
        
        
        uint256 price = currentPrice(identifier);
        require(msg.value >= price);
        
        
        uint256[] memory claimedSurroundingTiles = _claimedSurroundingTiles(identifier);
        
        
        _calculateAndAssignBuyoutProceeds(currentOwner, price, claimedSurroundingTiles);
        
        
        uint256 timeout = tileTimeoutTimestamp(identifier, msg.sender);
        gameStates[gameIndex].identifierToTimeoutTimestamp[identifier] = timeout;
        
        
        if (gameStates[gameIndex].lastTile == 0 || timeout >= gameStates[gameIndex].identifierToTimeoutTimestamp[gameStates[gameIndex].lastTile]) {
            if (gameStates[gameIndex].lastTile != identifier) {
                if (gameStates[gameIndex].lastTile != 0) {
                    
                    gameStates[gameIndex].penultimateTileTimeout = gameStates[gameIndex].identifierToTimeoutTimestamp[gameStates[gameIndex].lastTile];
                    PenultimateTileTimeout(gameIndex, gameStates[gameIndex].penultimateTileTimeout);
                }
            
                gameStates[gameIndex].lastTile = identifier;
                LastTile(gameIndex, identifier, x, y);
            }
        } else if (timeout > gameStates[gameIndex].penultimateTileTimeout) {
            gameStates[gameIndex].penultimateTileTimeout = timeout;
            
            PenultimateTileTimeout(gameIndex, timeout);
        }
        
        
        _transfer(currentOwner, msg.sender, identifier);
        
        
        gameStates[gameIndex].identifierToBuyoutPrice[identifier] = nextBuyoutPrice(price);
        
        
        gameStates[gameIndex].numberOfTileFlips++;
        
        
        Buyout(gameIndex, msg.sender, identifier, x, y, block.timestamp, timeout, gameStates[gameIndex].identifierToBuyoutPrice[identifier], gameStates[gameIndex].prizePool);
        
        
        
        
        uint256 excess = msg.value - price;
        
        if (excess > 0) {
            
            
            msg.sender.transfer(excess);
        }
    }