Solidity

猜數字遊戲,不工作

  • September 3, 2022

我正在嘗試在智能合約上編寫一個遊戲,這是一個玩家必須輸入 5 次猜測的遊戲,並且根據它們與實際數字的接近程度,他們將獲得乙太幣作為獎品。

猜數字:

// SPDX-License-Identifier: MIT
pragma solidity >= 0.7.0 < 0.9.0;

contract Oracle {

   address owner;
   uint public variableRand;

   constructor () {
       owner = msg.sender;
   }

   function setRandomVariable (uint _varRand) external {
       require (msg.sender == owner);
       variableRand = _varRand;
   }

}

contract Play {

   // Saves the value of what you have won in the session
   address owner;
   uint public currentPrize = 0;
   int[] public currentGuess;
   uint[] public randNum;

   event Log(uint gas);

   function showPoolBalance() public view returns(uint) {
       return address(this).balance;
   }

   function randMod() public view returns(uint) {
       // grab information from the blockchain randomly to generate random numbers - we need something dynamically changing
       // abi.encodePacked concatonates arguments nicely
       return uint(keccak256(abi.encodePacked(oracle.variableRand, block.timestamp, block.difficulty, msg.sender))) % 1000;
   }

   function inputGuess(int _guess) public {
       // Adds the guesses to the array
       currentGuess.push(_guess);
   }

   function clearGuesses() public {
       for (uint i = 0; i < currentGuess.length; i++) {
           //removes all of the guesses in the array
           currentGuess.pop();
       }
   }

   function abs(int x) private pure returns (int) {
       //returns the abolute value of the input
       return x >= 0 ? x : -x;
   }

   function game() private returns (int, uint) {

       for (uint i = 0; i <= 5; i++) {
           randNum[i] = randMod();
           int absv = abs(int(randNum[i]) - currentGuess[i]);
          if (absv < 6 && absv > 0) {
              currentPrize += 2;
          }
          else if (absv == 0) {
              currentPrize += 4;
          }
          else if (absv < 16 && absv > 5) {
              currentPrize += 1;
          }
          return (absv , randNum[i]);
       }

   }

   function play() public payable returns (int, uint) {

       require(msg.value == (10 ether), "You must pay with 10 ethers!");
       game();
       
   }

   function chargePool() public payable {
       require (msg.value >= 10 ether);
   }

   function withdrawPrize() public payable {

       payable(msg.sender).transfer(currentPrize);

       }

   fallback () external payable {
       emit Log(gasleft());
   }
   
   Oracle oracle;

   constructor (address oracleAddress) {
       oracle = Oracle(oracleAddress);
       owner = msg.sender;
   }

}

Oracle 合約部署正確。Play 合約也將部署。猜測的數字會被正確地推入數組,但是當我想用 10 乙太呼叫 play 函式時,會出現以下錯誤:

$$ vm $$從:0x5B3…eddC4 到:Play.play() 0xf8e…9fBe8value:10000000000000000000 weidata:0x93e…84cd9logs:0hash:0xfd3…dcad2 交易到 Play.play 錯誤:VM 錯誤:還原。 revert 事務已恢復到初始狀態。注意:如果您發送值並且您發送的值應該小於您目前的餘額,則呼叫的函式應該是應付的。調試事務以獲取更多資訊。

任何幫助,將不勝感激。

我發現你的程式碼可能出了什麼問題。

這是randNum數組的使用。它是動態的,因此您可以使用.push()它來添加元素,但您沒有指定大小,因此您不能使用randNum[i],因為這些索引可能還不存在。我將聲明更改為:

uint[] public randNum = new uint[](5);

看一看:

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.6;

contract Oracle {

   address public owner;
   uint public variableRand;

   constructor () {
       owner = msg.sender;
   }

   function setRandomVariable (uint _varRand) external {
       require (msg.sender == owner, "Not owner");
       variableRand = _varRand;
   }

}

contract Play {

   // Saves the value of what you have won in the session
   address owner;
   uint public currentPrize = 0;
   int[] public currentGuess;
   uint[] public randNum = new uint[](5);
   Oracle oracle;

   constructor(Oracle _oracle) {
       oracle = _oracle;
   }

   event Log(uint gas);

   function showPoolBalance() public view returns(uint) {
       return address(this).balance;
   }

   function randMod() public view returns(uint) {
       // grab information from the blockchain randomly to generate random numbers - we need something dynamically changing
       // abi.encodePacked concatonates arguments nicely
       return uint(keccak256(abi.encodePacked(oracle.variableRand(), block.timestamp, block.difficulty, msg.sender))) % 1000;
   }

   function inputGuess(int _guess) public {
       // Adds the guesses to the array
       currentGuess.push(_guess);
   }

   function clearGuesses() public {
       for (uint i = 0; i < currentGuess.length; i++) {
           //removes all of the guesses in the array
           currentGuess.pop();
       }
   }

   function abs(int x) private pure returns (int) {
       //returns the abolute value of the input
       return x >= 0 ? x : -x;
   }

   function game() private returns (int, uint) {

       for (uint i = 0; i <= 5; i++) {
           randNum[i] = randMod();
           int absv = abs(int(randNum[i]) - currentGuess[i]);
          if (absv < 6 && absv > 0) {
              currentPrize += 2;
          }
          else if (absv == 0) {
              currentPrize += 4;
          }
          else if (absv < 16 && absv > 5) {
              currentPrize += 1;
          }
          return (absv , randNum[i]);
       }

   }

   function play() public payable returns (int, uint) {

       require(msg.value == (10 ether), "You must pay with 10 ethers!");
       game();
   }
}

