├── assets ├── images │ ├── apple.png │ ├── menu.png │ ├── snake.png │ └── gameover.png ├── styles │ └── main.css └── js │ ├── main.js │ ├── menu.js │ ├── game_over.js │ └── game.js ├── README.md ├── contracts └── DAppHeroGame.sol └── index.html /assets/images/apple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apoorvlathey/Blockchain-Snake-Game-Using-DappHero-/HEAD/assets/images/apple.png -------------------------------------------------------------------------------- /assets/images/menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apoorvlathey/Blockchain-Snake-Game-Using-DappHero-/HEAD/assets/images/menu.png -------------------------------------------------------------------------------- /assets/images/snake.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apoorvlathey/Blockchain-Snake-Game-Using-DappHero-/HEAD/assets/images/snake.png -------------------------------------------------------------------------------- /assets/images/gameover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apoorvlathey/Blockchain-Snake-Game-Using-DappHero-/HEAD/assets/images/gameover.png -------------------------------------------------------------------------------- /assets/styles/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #061f27; 3 | color: white; 4 | } 5 | 6 | button { 7 | background-color: #44b9fc; 8 | border: none; 9 | color: white; 10 | padding: 15px 32px; 11 | text-align: center; 12 | text-decoration: none; 13 | display: inline-block; 14 | font-size: 16px; 15 | } 16 | 17 | .center { 18 | margin-left: 46%; 19 | border-radius: 1.4em; 20 | } 21 | 22 | canvas { 23 | margin-left: auto; 24 | margin-right: auto; 25 | border: 5px solid white; 26 | } -------------------------------------------------------------------------------- /assets/js/main.js: -------------------------------------------------------------------------------- 1 | var game; 2 | 3 | // create new game instance 600px wide and 450px tall 4 | game = new Phaser.Game(600, 450, Phaser.AUTO, ''); //phaser.auto is renderer type. 5 | 6 | // First param, how state will be called 7 | // Second param, obj containing needed methods for state functionality 8 | game.state.add('Menu', Menu); 9 | 10 | game.state.start('Menu'); 11 | 12 | // adding the game state 13 | game.state.add('Game', Game); 14 | 15 | game.state.start('Menu'); 16 | 17 | game.state.add('Game_Over', Game_Over); -------------------------------------------------------------------------------- /assets/js/menu.js: -------------------------------------------------------------------------------- 1 | var Menu = { 2 | 3 | preload : function() { 4 | // First arg how img referred to 5 | // Second arg path to file 6 | game.load.image('menu', './assets/images/menu.png'); 7 | }, 8 | 9 | create: function() { 10 | // // Add sprite to game. Here it's game logo 11 | // // Parameters are: X, Y, image name 12 | // this.add.sprite(0, 0, 'menu'); 13 | 14 | // Add menu screen as button 15 | this.add.button(0, 0, 'menu', this.startGame, this); 16 | }, 17 | 18 | startGame: function() { 19 | if(getStatus() === "true") { 20 | // change state to actual game 21 | this.state.start('Game'); 22 | } else if(getStatus() === "STATUS") { 23 | console.log("Wait to Load web3 data") 24 | } else { 25 | alert("Deposit ETH to Play!") 26 | } 27 | } 28 | 29 | }; -------------------------------------------------------------------------------- /assets/js/game_over.js: -------------------------------------------------------------------------------- 1 | var Game_Over = { 2 | 3 | preload : function() { 4 | // Load the needed image for this game screen. 5 | game.load.image('gameover', './assets/images/gameover.png'); 6 | }, 7 | 8 | create : function() { 9 | 10 | // Create button to start game like in Menu. 11 | this.add.button(0, 0, 'gameover', this.startGame, this); 12 | claimPrize(score); 13 | 14 | // Add text with information about the score from last game. 15 | game.add.text(235, 350, "LAST SCORE", { font: "bold 16px sans-serif", fill: "#46c0f9", align: "center"}); 16 | game.add.text(350, 348, score.toString(), { font: "bold 20px sans-serif", fill: "#fff", align: "center" }); 17 | 18 | }, 19 | 20 | startGame: function () { 21 | 22 | // Change the state back to Game. 23 | //this.state.start('Game'); 24 | 25 | } 26 | 27 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Blockchain Snake Game (Using DappHero) 2 | Score Higher than the Rest and Win Crypto! | [Rinkeby Demo](https://apoorvlathey.com/projects/BlockchainSnakeGame/) 3 | 4 | Video Demo: [YouTube](https://youtu.be/cBSaPn-Pdh0) 5 | 6 |  7 | 8 | Combining the Classic Snake game and the latest technology: Blockchain, this game aims to reward crypto to the Players that score better than the rest. 9 | 10 | In order to start the match, the player deposits .001 ETH into the Game Pool and begins the game. On biting the snake's tail or hitting the wall, the game ends and the score is sent to the Game's Smart Contract. If the player's score is greater than the current overall Player Average than he is rewarded with .001 ETH (initial deposit) + 1% winning prize (0.00001 ETH). Losers lose entire .001 ETH to the Game Pool. 11 | 12 | The entire blockchain interaction is handled via DappHero, game logic in phaser.js. 13 | 14 | The game follows a unique concept to maintain balance: 15 | If the OverallAverageScore to beat is lower, then the players can easily win emptying out our Prize Pool one-by-one, but because in order to win, the score must be higher than the Total Average Score. This means that with each new win the target to beat (Overall Average) keeps on increasing meaning that it becomes more difficult for each following player. This means more less-skilled players would now lose, losing their .001 ETH deposit thereby maintaining a balance between Prize Pool Reserves and Score To Beat. 16 | 17 |  18 | -------------------------------------------------------------------------------- /contracts/DAppHeroGame.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | library SafeMath { 4 | 5 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 6 | uint256 c = a + b; 7 | require(c >= a, "SafeMath: addition overflow"); 8 | 9 | return c; 10 | } 11 | 12 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 13 | return sub(a, b, "SafeMath: subtraction overflow"); 14 | } 15 | 16 | function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 17 | require(b <= a, errorMessage); 18 | uint256 c = a - b; 19 | 20 | return c; 21 | } 22 | 23 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 24 | // Gas optimization: this is cheaper than requiring 'a' not being zero, but the 25 | // benefit is lost if 'b' is also tested. 26 | // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 27 | if (a == 0) { 28 | return 0; 29 | } 30 | 31 | uint256 c = a * b; 32 | require(c / a == b, "SafeMath: multiplication overflow"); 33 | 34 | return c; 35 | } 36 | 37 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 38 | return div(a, b, "SafeMath: division by zero"); 39 | } 40 | 41 | function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 42 | // Solidity only automatically asserts when dividing by 0 43 | require(b > 0, errorMessage); 44 | uint256 c = a / b; 45 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 46 | 47 | return c; 48 | } 49 | 50 | function mod(uint256 a, uint256 b) internal pure returns (uint256) { 51 | return mod(a, b, "SafeMath: modulo by zero"); 52 | } 53 | 54 | function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 55 | require(b != 0, errorMessage); 56 | return a % b; 57 | } 58 | } 59 | 60 | contract DAppHeroGame { 61 | using SafeMath for uint256; 62 | uint256 public prizePool; 63 | uint256 public avgScore; 64 | 65 | struct Challenge { 66 | bool status; // 0 = Ended; 1 = Started 67 | uint256 deposited; 68 | } 69 | 70 | mapping(address => Challenge) public userChallenge; 71 | 72 | constructor(uint256 _initialAvgScore) public { 73 | avgScore = _initialAvgScore; 74 | } 75 | 76 | function newChallenge() external payable { 77 | require(msg.value > 0); 78 | 79 | userChallenge[msg.sender].deposited = (userChallenge[msg.sender].deposited).add(msg.value); 80 | userChallenge[msg.sender].status = true; 81 | prizePool = prizePool.add(msg.value); 82 | } 83 | 84 | function claim(uint256 _score) external { 85 | require(userChallenge[msg.sender].deposited > 0 && userChallenge[msg.sender].status == true); 86 | 87 | if(_score > avgScore) { 88 | //Send original + 1% extra from pool 89 | (msg.sender).transfer((userChallenge[msg.sender].deposited).mul(101).div(100)); 90 | prizePool = prizePool.sub((userChallenge[msg.sender].deposited).mul(101).div(100)); 91 | } 92 | 93 | avgScore = avgScore.add(_score).div(2); 94 | userChallenge[msg.sender].deposited = 0; 95 | userChallenge[msg.sender].status = false; 96 | } 97 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |