game.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. (function(web3){
  2. var MOVES = {
  3. SELECT : 3,
  4. BET : 5,
  5. CALL : 7,
  6. PROOF : 11,
  7. FOLD : 13,
  8. LEAVE : 17
  9. };
  10. var ROUNDTYPES = {
  11. READY : 666,
  12. SELECT : MOVES.SELECT,
  13. BET : MOVES.BET,
  14. CALLFOLD: MOVES.CALL * MOVES.FOLD,
  15. PROVE : MOVES.PROOF
  16. };
  17. var ROUNDS = [
  18. // ROUNDTYPES.READY,
  19. ROUNDTYPES.SELECT,
  20. ROUNDTYPES.BET,
  21. ROUNDTYPES.CALLFOLD,
  22. ROUNDTYPES.SELECT,
  23. ROUNDTYPES.BET,
  24. ROUNDTYPES.CALLFOLD,
  25. ROUNDTYPES.SELECT,
  26. ROUNDTYPES.BET,
  27. ROUNDTYPES.CALLFOLD,
  28. ROUNDTYPES.PROVE,
  29. ROUNDTYPES.BET,
  30. ROUNDTYPES.CALLFOLD,
  31. ROUNDTYPES.PROVE,
  32. ROUNDTYPES.PROVE
  33. ];
  34. var SELECTINDEXES = [1,0,0,2,0,0,3,0,0,5,0,0,6,7];
  35. var PROOFTOSELECT = [0,1,4,7];
  36. web3.Game = {
  37. MOVES : MOVES,
  38. ROUNDS : ROUNDS,
  39. ROUNDTYPES : ROUNDTYPES
  40. };
  41. var _stack = [];
  42. var _roundStack = [];
  43. var _isAlreadySelected = {
  44. };
  45. var currentState = {};
  46. var _gameState = {};
  47. var _onStateChange;
  48. var _onRoundChange;
  49. // SAVE MOVES //
  50. web3.Game.saveMove = function(move){
  51. currentState[move.who] = move;
  52. if(currentState[getOther(move.who)])
  53. return this.saveState();
  54. if(move.type == MOVES.FOLD){ // TODO -- this is propably wrong but i can hhandle it ftm
  55. return this.saveState();
  56. }
  57. return true;
  58. };
  59. web3.Game.isAvailableNow = function(type){
  60. return ROUNDS[_roundStack.length] % type == 0;
  61. }
  62. web3.Game.isValidMove = function(move){ // GAME STATE CONTRAINT FUNCTION
  63. var type = move.type;
  64. if(!this.isAvailableNow(type))return false;
  65. switch(type){
  66. case MOVES.SELECT :
  67. var opp = getOther(move.who);
  68. if(!isCorrectSelectIndex(move.data.selectIndex))return false;
  69. if(_gameState.selected[opp][move.data[opp] % 6])return false;
  70. if(_gameState.hidden[move.who][move.data.selectIndex])return false; // already selected
  71. if(move.data[move.who].length != 66)return false;
  72. return true;
  73. case MOVES.PROOF :
  74. var data = move.data[move.who];
  75. //if(web3.sha3(JSON.stringify(data)) != _roundStack[SELECTINDEXES.indexOf(move.data.selectIndex%4)][move.who].data[move.who])return false;
  76. if(_gameState.selected[getOther(move.who)][move.data[move.who] % 6])return false;
  77. if(_gameState.hidden[move.who][move.data.selectIndex%4] != web3.sha3(JSON.stringify(data)))return false;
  78. return true;
  79. case MOVES.BET :
  80. var data = move.data.amount;
  81. if( data < 0 || parseInt(data) != data)return false;
  82. if( _gameState.coins[move.who] < data )return false;
  83. if( _gameState.coins[getOther(move.who)] < data)return false;
  84. return true;
  85. case MOVES.FOLD :
  86. if( _gameState.bets[getOther(move.who)] < _gameState.bets[move.who] )return false;
  87. return true;
  88. case MOVES.CALL :
  89. return true;
  90. }
  91. };
  92. web3.Game.performMove = function(move){ // GAME STATE CHANGING FUNCTION
  93. if(!this.isValidMove(move))return false;
  94. switch(move.type){
  95. case MOVES.SELECT :
  96. var opp = getOther(move.who);
  97. _gameState.selected[opp][move.data[opp] % 6] = SELECTINDEXES[_roundStack.length];
  98. _gameState.hidden[move.who][move.data.selectIndex] = move.data[move.who]; // Dictionary of hidden hashes
  99. break;
  100. case MOVES.PROOF :
  101. var data = move.data[move.who];
  102. if(web3.sha3(JSON.stringify(data)) != _gameState.hidden[move.who][this.getCurrentProofIndex()%4])return false;
  103. _gameState.hidden[move.who][this.getCurrentProofIndex()%4] = data;
  104. break;
  105. case MOVES.BET :
  106. var data = move.data.amount;
  107. _gameState.coins[move.who] -= data;
  108. _gameState.pot += data;
  109. _gameState.bets[move.who] = data;
  110. break;
  111. case MOVES.FOLD :
  112. _gameState.fold[move.who] = true;
  113. break;
  114. case MOVES.CALL :
  115. var def = Math.step(_gameState.bets[getOther(move.who)] - _gameState.bets[move.who]);
  116. _gameState.coins[move.who] -= def;
  117. _gameState.pot += def;
  118. break;
  119. }
  120. return this.saveMove(move);
  121. };
  122. web3.Game.getCurrentProofIndex = function(){
  123. return SELECTINDEXES[_roundStack.length];
  124. };
  125. web3.Game.saveProof = function(index, proof){
  126. this.proofs[index] = proof;
  127. };
  128. // SAVE ROUND //
  129. web3.Game.saveRound = function(){
  130. _stack.push(_roundStack);
  131. _roundStack = [];
  132. // TODO calculate winner!!!
  133. //
  134. _isAlreadySelected[web3.eth.defaultAccount] = [0,0,0,0,0,0];
  135. _isAlreadySelected[web3.eth.opponentAccount] = [0,0,0,0,0,0];
  136. _gameState.coins[web3.eth.defaultAccount] -= 1;
  137. _gameState.coins[web3.eth.opponentAccount] -= 1;
  138. _gameState.hidden[web3.eth.defaultAccount] = {};
  139. _gameState.hidden[web3.eth.opponentAccount] = {};
  140. _gameState.pot = 2;
  141. _gameState.round++;
  142. _gameState.roundstate = 0;
  143. _gameState.bets[web3.eth.defaultAccount] = 0;
  144. _gameState.bets[web3.eth.opponentAccount] = 0
  145. _gameState.selected[web3.eth.defaultAccount] = [0,0,0,0,0,0];
  146. _gameState.selected[web3.eth.opponentAccount] = [0,0,0,0,0,0];
  147. _gameState.fold[web3.eth.defaultAccount] = false;
  148. _gameState.fold[web3.eth.opponentAccount] = false;
  149. this.proofs = {};
  150. this.saveToDB();
  151. if(_onRoundChange)_onRoundChange();
  152. return true;
  153. };
  154. // GAME STATE ///
  155. web3.Game.saveState = function(){
  156. _roundStack.push(currentState);
  157. _gameState.roundstate ++;
  158. currentState = {};
  159. if(_onStateChange)_onStateChange();
  160. if(this.isValidRound())return this.saveRound();
  161. this.saveToDB();
  162. return true;
  163. };
  164. web3.Game.checkRound = function(){
  165. if(this.isValidRound())return this.saveRound();
  166. return false;
  167. }
  168. web3.Game.setOnStateChange = function(fn){_onStateChange = fn;}
  169. web3.Game.clearOnStateChange = function(){delete _onStateChange;}
  170. web3.Game.setOnRoundChange = function(fn){_onRoundChange = fn;}
  171. web3.Game.clearOnRoundChange = function(){delete _onRoundChange;}
  172. // ROUND //
  173. // function hasRoundFinished(){
  174. // //_roundStack;
  175. // if(web3.Game.isValidRound(currentState)){
  176. // web3.Game.saveRound();
  177. // }
  178. // else{
  179. // // Something is invalid!!!
  180. // console.log(currentState);
  181. // throw new Error("This state is not valid! ^^");
  182. // }
  183. // }
  184. web3.Game._givemoney = function(addr){
  185. _gameState.coins[addr] += _gameState.pot;
  186. _gameState.pot = 0;
  187. };
  188. web3.Game.isValidRound = function(){
  189. if(_gameState.fold[web3.eth.defaultAccount]){
  190. this._givemoney(web3.eth.opponentAccount);
  191. return true;
  192. }
  193. if(_gameState.fold[web3.eth.opponentAccount]){
  194. this._givemoney(web3.eth.defaultAccount);
  195. return true;
  196. }
  197. if(_gameState.roundstate == SELECTINDEXES.length){
  198. var sums = this.findSums();
  199. if(sums[web3.eth.defaultAccount] > sums[web3.eth.opponentAccount]){ // TODO - check equality
  200. this._givemoney(web3.eth.defaultAccount);
  201. return true;
  202. }
  203. else{
  204. this._givemoney(web3.eth.opponentAccount);
  205. return true;
  206. }
  207. }
  208. return false;
  209. };
  210. // MOVE CREATION ///
  211. web3.Game._createMove = function (type, data){
  212. if( type != web3.Game.MOVES.SELECT &&
  213. type != web3.Game.MOVES.BET &&
  214. type != web3.Game.MOVES.FOLD &&
  215. type != web3.Game.MOVES.CALL &&
  216. type != web3.Game.MOVES.PROOF &&
  217. type != web3.Game.MOVES.LEAVE )
  218. throw new Error("This type of move doesnt exist. Type : " + type);
  219. var move = {
  220. who : web3.eth.defaultAccount,
  221. type : type,
  222. data : JSON.parse(JSON.stringify(data))
  223. //lastState : _calculateLastStateHash()
  224. };
  225. return move;
  226. };
  227. web3.Game.createSelectMove = function (mdata1, mdata2){
  228. if(!this.isAvailableNow(MOVES.SELECT))throw new Error("This type of move is not available now");
  229. data1 = this.applyRotation(mdata1);
  230. data2 = this.applyRotation(mdata2);
  231. if(data1 < 0 || data1 > 5 )throw new Error("A SELECT Move is always between [0-5]");
  232. if(data2 < 0 || data2 > 5 )throw new Error("A SELECT Move is always between [0-5]");
  233. if(_gameState.selected[web3.eth.opponentAccount][data2])throw new Error("You cannot select an already selected column");
  234. if(_isAlreadySelected[web3.eth.defaultAccount][data1])throw new Error("You cannot select an already selected column");
  235. var cryptoData1 = _findRandomBigNumber(data1, 6);
  236. var cryptoData2 = _findRandomBigNumber(data2, 6);
  237. _isAlreadySelected[web3.eth.defaultAccount][data1] = SELECTINDEXES[_roundStack.length];
  238. var cryptoData = {
  239. selectIndex : SELECTINDEXES[_roundStack.length]
  240. };
  241. cryptoData[web3.eth.defaultAccount] = web3.sha3(''+cryptoData1);
  242. cryptoData[web3.eth.opponentAccount] = cryptoData2;
  243. var move = this._createMove(web3.Game.MOVES.SELECT, cryptoData);
  244. cryptoData[web3.eth.defaultAccount] = cryptoData1;
  245. var proof = this.createProofMove(cryptoData);
  246. return {
  247. move : move,
  248. proof: proof
  249. };
  250. };
  251. web3.Game.createBetMove = function (amount){
  252. if(!this.isAvailableNow(MOVES.BET))throw new Error("This type of move is not available now");
  253. if(amount < 0 || parseInt(amount) != amount)throw new Error("Amount parameter must be positive integer or 0!")
  254. if(amount > _gameState.coins[web3.eth.defaultAccount])throw new Error("Not enough coins to bet! Current Coins:" + _gameState.coins[web3.eth.defaultAccount] + " Amount requested:" + amount );
  255. if(amount > _gameState.coins[web3.eth.opponentAccount])throw new Error("Not enough opponent coins to bet! Current Coins:" + _gameState.coins[web3.eth.opponentAccount] + " Amount requested:" + amount );
  256. var cryptoData = {
  257. amount : amount
  258. };
  259. var move = this._createMove(web3.Game.MOVES.BET, cryptoData);
  260. return move;
  261. };
  262. web3.Game.createCallMove = function (data){
  263. if(!this.isAvailableNow(MOVES.CALL))throw new Error("This type of move is not available now");
  264. var cryptoData = {
  265. };
  266. var move = this._createMove(web3.Game.MOVES.CALL, cryptoData);
  267. return move;
  268. };
  269. web3.Game.createFoldMove = function (data){
  270. if(!this.isAvailableNow(MOVES.FOLD))throw new Error("This type of move is not available now");
  271. var cryptoData = {
  272. };
  273. var move = this._createMove(web3.Game.MOVES.FOLD, cryptoData);
  274. return move;
  275. };
  276. web3.Game.createProofMove = function (data){
  277. var r = this._createMove(web3.Game.MOVES.PROOF, data);
  278. return r;
  279. };
  280. web3.Game.getProofMove = function (index){
  281. return this.proofs[index];
  282. };
  283. web3.Game.saveToDB = function(){
  284. web3.db.putString(web3.db.DBNAME,"THEREAREDATA","true");
  285. web3.db.putString(web3.db.DBNAME,"_stack",JSON.stringify(_stack));
  286. web3.db.putString(web3.db.DBNAME,"_roundStack",JSON.stringify(_roundStack));
  287. web3.db.putString(web3.db.DBNAME,"_isAlreadySelected",JSON.stringify(_isAlreadySelected));
  288. web3.db.putString(web3.db.DBNAME,"_currentState",JSON.stringify(currentState));
  289. web3.db.putString(web3.db.DBNAME,"_gameState",JSON.stringify(_gameState));
  290. web3.db.putString(web3.db.DBNAME,"_stack",JSON.stringify(_stack));
  291. web3.db.putString(web3.db.DBNAME,"proofs",JSON.stringify(this.proofs));
  292. }
  293. web3.Game.clearDB = function(){
  294. web3.db.putString(web3.db.DBNAME,"THEREAREDATA",undefined);
  295. }
  296. web3.Game.loadFromDB = function(){
  297. if(!web3.db.getString(web3.db.DBNAME,"THEREAREDATA"))return;
  298. _stack = JSON.parse(web3.db.getString(web3.db.DBNAME,"_stack"));
  299. _roundStack = JSON.parse(web3.db.getString(web3.db.DBNAME,"_roundStack"));
  300. _isAlreadySelected = JSON.parse(web3.db.getString(web3.db.DBNAME,"_isAlreadySelected"));
  301. _currentState = JSON.parse(web3.db.getString(web3.db.DBNAME,"_currentState"));
  302. _gameState = JSON.parse(web3.db.getString(web3.db.DBNAME,"_gameState"));
  303. _stack = JSON.parse(web3.db.getString(web3.db.DBNAME,"_stack"));
  304. this.proofs = JSON.parse(web3.db.getString(web3.db.DBNAME,"proofs"));
  305. }
  306. //////////////////////
  307. /// GAME HELPERS ////
  308. web3.Game.calculateMySum =function (){
  309. };
  310. web3.Game.calculateMyMoney = function(){
  311. };
  312. web3.Game.getMapArray = function(){
  313. return this._maparray;
  314. };
  315. web3.Game.getState = function(){
  316. return _gameState;
  317. };
  318. web3.Game.getStack = function(){
  319. return _stack;
  320. };
  321. web3.Game.getRoundStack = function(){
  322. return _roundStack;
  323. };
  324. web3.Game.haveSelected = function(i){
  325. i = this.applyRotation(i);
  326. return _isAlreadySelected[web3.eth.defaultAccount][i] != 0;
  327. };
  328. web3.Game.isMyNumber = function(i,j){
  329. j = this.applyRotation(j);
  330. return _isAlreadySelected[web3.eth.defaultAccount][j] == _gameState.selected[web3.eth.defaultAccount][i]
  331. && _isAlreadySelected[web3.eth.defaultAccount][j] != 0;
  332. };
  333. web3.Game.findSums = function(){
  334. var sums = {
  335. [web3.eth.defaultAccount] : 0,
  336. [web3.eth.opponentAccount]: 0
  337. };
  338. var map = calculateMapArray(this.maphash, this._mapRotation);
  339. for(var i=0;i<6;i++)
  340. for (var j = 0; j < 6; j++)
  341. if(web3.Game.isMyNumber(i,j))
  342. sums[web3.eth.defaultAccount] += map[i][j];
  343. else if(web3.Game.isOpponentNumber(i,j))
  344. sums[web3.eth.opponentAccount] += map[i][j];
  345. return sums;
  346. };
  347. web3.Game.isOpponentNumber = function(i,j){
  348. j = this.applyRotation(j);
  349. return _gameState.hidden[web3.eth.opponentAccount][_gameState.selected[web3.eth.opponentAccount][j]] % 6 == i && typeof _gameState.hidden[web3.eth.opponentAccount][_gameState.selected[web3.eth.opponentAccount][j]] == "number" ;
  350. };
  351. web3.Game.reset = function(){
  352. web3.Game.init();
  353. }
  354. web3.Game.setMapRotation = function(r){
  355. this._mapRotation = r;
  356. this._maparray = calculateMapArray(this.maphash, this._mapRotation);
  357. };
  358. web3.Game.getMapRotation = function(){
  359. return web3.Game._mapRotation;
  360. };
  361. web3.Game.applyRotation = function(d){
  362. if(this._mapRotation == 1){
  363. d = 5 - d;
  364. }
  365. return d;
  366. };
  367. var _applyStateRotation = function(arr){
  368. var a = [];
  369. for(var i = 0; i < arr.length; i ++){
  370. a.push(arr[5-i]);
  371. }
  372. return a;
  373. };
  374. web3.Game.applyStateRotation = function(s){
  375. var r = JSON.parse(JSON.stringify(s));
  376. if(this._mapRotation == 1){
  377. r.selected[web3.eth.opponentAccount] = _applyStateRotation(s.selected[web3.eth.opponentAccount]);
  378. }
  379. return r;
  380. };
  381. /// INIT //
  382. web3.Game.init = function(mapHash){
  383. this.maphash = mapHash;
  384. this._maparray = calculateMapArray(this.maphash);
  385. _stack = [];
  386. _roundStack = [];
  387. currentState = {};
  388. _isAlreadySelected[web3.eth.defaultAccount] = [0,0,0,0,0,0];
  389. _isAlreadySelected[web3.eth.opponentAccount]= [0,0,0,0,0,0];
  390. _gameState = {
  391. coins : {
  392. [web3.eth.defaultAccount] : 99, // TODO CHANGE web3.eth.defaultAccount and opAcc to be settable to object
  393. [web3.eth.opponentAccount]: 99
  394. },
  395. selected :{
  396. [web3.eth.defaultAccount] : [0,0,0,0,0,0],
  397. [web3.eth.opponentAccount]: [0,0,0,0,0,0]
  398. },
  399. pot : 2,
  400. round : 1,
  401. roundstate : 0,
  402. bets : {
  403. [web3.eth.defaultAccount] : 0,
  404. [web3.eth.opponentAccount]: 0
  405. },
  406. fold : {
  407. [web3.eth.defaultAccount] : 0,
  408. [web3.eth.opponentAccount]: 0
  409. },
  410. hidden :{
  411. [web3.eth.defaultAccount] : {},
  412. [web3.eth.opponentAccount]: {}
  413. }
  414. };
  415. this.proofs = {};
  416. };
  417. // STATE INTERCEPTOR
  418. web3.Game._stateInterceptor = function(data, original){
  419. if(data.signature.data.stateConflict){
  420. if( data.signature.data.myCurrentState ){
  421. // verify and reset Game
  422. return false;
  423. }
  424. // send My state
  425. var data = {
  426. myCurrentState : {
  427. stack : web3.Game.getStack()
  428. }
  429. };
  430. return false;
  431. }
  432. return data;
  433. };
  434. function isCorrectSelectIndex(index){
  435. return SELECTINDEXES[_roundStack.length] == index;
  436. }
  437. // PRIVATE HELPERS //
  438. function _calculateLastStateHash(){
  439. var hash = web3.sha3(_gameState);
  440. return hash;
  441. }
  442. function _findRandomBigNumber(num, field){
  443. var randomNumber = Math.floor(10000000000 + Math.random() * 90000000000);
  444. while(randomNumber % field != num){
  445. randomNumber++;
  446. }
  447. return randomNumber;
  448. }
  449. function getOther(acc){
  450. return acc == web3.eth.defaultAccount ? web3.eth.opponentAccount : web3.eth.defaultAccount;
  451. }
  452. /////////////////////
  453. //// CALCULATIONS ///
  454. /////////////////////
  455. function hashToNumbers(hash){
  456. var nums = [];
  457. hash = hash.substr(2);
  458. for(var i=0;i<32;i++){
  459. var b = hash.substr(2*i, 2*i+2);
  460. nums.push(web3.toDecimal('0x'+b));
  461. }
  462. return nums;
  463. }
  464. /////////////////////
  465. function getNewMapHash(mapHash){
  466. return web3.sha3(web3.sha3(web3.sha3(mapHash)));
  467. }
  468. function calculateMapArray(mapHash, rot){
  469. console.log("Calculating map array for map hash: " + mapHash);
  470. var first32Hash = mapHash,
  471. second32Hash = web3.sha3(first32Hash),
  472. third32Hash = web3.sha3(second32Hash);
  473. // Make hash to numbers
  474. var numbers = hashToNumbers(first32Hash)
  475. .concat(hashToNumbers(second32Hash))
  476. .concat(hashToNumbers(third32Hash));
  477. // init mapArray
  478. var maparray = [];
  479. for(var i=0;i<6;i++){
  480. maparray.push([]);
  481. for(var j=0;j<6;j++){
  482. maparray[i][j] = i*6 + j + 1;
  483. }
  484. }
  485. ///
  486. /// helper local swapPosition
  487. function swapPositions(a,b){
  488. a = a % 36
  489. ai = a % 6;
  490. aj = parseInt(a / 6);
  491. b = b % 36
  492. bi = b % 6;
  493. bj = parseInt(b / 6);
  494. var temp = maparray[ai][aj];
  495. maparray[ai][aj] = maparray[bi][bj];
  496. maparray[bi][bj] = temp;
  497. }
  498. ///
  499. for(var i=0;i<numbers.length;i++){
  500. swapPositions(i, numbers[i]);
  501. }
  502. ////////////////////////////////
  503. //console.log(maparray);
  504. ////////////////////////////////
  505. // Rotate based on rot
  506. if(rot == 1){
  507. var f = 3;
  508. var c = 3;
  509. var n = 6;
  510. for(var k = 0;k < 3;k++)
  511. for( var x = 0; x < f; x++ ){
  512. for(var y = 0; y <c;y++ ){
  513. var temp = maparray[x][y];
  514. maparray[x][y] = maparray[y][n-1-x];
  515. maparray[y][n-1-x] = maparray[n-1-x][n-1-y];
  516. maparray[n-1-x][n-1-y] = maparray[n-1-y][x];
  517. maparray[n-1-y][x] = temp;
  518. }
  519. }
  520. }
  521. return maparray;
  522. }
  523. // Math Helpers
  524. Math.step = function(x){return x < 0 ? 0 : x;};
  525. })(web3);