Are you good at computing sha3 hashes? Compete with other players with the objective of getting the lowest hash value and win some Ether! Note that if you are the sole player, this is essentially a faucet when the bonus fund has sufficient ether.

The game has three states:

0 - Waiting for first player to join

1 - Waiting for more players, countdown to competition started!

2 - Competition! Submit your solutions at this stage

Disclaimer: I, the author have hard-coded 1% of the winnings to go to my address. I believe this is fair, since I spent time writing this contract, and also am periodically refilling the (optional) bonus fund that gives additional payouts. Please consider this when playing the game.

LOAD CONTRACT:

Copy the following commands into your geth console:

var mygameContract = eth.contract( [{"constant":false,"inputs":[{"name":"sol","type":"uint256"}],"name":"submitSolution","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"bidIndex","type":"uint256"}],"name":"getBidderAt","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[],"name":"status","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"getCompetitionEnd","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"bidIndex","type":"uint256"}],"name":"getHashAt","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"bidIndex","type":"uint256"}],"name":"getSolnAt","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"blockToHash","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"bidIndex","type":"uint256"}],"name":"getBidAt","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"interval","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"bonusFund","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[],"name":"setStatus","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"getNumBids","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"suggestedInterval","type":"uint256"}],"name":"newBid","outputs":[],"type":"function"},{"inputs":[],"type":"constructor"}]);

var l = mygameContract.at("0x35e57f2b08596f1946ecd9d31975ec3c2d7e1c1d")

USAGE:

When game is in state 0:

Submit first bet, you have the option of choosing the interval value, which is the length of state 1 and state 2 (in terms of number of blocks). The first number (20 in this example) is the interval length you are free to choose. Pick any value between 20 and 200 inclusive. The amount you send is equal to your wager.

l.newBid(20, {from:eth.accounts[0], to:l.address, value:web3.toWei(1, "ether"),gas:3000000})

When game is in state 1:

Submit your wager, now the first number in the line below does not matter. Leave it at 0. The amount you send is equal to your wager.

l.newBid(0, {from:eth.accounts[0], to:l.address, value:web3.toWei(1, "ether"),gas:3000000})

When game is in state 2:

Submit solutions, by typing the follwing line, where you substitute 0 with your solution. The objective is to find a solution so that the value of sha3(block.blockhash(blockToHash),solution) is lower than that of other players.

l.submitSolution(0, {from:eth.accounts[0], to:l.address,gas:3000000})

When the time interval has passed, you can trigger the payout by typing

l.setStatus({from:eth.accounts[0], to:l.address,gas:3000000}), or it is triggered automatically with a call to newBid or submitSolution.

MONITORING:

You can monitor the current state of the game by typing the following command.

text="";

text="Contract balance

"+web3.fromWei(eth.getBalance(l.address), "ether")+" Ether"+"

status:

"+l.status()+"

blockToHash (competition start):

"+l.blockToHash()+"

Competition end

:"+l.getCompetitionEnd()+"

bonusFund:

"+web3.fromWei(l.bonusFund(), "ether")+" Ether"+"

getNumBids()

:"+l.getNumBids() +"

";

for (i = 0; i < l.getNumBids(); i++) {

text+= "i= "+i+"

getBidderAt(i)

:"+l.getBidderAt(i) +"

getHashAt(i)

:"+ l.getHashAt(i)+"

getBidAt(i)

:"+ web3.fromWei(l.getBidAt(i), "ether")+" Ether"+"

getSolnAt(i)

:"+ l.getSolnAt(i)+"

";

}

text

VERIFICATION:

This is the source code, compiled with optimization flag set to true.

contract SHA3compete {

address developer;

uint public interval; // Between 20 and 200

uint public status;

uint public blockToHash;

uint public bonusFund;

Bid[] bids;

struct Bid{

address bidder;

uint value;

uint bestHash;

uint sol;

}

function SHA3compete(){

developer = msg.sender;

bonusFund = msg.value;

status = 0; // 0 waiting for first bid, 1 has first bid, 2 in competition

bids.length=0;

}

function getNumBids() constant returns(uint){

return bids.length;

}

function getCompetitionEnd() constant returns(uint){

if (status>0){

return blockToHash+interval;

}

else{

return 0;

}

}

function getBidderAt(uint bidIndex) constant returns(address){

return bids[bidIndex].bidder;

}

function getHashAt(uint bidIndex) constant returns(uint){

return bids[bidIndex].bestHash;

}

function getBidAt(uint bidIndex) constant returns(uint){

return bids[bidIndex].value;

}

function getSolnAt(uint bidIndex) constant returns(uint){

return bids[bidIndex].sol;

}

function (){

bonusFund+=msg.value;

}

function newBid(uint suggestedInterval){ //This is the bid submission

setStatus();

if (msg.value>0 && status!=2 && !(status==0 && (suggestedInterval<20 || suggestedInterval>200) )){

// If in competition, do not accept bids

if (status==0 ){ // Add the first competitor

interval = suggestedInterval;

blockToHash = block.number+interval;

status = 1;

}

bids.length+=1;

bids[bids.length-1]=Bid(msg.sender, msg.value,2**256-1,0);

}

else{ // Catch erroneous sends

bonusFund+=msg.value;

}

}

function submitSolution(uint sol){

setStatus();

if (status == 2){

bool foundIdentical = false;

for (uint i=0;i<bids.length;i++){

if (bids[i].sol == sol){

foundIdentical = true;

}

}

if (foundIdentical==false){

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

if (bids[i].bidder == msg.sender){

bids[i].bestHash=uint(sha3(block.blockhash(blockToHash),sol));

bids[i].sol=sol;

}

}

}

}

// Catch erroneous sends

bonusFund+=msg.value;

}

function setStatus(){

if(status== 1 && blockToHash<=block.number){ //Condition that starts competition

status = 2;

}

if(status==2 && blockToHash+interval<=block.number){ // Condition that ends competition

status=0;

payout();

blockToHash=0;

delete bids;

}

}

function payout() private{

bool foundNonZero=true;

while (foundNonZero==true){

foundNonZero=false;

uint currentBest=2**256-1;

uint bestI = 0;

for (uint i=0;i<bids.length;i++){

if (bids[i].bestHash <currentBest && bids[i].value>0){

currentBest = bids[i].bestHash;

foundNonZero=true;

bestI = i;

}

}

// Collect money for bestI

uint betOfI = bids[bestI].value;

uint toSend = 0;

for (uint j = 0;j<bids.length;j++){

if (bids[j].value>=betOfI){

toSend+=betOfI;

bids[j].value-=betOfI;

}

else{

toSend+=bids[j].value;

bids[j].value=0;

}

}

if (foundNonZero==true){

bids[bestI].bidder.send(toSend/100*99+bonusFund/100);

bonusFund-=bonusFund/100;

developer.send(toSend-toSend/100*99);

}

}

}