// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract VersusWager {
struct Game {
address payable playerA;
address payable playerB;
uint256 stake;
bool isFlipped;
address winner;
}
mapping(string => Game) public games; // Maps gameID to Game.
mapping(uint256 => string) public gameNumbers; // Maps gameNumber to gameID.
uint256 public currentGameNumber = 0;
string[] public openGames;
function createGame(string memory gameID) public payable {
require(bytes(gameID).length <= 10, "GameID too long");
require(games[gameID].playerA == address(0), "GameID already exists");
games[gameID] = Game({
playerA: payable(msg.sender),
playerB: payable(address(0)),
stake: msg.value,
isFlipped: false,
winner: address(0)
});
currentGameNumber++;
gameNumbers[currentGameNumber] = gameID;
openGames.push(gameID);
}
function joinGame(string memory gameID) public payable {
require(games[gameID].playerA != address(0), "Game does not exist");
require(games[gameID].playerB == address(0), "Game already has a second player");
require(msg.value == games[gameID].stake, "Must send the correct stake amount");
games[gameID].playerB = payable(msg.sender);
// Removing game from the openGames list
for(uint i = 0; i < openGames.length; i++) {
if(keccak256(abi.encodePacked(openGames[i])) == keccak256(abi.encodePacked(gameID))) {
openGames[i] = openGames[openGames.length - 1];
openGames.pop();
break;
}
}
}
function flip(string memory gameID) public {
require(games[gameID].playerB != address(0), "Game does not have a second player yet");
require(!games[gameID].isFlipped, "Game has already been flipped");
require(msg.sender == games[gameID].playerA || msg.sender == games[gameID].playerB, "Only participants can initiate the flip");
uint256 randomness = uint256(keccak256(abi.encodePacked(blockhash(block.number - 1), msg.sender))) % 2;
if(randomness == 0) {
games[gameID].winner = games[gameID].playerA;
games[gameID].playerA.transfer(games[gameID].stake * 2);
} else {
games[gameID].winner = games[gameID].playerB;
games[gameID].playerB.transfer(games[gameID].stake * 2);
}
games[gameID].isFlipped = true;
}
function getGame(string memory gameID) public view returns(Game memory) {
return games[gameID];
}
function getOpenGames() public view returns(string[] memory) {
return openGames;
}
function getAllGames() public view returns(string[] memory) {
string[] memory allGames = new string[](currentGameNumber);
for(uint i = 0; i < currentGameNumber; i++) {
allGames[i] = gameNumbers[i+1];
}
return allGames;
}
function getUnflippedGames(address player) public view returns(string[] memory) {
uint count = 0;
for(uint i = 0; i < openGames.length; i++) {
if(games[openGames[i]].playerA == player || games[openGames[i]].playerB == player) {
count++;
}
}
string[] memory unflipped = new string[](count);
uint j = 0;
for(uint i = 0; i < openGames.length; i++) {
if(games[openGames[i]].playerA == player || games[openGames[i]].playerB == player) {
unflipped[j] = openGames[i];
j++;
}
}
return unflipped;
}
}
{
"compilationTarget": {
"contracts/Versus.sol": "VersusWager"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"string","name":"gameID","type":"string"}],"name":"createGame","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"currentGameNumber","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"gameID","type":"string"}],"name":"flip","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"gameNumbers","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"games","outputs":[{"internalType":"address payable","name":"playerA","type":"address"},{"internalType":"address payable","name":"playerB","type":"address"},{"internalType":"uint256","name":"stake","type":"uint256"},{"internalType":"bool","name":"isFlipped","type":"bool"},{"internalType":"address","name":"winner","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllGames","outputs":[{"internalType":"string[]","name":"","type":"string[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"gameID","type":"string"}],"name":"getGame","outputs":[{"components":[{"internalType":"address payable","name":"playerA","type":"address"},{"internalType":"address payable","name":"playerB","type":"address"},{"internalType":"uint256","name":"stake","type":"uint256"},{"internalType":"bool","name":"isFlipped","type":"bool"},{"internalType":"address","name":"winner","type":"address"}],"internalType":"struct VersusWager.Game","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getOpenGames","outputs":[{"internalType":"string[]","name":"","type":"string[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"player","type":"address"}],"name":"getUnflippedGames","outputs":[{"internalType":"string[]","name":"","type":"string[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"gameID","type":"string"}],"name":"joinGame","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"openGames","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"}]