除此之外,我看到您的程式碼確實可以在許多方面得到改進。

例如,return (absv , randNum[i]);在循環結束時在循環內部和 any 外部做什麼if?循環只會執行一次然後返回。也許那不是你想要的。

看看這個可能對您有幫助的新實現。

看一下playv2and playv3,尤其是playv3,它將記錄保存SenderGuesssenderGuesses可以幫助您調試的映射中。這是一個想法,您可以根據需要對其進行修改。

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.6;

contract Contract {

   event Payment(address indexed from, address indexed to, string recipientName);

   function sendPayment(address from, address payable to, string memory recipientName) public payable {
       to.transfer(msg.value);
       emit Payment(from, to, recipientName);
   }

}

contract Oracle {
   

   address public owner;
   uint public variableRand;

   constructor () {
       owner = msg.sender;
   }

   function setRandomVariable (uint _varRand) external {
       require (msg.sender == owner, "Not owner");
       variableRand = _varRand;
   }

}

contract Play {

   // Saves the value of what you have won in the session
   address owner;
   uint public currentPrize = 0;
   int[] public currentGuess;
   uint[] public randNum = new uint[](5);
   Oracle oracle;

   mapping(address => SenderGuess[]) public senderGuesses;

   struct SenderGuess {
       address sender;
       uint256 blockNumber;
       uint256[] guesses;
       uint256 diff;
       uint256 prize;
   }

   constructor(Oracle _oracle) {
       oracle = _oracle;
   }

   event Log(uint gas);

   function showPoolBalance() public view returns(uint) {
       return address(this).balance;
   }

   function randMod() public view returns(uint) {
       // grab information from the blockchain randomly to generate random numbers - we need something dynamically changing
       // abi.encodePacked concatonates arguments nicely
       return uint(keccak256(abi.encodePacked(oracle.variableRand(), block.timestamp, block.difficulty, msg.sender))) % 1000;
   }

   function inputGuess(int _guess) public {
       // Adds the guesses to the array
       currentGuess.push(_guess);
   }

   function clearGuesses() public {
       for (uint i = 0; i < currentGuess.length; i++) {
           //removes all of the guesses in the array
           currentGuess.pop();
       }
   }

   function abs(int x) private pure returns (int) {
       //returns the abolute value of the input
       return x >= 0 ? x : -x;
   }



   function game() private returns (int, uint) {

       for (uint i = 0; i <= 5; i++) {
           randNum[i] = randMod();
           int absv = abs(int(randNum[i]) - currentGuess[i]);
          if (absv < 6 && absv > 0) {
              currentPrize += 2;
          }
          else if (absv == 0) {
              currentPrize += 4;
          }
          else if (absv < 16 && absv > 5) {
              currentPrize += 1;
          }
          return (absv , randNum[i]);
       }

   }

   function play() public payable returns (int, uint) {

       require(msg.value == (10 ether), "You must pay with 10 ethers!");
       game();
   }

   // v2
   // This version gets closest guess and and assigns the prize multiple times.
   function playv2(uint256[] memory guesses) public payable returns (uint256) {
       require(msg.value == (10 ether), "You must pay with 10 ethers!");
       require(guesses.length == 5, "You need to send 5 guesses");

       uint256 _currentPrize = 10;

       // Getting the 'random' mod number only once because if the call to oracle.variableRand() does not return
       // a different "random" number, then the randMod function will return the same thing anyways.
       uint256 randNumber = randMod();

       for(uint256 i = 0; i < guesses.length; i++) {

           uint256 guess = guesses[i];
           uint256 min = randNumber > guess ? randNumber : guess;
           uint256 max = guess > randNumber ? guess : randNumber;

           uint256 diff = max - min;

           if (diff == 0) {
               _currentPrize += 4;
           } else if (diff < 6) {
              _currentPrize += 2;
          } else if (diff < 16) {
              _currentPrize += 1;
          }

       }

       return _currentPrize;

   }

   // v3
   // This version gets closest guess and and assigns the prize only once.
   function playv3(uint256[] memory guesses) public payable {
       require(msg.value == (10 ether), "You must pay with 10 ethers!");
       require(guesses.length == 5, "You need to send 5 guesses");

       uint256 _currentPrize = 10;

       // Getting the 'random' mod number only once because if the call to oracle.variableRand() does not return
       // a different "random" number, then the randMod function will return the same thing anyways.
       uint256 randNumber = randMod();

       uint256 smallestDiff = 16;

       for(uint256 i = 0; i < guesses.length; i++) {

           uint256 guess = guesses[i];
           uint256 min = randNumber > guess ? randNumber : guess;
           uint256 max = guess > randNumber ? guess : randNumber;

           uint256 diff = max - min;

           if(diff < smallestDiff) {
               smallestDiff = diff;
           }

       }

       if (smallestDiff == 0) {
           _currentPrize += 4;
       } else if (smallestDiff < 6) {
           _currentPrize += 2;
       } else if (smallestDiff < 16) {
           _currentPrize += 1;
       }

       SenderGuess memory senderGuess = SenderGuess(msg.sender, block.number, guesses, smallestDiff, _currentPrize);

       senderGuesses[msg.sender].push(senderGuess);

   }


}

引用自:https://ethereum.stackexchange.com/questions/134911