diff --git a/app/css/dball.scss b/app/css/dball.scss new file mode 100644 index 00000000..2820bf9a --- /dev/null +++ b/app/css/dball.scss @@ -0,0 +1,97 @@ +.dball-container { + + .ball-wrapper { + padding-bottom: 7px; + padding-right: 8px; + -moz-user-select: none; + cursor: pointer; + + .ball-default, .ball-red, .ball-blue { + width: 39px; + height: 37px; + line-height: 36px; + text-align: center; + font-weight: bold; + cursor: pointer; + font-size: 14px; + font-family: 微软雅黑 arial; + background: url(/img/dball-assets.png) no-repeat; + } + + .ball-blue-hover { + background-position: -95px 0; + color: #ca1f27; + } + + .ball-default { + background-position: -55px 0; + color: #333; + } + + .ball-red { + background-position: -135px 0; + color: #fff; + } + + .ball-blue { + background-position: -215px 0; + color: #fff; + } + } + + .title-section { + margin-bottom:2em; + + .title { + margin:0 1em 0 0; + } + } + + .blue-numbers { + background-color: #ececf7; + padding: 1em; + + .ball-default:hover, .ball-default:active { + background-position: -175px 0; + color: #0d3f73; + } + + .title { color: #0d3f73; } + } + + .red-numbers { + background-color: #ffebef; + padding: 1em; + + .ball-default:hover, .ball-default:active { + background-position: -95px 0; + color: #ca1f27; + } + + .title { color: red; } + } + + .bet-notice { + margin:0 5px; + .red-numbers, .blue-numbers { padding:0; background-color: transparent; } + .ball-wrapper { zoom: 0.8; } + } + + .result-pane{ + .result{ + padding:2px 5px; color:#666; font-weight:bold; + display:inline-block; background-color:#ccc; + &.win { + color:white; + background-color:green; + } + &.lose { + color:white; + background-color:red; + } + } + .ball-wrapper { + zoom: 0.7; + } + } +} \ No newline at end of file diff --git a/app/css/main.scss b/app/css/main.scss index ab62ba6a..ac15d11b 100644 --- a/app/css/main.scss +++ b/app/css/main.scss @@ -28,6 +28,7 @@ $default_blue: #3f51b5; @import 'trollbox'; @import 'packets'; @import 'wheel'; +@import 'dball'; /* Fonts */ diff --git a/app/img/allbgs.png b/app/img/allbgs.png new file mode 100644 index 00000000..9796165a Binary files /dev/null and b/app/img/allbgs.png differ diff --git a/app/img/dball-assets.png b/app/img/dball-assets.png new file mode 100644 index 00000000..bd1b366c Binary files /dev/null and b/app/img/dball-assets.png differ diff --git a/app/img/dcolorball-sample.png b/app/img/dcolorball-sample.png new file mode 100644 index 00000000..4afc31c6 Binary files /dev/null and b/app/img/dcolorball-sample.png differ diff --git a/app/img/header-dcolorball@300.jpg b/app/img/header-dcolorball@300.jpg new file mode 100644 index 00000000..cd2ddcdc Binary files /dev/null and b/app/img/header-dcolorball@300.jpg differ diff --git a/app/img/header-placeholder@300.jpg b/app/img/header-placeholder@300.jpg new file mode 100644 index 00000000..82c17a26 Binary files /dev/null and b/app/img/header-placeholder@300.jpg differ diff --git a/app/js/app.coffee b/app/js/app.coffee index 180dcb05..57e2a0bf 100644 --- a/app/js/app.coffee +++ b/app/js/app.coffee @@ -12,12 +12,14 @@ window.open_external_url = (url) -> else window.open(url) +# debugger +window.getHandlers = -> + # get service + services = ['Idle', 'Wallet', 'Info'] + window[service] = angular.element(document.body).injector().get(service) for service in services app = angular.module("app", - ["ngResource", "ui.router", "ngIdle", "app.services", "app.directives", "ui.bootstrap", - "ui.validate", "xeditable", "pascalprecht.translate", "pageslide-directive", "ui.grid", - "ngMaterial", "utils.autofocus", "ngMessages", "ui.grid.autoResize", "ngAnimate", - "angular-carousel", "ng-mfb", "angular-growl"]) + ["ngResource", "ui.router", "ngIdle", "app.services", "app.directives", "ui.bootstrap", "ui.validate", "xeditable", "pascalprecht.translate", "pageslide-directive", "ui.grid", "ngMaterial", "utils.autofocus", "ngMessages", "ui.grid.autoResize", "ngAnimate", "angular-carousel","ng-mfb", "angular-growl"]) app.run ($rootScope, $location, Idle, $state, $interval, $window, $templateCache, $translate, editableOptions, editableThemes) -> @@ -106,10 +108,13 @@ app.run ($rootScope, $location, Idle, $state, $interval, $window, $templateCache app.config ($mdThemingProvider, IdleProvider, $translateProvider, $uibTooltipProvider, $compileProvider) -> $mdThemingProvider.theme('default').primaryPalette('indigo') - $compileProvider.debugInfoEnabled false $mdThemingProvider.theme("warning-toast") $mdThemingProvider.theme("notice-toast") $mdThemingProvider.theme("error-toast") + + # $touchProvider.ngClickOverrideEnabled(true); + + $compileProvider.debugInfoEnabled true # set this to false in production to gain performance boost # use angular.reloadWithDebugInfo() to reload the page and obtain debug capability diff --git a/app/js/controllers/apps.coffee b/app/js/controllers/apps.coffee index 65f4cbeb..29a14a12 100644 --- a/app/js/controllers/apps.coffee +++ b/app/js/controllers/apps.coffee @@ -2,6 +2,10 @@ angular.module("app").controller "AppsController", ($scope) -> $scope.apps = [ { id: "packet", heading: "index.packets", route: 'packets', icon: "fa-envelope", header_image: "/img/header-packets@300.jpg" }, { id: "notes", heading: "index.notes", route: 'notebooks', icon: "fa-flag", header_image: "/img/header-notes@300.jpg" }, - # { id: "dice", heading: "index.dices", route: 'dice', icon: "fa-flag", header_image: "/img/header-dice@300.jpg" }, - { id: "wheel", heading: "index.wheel", route: 'wheel', icon: "fa-flag", header_image: "/img/header-wheel@300.jpg" }, + { id: "scolorball", heading: "index.scolorball", route: 'scolorball', icon: "fa-flag", header_image: "/img/header-dcolorball@300.jpg" }, + { id: "dcolorball", heading: "index.dcolorball", route: 'dcolorball', icon: "fa-flag", header_image: "/img/header-dcolorball@300.jpg" }, + { id: "wheel", heading: "index.wheel", route: 'wheel', icon: "fa-flag", header_image: "/img/header-wheel@300.jpg", disabled: true }, + { id: "dice", heading: "index.dices", route: 'dice', icon: "fa-flag", header_image: "/img/header-dice@300.jpg", disabled: true }, + { id: "placeholder", heading: "index.placeholder", route: 'dice', icon: "fa-flag", header_image: "/img/header-placeholder@300.jpg", disabled: true }, + { id: "placeholder", heading: "index.placeholder", route: 'dice', icon: "fa-flag", header_image: "/img/header-placeholder@300.jpg", disabled: true }, ] \ No newline at end of file diff --git a/app/js/controllers/dcolorball.coffee b/app/js/controllers/dcolorball.coffee new file mode 100644 index 00000000..b8a97a6a --- /dev/null +++ b/app/js/controllers/dcolorball.coffee @@ -0,0 +1,396 @@ +angular.module("app").controller "DColorBallController", ($scope, $mdDialog, $stateParams, BlockchainAPI, Observer, Utils, Wallet, $rootScope, RpcService, Info, GameAPI, Growl) -> + $scope.game_name = 'dice' + $scope.chip_asset_name = 'DICE' + # the name within play trx memo, if multiple games share one contract + # this needs to be set and it's probably different from game name + $scope.memo_game_name = 'dice' + $scope.chip_asset = null + $scope.chip_price = null + $scope.reveal_block_distance = 10 + + # game modes + # support 3 different static modes at the moment + # red balls: + # 1. 2 / 5 + # 2. 5 / 10 + # 3. 5 / 15 + # blue balls: + # always select 1 blue ball + # 1 / 4 + modes = + "2/5": + label: "2/5", + red: [2,5], + blue: [1,4], + + "5/10": + label: "5/10", + red: [5,10], + blue: [1,4], + + "5/15": + label: "5/15", + red: [5,15], + blue: [1,4], + + # compute Ns + for section, mode of modes + mode.red_n = Combinatorics.C(mode.red[1], mode.red[0]) + mode.blue_n = Combinatorics.C(mode.blue[1], mode.blue[0]) + mode.combined_n = mode.red_n * mode.blue_n + + # game mode list + $scope.gameModes = modes + + # default mode + $scope.currentMode = "2/5"; + + # max allowed bet amount, default 1000 + # will be set to current_account's balance when the value is ready + $scope.bet_max = 1000 + + # user selection + # [[red selections], [blue selections]] + $scope.selections = + red: [], + blue: [] + + # odds/betting params + $scope.seg_num = Math.floor(Math.random() * 15) + 2 # minimum 2 seg + $scope.guess_num = Math.floor(Math.random() * $scope.seg_num) + 2 + $scope.bet_amount = Math.floor(Math.random() * $scope.bet_max) + + $scope.current_account = null + $scope.current_account_name = null + $scope.current_balance = null + $scope.current_core_balance = null + + $scope.account_transactions = [] + + $scope.buychipForm = null + $scope.buychip = + amount: 1000 + + $scope.betForm = null + + $scope.lastBet = '' + + # store wheel instances, debug purpose + # disabled for now + $scope.ws = [] + + # initial display + $scope.$watch 'currentMode', (new_val, old_val) -> + return if new_val == old_val + + $scope.setGameMode(new_val) + + $scope.setGameMode = (mode) -> + return unless mode && $scope.gameModes[mode] + + $scope.currentMode = mode + $scope.ballNumbers = [ + [1..$scope.gameModes[$scope.currentMode].red[1]], + [1..$scope.gameModes[$scope.currentMode].blue[1]] + ] + + $scope.clearSelections() + + + $scope.getNumbers = (section) -> + return $scope.ballNumbers[$scope.getSectionId(section)] + + $scope.getSectionId = (section) -> + return if section == 'red' then 0 else 1 + + $scope.userSelect = (section, num) -> + selected = $scope.selections[section] + + # toggle selection + if (index = selected.indexOf(num)) != -1 + selected.splice(index, 1) + else + selected.push(num) + + $scope.selections[section] = selected + + # check if user selected this num + $scope.userSelected = (section, numToCheck) -> + selected = $scope.selections[section] + + return selected.indexOf(numToCheck) != -1 + + + $scope.randSelect = (section) -> + [reqNum, total] = $scope.gameModes[$scope.currentMode][section] + rands = Utils.shuffleArray([1..total])[0...reqNum] + + $scope.selections[section] = rands + + + $scope.clearSelection = (section) -> $scope.selections[section] = [] + $scope.clearSelections = -> $scope.selections = red:[], blue:[] + + $scope.alertPrize = (wheel)-> + # Get the segment indicated by the pointer on the wheel background which is at 0 degrees. + winning_segment = wheel.getIndicatedSegment() + + # Do basic alert of the segment text. You would probably want to do something more interesting with this information. + alert("You have won " + winningSegment.text) + + # decide min bet amount + $scope.getMinBetAmount = -> + unless $scope.current_balance + 100 + else + Math.min(100, parseInt($scope.current_balance.amount / $scope.current_balance.precision) / 20) + + # make step for 20 rounds + $scope.getBetStep = -> + unless $scope.current_balance + 100 + else + parseInt($scope.current_balance.amount / $scope.current_balance.precision / 20) + + $scope.showPurchaseDialog = (evt) -> + $mdDialog.show + parent: angular.element document.body + scope: $scope + preserveScope: true + templateUrl: "wheel/purchase.html" + targetEvent: evt + .then -> + $scope.fetchGameAssetPrice() + + $scope.cancel = -> + $mdDialog.cancel() + + $scope.hide = -> + $mdDialog.hide() + + $scope.fetchGameAssetPrice = -> + BlockchainAPI.get_asset($scope.chip_asset_name).then (asset) -> + return 0 unless asset && asset.current_collateral && asset.current_supply + $scope.chip_asset = asset + $scope.chip_price = Utils.formatDecimal((asset.current_collateral / ($scope.current_core_balance?.precision || 5)) / (asset.current_supply / asset.precision), 2) + + $scope.hotCheckBuyChipAmount = -> + return $scope.buychip.amount > 0 + + $scope.doPurchase = -> + return false unless $scope.hotCheckBuyChipAmount() + + GameAPI.buy_chips($scope.current_account_name, $scope.buychip.amount, $scope.chip_asset_name).then (response) -> + Growl.notice 'Success', '购买成功' + , (err) -> + error = err.data?.error || err.response?.data?.error + error_message = error.locale_message || error.message + Growl.error "", error_message + + $scope.hide() + + $scope.getOdds = -> + # return current game mode's n + # could be pre-calculated + $scope.gameModes[$scope.currentMode].combined_n + + # # forward rank + # rank = red_rank * blue_n + blue_rank + $scope.getRanking = -> + red_rank = Utils.ranking( $scope.selections.red ) + blue_rank = Utils.ranking( $scope.selections.blue ) || 0 + blue_n = $scope.gameModes[$scope.currentMode].blue_n + + rank = red_rank * blue_n + blue_rank + + # # unrank + # red_rank = (rank / blue_n).to_i + # blue_rank = rank % blue_n + $scope.revealResult = (rank, odds) -> + gameMode = $scope.getGameFromOdds(odds) + red_rank = parseInt(rank / gameMode.blue_n) + blue_rank = rank % gameMode.blue_n + + return { + red: Utils.unranking(red_rank, gameMode.red[0], gameMode.red[1]).map (x) -> x + 1 + blue: Utils.unranking(blue_rank, gameMode.blue[0], gameMode.blue[1]).map (x) -> x + 1 + } + + $scope.getGameFromOdds = (odds) -> + for section, mode of $scope.gameModes + if mode.combined_n == odds + return mode + + $scope.betted = -> + $scope.selections.red.length == parseInt($scope.currentMode.split('/')[0]) && + $scope.selections.blue.length == 1 + + $scope.placeBet = -> + gameParam = + from_account_name: $scope.current_account_name + amount: $scope.bet_amount + odds: $scope.getOdds() + guess: $scope.getRanking() + + console.log "placeBet:", JSON.stringify(gameParam) + GameAPI.play($scope.game_name, gameParam).then (resp) -> + console.log resp + + $scope.current_balance.amount -= gameParam.amount * $scope.current_balance.precision + + refresh_balance() + $scope.clearSelections() + + Growl.notice "", "下注:#{gameParam.amount},几率:1 / #{gameParam.odds}" + , (err) -> + error = err.data?.error || err.response?.data?.error + error_message = error.locale_message || error.message + Growl.error "", error_message + + + $scope.calculate_waiting_blocks = (trx) -> + return "..." unless trx.is_confirmed + + $scope.reveal_block_distance - (Info.info.last_block_num - trx.block_num) + + + refresh_transactions = -> + console.log 'refresh_transactions' + Wallet.refresh_transactions().then -> + return unless Wallet.transactions[$scope.current_account_name]?.length > 0 + + # store result transactions temporarily + checkIds = [] + checkTrx = [] + idBlockMap = {} + + i = 0 + $scope.account_transactions = Wallet.transactions[$scope.current_account_name].map (trx) -> + # ledger entry + lentry = trx.ledger_entries[0] + + # this should be play trx + if !trx.is_virtual and lentry.to == $scope.current_account_name and lentry.memo == "play #{$scope.memo_game_name}" + checkIds.push trx.block_num + checkTrx.push trx.id + + # construct a map for faster look up + if !idBlockMap[trx.block_num] + idBlockMap[trx.block_num] = [i] + else + idBlockMap[trx.block_num].push i + + i++ + trx + + else + null + + .filter (x) -> + x != null + + # console.log 'account_transactions', $scope.account_transactions.map (x)-> x.block_num + # console.log 'idBlockMap' + # for block_num, index of idBlockMap + # console.log "#{block_num} => #{index}" + + ids = Utils.unique_array(checkIds).map (x)->[x+$scope.reveal_block_distance] + trxes = Utils.unique_array(checkTrx).map (x)->[x] + + RpcService.request('batch', ['blockchain_get_transaction', trxes]).then (resp) -> + l = resp.result.length + if l > 0 + i = 0 + for trx_data in (resp.result.map (x) -> x[1]) + bet = -1 + for op in trx_data.trx.operations + if op.type == 'game_play_op_type' + bet = op.data.input.data + block_num = trx_data.chain_location.block_num + trx_ids = idBlockMap[block_num] + trx = $scope.account_transactions[trx_ids[0]] + if bet != -1 + trx.bet = bet + trx.bet.combination = $scope.revealResult(bet.guess, bet.odds) + break + i++ + + RpcService.request('batch', ['game_list_result_transactions', ids]).then (resp) -> + reveals = resp.result + + game_ns = [] + game_ns.push mode.combined_n for section, mode of $scope.gameModes + + l = reveals.length + if l > 0 + i = 0 + for datas in reveals + block_num = ids[i] + + j = 0 + trx_ids = idBlockMap[block_num - $scope.reveal_block_distance] + # console.log 'trx_ids', trx_ids + while datas.length > 0 + data = datas.shift() + + # check if this belongs to this game's odds + if (game_ns.indexOf(data.data.odds) == -1) + $scope.account_transactions.splice(trx_ids[j],1) + break + + trx = $scope.account_transactions[trx_ids[j]] + trx.reveal = data + trx.reveal.combination = $scope.revealResult(data.data.lucky_number, data.data.odds) + + # this is the last bet result + if i == 0 and j == 0 + if data.data.jackpot_received > 0 + $scope.lastBet = 'win' + else + $scope.lastBet = 'lose' + # console.log 'lastBet', $scope.lastBet + + j++ + i++ + + + BlockchainAPI.get_asset($scope.chip_asset_name).then (asset) -> + $scope.chip_asset = asset + + $scope.$watch -> + Wallet.current_account + , (acct) -> + $scope.current_account = acct + $scope.current_account_name = acct?.name + + if acct?.name + refresh_balance() + refresh_transactions() + + refresh_balance = -> + Wallet.refresh_balances().then -> + $scope.current_core_balance = Wallet.balances[$scope.current_account_name]?[Info.info.symbol] + $scope.current_balance = Wallet.balances[$scope.current_account_name]?[$scope.chip_asset_name] + if $scope.current_balance?.amount > 0 + $scope.bet_max = $scope.current_balance.amount / $scope.current_balance.precision + # if old bet amount overflows, set it to bet max + $scope.bet_amount = $scope.bet_max if $scope.bet_max < $scope.bet_amount + else + $scope.bet_max = 0 + $scope.bet_amount = 0 + + $scope.fetchGameAssetPrice() + + block_observer = + name: "dice_block_observer" + frequency: "each_block" + update: (data, deferred) => + refresh_balance() + refresh_transactions() + $scope.last_block_num = Info.info.last_block_num + + deferred.resolve(true) + Observer.registerObserver(block_observer) + + $scope.$on "$destroy", -> + Observer.unregisterObserver(block_observer) diff --git a/app/js/controllers/scolorball.coffee b/app/js/controllers/scolorball.coffee new file mode 100644 index 00000000..f558c16f --- /dev/null +++ b/app/js/controllers/scolorball.coffee @@ -0,0 +1,399 @@ +angular.module("app").controller "SColorBallController", ($scope, $mdDialog, $stateParams, BlockchainAPI, Observer, Utils, Wallet, $rootScope, RpcService, Info, GameAPI, Growl) -> + $scope.game_name = 'dice' + $scope.chip_asset_name = 'DICE' + # the name within play trx memo, if multiple games share one contract + # this needs to be set and it's probably different from game name + $scope.memo_game_name = 'dice' + $scope.chip_asset = null + $scope.chip_price = null + $scope.reveal_block_distance = 10 + + # game modes + # support 3 different static modes at the moment + # red balls: + # 1. 2 / 5 + # 2. 5 / 10 + # 3. 5 / 15 + # blue balls: + # always select 1 blue ball + # 1 / 4 + modes = + "2/4": + label: "2/4", + red: [2,4], + blue: [1,1], + # n = 6 + + "3/6": + label: "3/6", + red: [3,6], + blue: [1,1], + # n = 20 + + "4/8": + label: "4/8", + red: [4,8], + blue: [1,1], + # n = 70 + + # compute Ns + for section, mode of modes + mode.red_n = Combinatorics.C(mode.red[1], mode.red[0]) + mode.blue_n = Combinatorics.C(mode.blue[1], mode.blue[0]) + mode.combined_n = mode.red_n * mode.blue_n + + # game mode list + $scope.gameModes = modes + + # default mode + $scope.currentMode = "2/4"; + + # max allowed bet amount, default 1000 + # will be set to current_account's balance when the value is ready + $scope.bet_max = 1000 + + # user selection + # [[red selections], [blue selections]] + $scope.selections = + red: [], + blue: [] + + # odds/betting params + $scope.seg_num = Math.floor(Math.random() * 15) + 2 # minimum 2 seg + $scope.guess_num = Math.floor(Math.random() * $scope.seg_num) + 2 + $scope.bet_amount = Math.floor(Math.random() * $scope.bet_max) + + $scope.current_account = null + $scope.current_account_name = null + $scope.current_balance = null + $scope.current_core_balance = null + + $scope.account_transactions = [] + + $scope.buychipForm = null + $scope.buychip = + amount: 1000 + + $scope.betForm = null + + $scope.lastBet = '' + + # store wheel instances, debug purpose + # disabled for now + $scope.ws = [] + + # initial display + $scope.$watch 'currentMode', (new_val, old_val) -> + return if new_val == old_val + + $scope.setGameMode(new_val) + + $scope.setGameMode = (mode) -> + return unless mode && $scope.gameModes[mode] + + $scope.currentMode = mode + $scope.ballNumbers = [ + [1..$scope.gameModes[$scope.currentMode].red[1]], + [1..$scope.gameModes[$scope.currentMode].blue[1]] + ] + + $scope.clearSelections() + + + $scope.getNumbers = (section) -> + return $scope.ballNumbers[$scope.getSectionId(section)] + + $scope.getSectionId = (section) -> + return if section == 'red' then 0 else 1 + + $scope.userSelect = (section, num) -> + selected = $scope.selections[section] + + # toggle selection + if (index = selected.indexOf(num)) != -1 + selected.splice(index, 1) + else + selected.push(num) + + $scope.selections[section] = selected + + # check if user selected this num + $scope.userSelected = (section, numToCheck) -> + selected = $scope.selections[section] + + return selected.indexOf(numToCheck) != -1 + + + $scope.randSelect = (section) -> + [reqNum, total] = $scope.gameModes[$scope.currentMode][section] + rands = Utils.shuffleArray([1..total])[0...reqNum] + + $scope.selections[section] = rands + + + $scope.clearSelection = (section) -> $scope.selections[section] = [] + $scope.clearSelections = -> $scope.selections = red:[], blue:[] + + $scope.alertPrize = (wheel)-> + # Get the segment indicated by the pointer on the wheel background which is at 0 degrees. + winning_segment = wheel.getIndicatedSegment() + + # Do basic alert of the segment text. You would probably want to do something more interesting with this information. + alert("You have won " + winningSegment.text) + + # decide min bet amount + $scope.getMinBetAmount = -> + unless $scope.current_balance + 100 + else + Math.min(100, parseInt($scope.current_balance.amount / $scope.current_balance.precision) / 20) + + # make step for 20 rounds + $scope.getBetStep = -> + unless $scope.current_balance + 100 + else + parseInt($scope.current_balance.amount / $scope.current_balance.precision / 20) + + $scope.showPurchaseDialog = (evt) -> + $mdDialog.show + parent: angular.element document.body + scope: $scope + preserveScope: true + templateUrl: "wheel/purchase.html" + targetEvent: evt + .then -> + $scope.fetchGameAssetPrice() + + $scope.cancel = -> + $mdDialog.cancel() + + $scope.hide = -> + $mdDialog.hide() + + $scope.fetchGameAssetPrice = -> + BlockchainAPI.get_asset($scope.chip_asset_name).then (asset) -> + return 0 unless asset && asset.current_collateral && asset.current_supply + $scope.chip_asset = asset + $scope.chip_price = Utils.formatDecimal((asset.current_collateral / ($scope.current_core_balance?.precision || 5)) / (asset.current_supply / asset.precision), 2) + + $scope.hotCheckBuyChipAmount = -> + return $scope.buychip.amount > 0 + + $scope.doPurchase = -> + return false unless $scope.hotCheckBuyChipAmount() + + GameAPI.buy_chips($scope.current_account_name, $scope.buychip.amount, $scope.chip_asset_name).then (response) -> + Growl.notice 'Success', '购买成功' + , (err) -> + error = err.data?.error || err.response?.data?.error + error_message = error.locale_message || error.message + Growl.error "", error_message + + $scope.hide() + + $scope.getOdds = -> + # return current game mode's n + # could be pre-calculated + $scope.gameModes[$scope.currentMode].combined_n + + # # forward rank + # rank = red_rank * blue_n + blue_rank + $scope.getRanking = -> + red_rank = Utils.ranking( $scope.selections.red ) + blue_rank = Utils.ranking( $scope.selections.blue ) || 0 + blue_n = $scope.gameModes[$scope.currentMode].blue_n + + rank = red_rank * blue_n + blue_rank + + # # unrank + # red_rank = (rank / blue_n).to_i + # blue_rank = rank % blue_n + $scope.revealResult = (rank, odds) -> + gameMode = $scope.getGameFromOdds(odds) + red_rank = parseInt(rank / gameMode.blue_n) + blue_rank = rank % gameMode.blue_n + + return { + red: Utils.unranking(red_rank, gameMode.red[0], gameMode.red[1]).map (x) -> x + 1 + blue: Utils.unranking(blue_rank, gameMode.blue[0], gameMode.blue[1]).map (x) -> x + 1 + } + + $scope.getGameFromOdds = (odds) -> + for section, mode of $scope.gameModes + if mode.combined_n == odds + return mode + + $scope.betted = -> + $scope.selections.red.length == parseInt($scope.currentMode.split('/')[0]) && + $scope.selections.blue.length == 1 + + $scope.placeBet = -> + gameParam = + from_account_name: $scope.current_account_name + amount: $scope.bet_amount + odds: $scope.getOdds() + guess: $scope.getRanking() + + console.log "placeBet:", JSON.stringify(gameParam) + GameAPI.play($scope.game_name, gameParam).then (resp) -> + console.log resp + + $scope.current_balance.amount -= gameParam.amount * $scope.current_balance.precision + + refresh_balance() + $scope.clearSelections() + + Growl.notice "", "下注:#{gameParam.amount},几率:1 / #{gameParam.odds}" + , (err) -> + error = err.data?.error || err.response?.data?.error + error_message = error.locale_message || error.message + Growl.error "", error_message + + + $scope.calculate_waiting_blocks = (trx) -> + return "..." unless trx.is_confirmed + + $scope.reveal_block_distance - (Info.info.last_block_num - trx.block_num) + + + refresh_transactions = -> + console.log 'refresh_transactions' + Wallet.refresh_transactions().then -> + return unless Wallet.transactions[$scope.current_account_name]?.length > 0 + + # store result transactions temporarily + checkIds = [] + checkTrx = [] + idBlockMap = {} + + i = 0 + $scope.account_transactions = Wallet.transactions[$scope.current_account_name].map (trx) -> + # ledger entry + lentry = trx.ledger_entries[0] + + # this should be play trx + if !trx.is_virtual and lentry.to == $scope.current_account_name and lentry.memo == "play #{$scope.memo_game_name}" + checkIds.push trx.block_num + checkTrx.push trx.id + + # construct a map for faster look up + if !idBlockMap[trx.block_num] + idBlockMap[trx.block_num] = [i] + else + idBlockMap[trx.block_num].push i + + i++ + trx + + else + null + + .filter (x) -> + x != null + + # console.log 'account_transactions', $scope.account_transactions.map (x)-> x.block_num + # console.log 'idBlockMap' + # for block_num, index of idBlockMap + # console.log "#{block_num} => #{index}" + + ids = Utils.unique_array(checkIds).map (x)->[x+$scope.reveal_block_distance] + trxes = Utils.unique_array(checkTrx).map (x)->[x] + + RpcService.request('batch', ['blockchain_get_transaction', trxes]).then (resp) -> + l = resp.result.length + if l > 0 + i = 0 + for trx_data in (resp.result.map (x) -> x[1]) + bet = -1 + for op in trx_data.trx.operations + if op.type == 'game_play_op_type' + bet = op.data.input.data + block_num = trx_data.chain_location.block_num + trx_ids = idBlockMap[block_num] + trx = $scope.account_transactions[trx_ids[0]] + if bet != -1 + trx.bet = bet + trx.bet.combination = $scope.revealResult(bet.guess, bet.odds) + break + i++ + + RpcService.request('batch', ['game_list_result_transactions', ids]).then (resp) -> + reveals = resp.result + + game_ns = [] + game_ns.push mode.combined_n for section, mode of $scope.gameModes + + l = reveals.length + if l > 0 + i = 0 + for datas in reveals + block_num = ids[i] + + j = 0 + trx_ids = idBlockMap[block_num - $scope.reveal_block_distance] + # console.log 'trx_ids', trx_ids + while datas.length > 0 + data = datas.shift() + + # check if this belongs to this game's odds + if (game_ns.indexOf(data.data.odds) == -1) + $scope.account_transactions.splice(trx_ids[j],1) + break + + trx = $scope.account_transactions[trx_ids[j]] + trx.reveal = data + trx.reveal.combination = $scope.revealResult(data.data.lucky_number, data.data.odds) + + # this is the last bet result + if i == 0 and j == 0 + if data.data.jackpot_received > 0 + $scope.lastBet = 'win' + else + $scope.lastBet = 'lose' + # console.log 'lastBet', $scope.lastBet + + j++ + i++ + + + BlockchainAPI.get_asset($scope.chip_asset_name).then (asset) -> + $scope.chip_asset = asset + + $scope.$watch -> + Wallet.current_account + , (acct) -> + $scope.current_account = acct + $scope.current_account_name = acct?.name + + if acct?.name + refresh_balance() + refresh_transactions() + + refresh_balance = -> + Wallet.refresh_balances().then -> + $scope.current_core_balance = Wallet.balances[$scope.current_account_name]?[Info.info.symbol] + $scope.current_balance = Wallet.balances[$scope.current_account_name]?[$scope.chip_asset_name] + if $scope.current_balance?.amount > 0 + $scope.bet_max = $scope.current_balance.amount / $scope.current_balance.precision + # if old bet amount overflows, set it to bet max + $scope.bet_amount = $scope.bet_max if $scope.bet_max < $scope.bet_amount + else + $scope.bet_max = 0 + $scope.bet_amount = 0 + + $scope.fetchGameAssetPrice() + + block_observer = + name: "dice_block_observer" + frequency: "each_block" + update: (data, deferred) => + refresh_balance() + refresh_transactions() + $scope.last_block_num = Info.info.last_block_num + + deferred.resolve(true) + Observer.registerObserver(block_observer) + + $scope.$on "$destroy", -> + Observer.unregisterObserver(block_observer) diff --git a/app/js/router.coffee b/app/js/router.coffee index 275a965f..45de9bb7 100644 --- a/app/js/router.coffee +++ b/app/js/router.coffee @@ -173,6 +173,16 @@ angular.module("app").config ($stateProvider, $urlRouterProvider, $locationProvi templateUrl: 'wheel/wheel.html' controller: 'WheelController' + sp.state "dcolorball", + url: prefix + '/dcolorball' + templateUrl: 'dcolorball/dcolorball.html' + controller: 'DColorBallController' + + sp.state "scolorball", + url: prefix + '/scolorball' + templateUrl: 'scolorball/scolorball.html' + controller: 'SColorBallController' + sp.state "dice", url: prefix + '/dice' templateUrl: 'dice/dice.html' diff --git a/app/js/services/utils.coffee b/app/js/services/utils.coffee index a592e4e6..72cc346e 100644 --- a/app/js/services/utils.coffee +++ b/app/js/services/utils.coffee @@ -286,3 +286,33 @@ angular.module("app.services").factory "Utils", ($translate,$q,$sce) -> newInstance[key] = @clone obj[key] return newInstance + + + # https://github.com/dacsunlimited/dac_play/blob/master/libraries/utilities/combinatorics.cpp + ranking: (comb) -> + rank = 0 + sorted = (comb.map (x) -> x - 1).sort() + for i in [1..sorted.length] + rank += Combinatorics.C(sorted[i-1], i) + + return rank + + # # https://github.com/dacsunlimited/dac_play/blob/master/libraries/utilities/combinatorics.cpp + unranking: (rank, k, n) -> + comb = [] + max = n + + for i in [k..1] + if rank <= 0 then comb.push i-1 + else + while max >= 1 + c_max_i = Combinatorics.C(max, i) + if rank >= c_max_i + comb.push max + rank -= c_max_i + break + + max-- + + return comb.sort() + diff --git a/app/static/locale-en.json b/app/static/locale-en.json index 8f964cac..2c8c8883 100644 --- a/app/static/locale-en.json +++ b/app/static/locale-en.json @@ -223,6 +223,8 @@ "apps": "APPs", "dices": "DICE", "wheel": "Super Lucky Wheel", + "scolorball": "Single Color Ball", + "dcolorball": "Double Color Ball", "chips": "Game Assets", "games": "Games", "market": "Market", @@ -235,7 +237,8 @@ "daily_income": "Network Daily Income", "daily_expense": "Network Daily Expense", "daily_burn": "Network Daily Burn", - "packets": "Red Packets" + "packets": "Red Packets", + "placeholder": "More Game" }, "delegate": { "title": "Delegates", @@ -715,6 +718,41 @@ "wheel":{ "desc": "Super Lucky Wheel. Player can adjust number of segment for various odds." }, + "scolorball":{ + "desc": "Select several red balls to bet, better odds." + }, + "placeholder":{ + "desc": "More games coming soon." + }, + "scolorball":{ + "desc": "Select several red balls for one bet", + "mode":{ + "label_2/4": "2/4 2 of 4", + "label_3/6": "3/6 3 of 6", + "label_4/8": "4/8 4 of 8", + "hint_2/4": "Select 2 red balls out of 4, each bet contains 2 red balls.", + "hint_3/6": "Select 3 red balls out of 6, each bet contains 3 red balls.", + "hint_4/8": "Select 4 red balls out of 8, each bet contains 4 red balls." + } + }, + "dcolorball":{ + "desc": "Select several red balls and 1 blue ball for one bet", + "game_mode": "Select Game Mode", + "section_red": "Red Zone", + "section_blue": "Blue Zone", + "rand_select": "Random", + "clear_selected": "Clear", + "clear_all": "Clear All", + "your_bet": "Your choices", + "mode":{ + "label_2/5": "2/5 2 of 5", + "label_5/10": "5/10 5 of 10", + "label_5/15": "5/15 5 of 15", + "hint_2/5": "Select 2 red balls out of 5 and 1 blue ball out of 4, each bet contains 2 red balls and 1 blue ball.", + "hint_5/10": "Select 5 red balls out of 10 and 1 blue ball out of 4, each bet contains 5 red balls and 1 blue ball.", + "hint_5/15": "Select 5 red balls out of 15 and 1 blue ball out of 4, each bet contains 5 red balls and 1 blue ball." + } + }, "packet":{ "desc": "Let's send and collect red packets together", "type":{ diff --git a/app/static/locale-zh-CN.json b/app/static/locale-zh-CN.json index 8ef980a8..c039aa46 100644 --- a/app/static/locale-zh-CN.json +++ b/app/static/locale-zh-CN.json @@ -161,7 +161,7 @@ "btn_unlock": "解 锁", "caps_lock_on": "大写键已按下!", "desc_title": "DAC PLAY", - "desc_content_1": "DAC PLAY是一个实验性的去中心化、自运营的游戏资产交易市场和游戏平台", + "desc_content_1": "DAC PLAY是一个实验性的去中心化、自运营的游戏资产交易市场和游戏平台。项目处于早期实验性阶段,以及面对的虚拟资产的特性,请充分理解其隐含的风险。", "desc_content_2": "", "spending_password": "密 码", "wrong_pass": "密码错误", @@ -223,6 +223,8 @@ "apps": "应用市场", "dices": "DICE游戏", "wheel": "超级幸运大转盘", + "scolorball": "单色球", + "dcolorball": "双色球", "chips": "游戏资产", "games": "游戏", "market": "市场", @@ -234,7 +236,8 @@ "daily_income": "网络每日收入", "daily_expense": "网络每日花费", "daily_burn": "网络每日销毁", - "packets": "红包" + "packets": "红包", + "placeholder": "更多游戏" }, "delegate": { "title": "受托人", @@ -487,7 +490,7 @@ }, "notes": { "title": "日志簿", - "desc": "你可以在区块链上保存公开或私密的日志。隐秘日志只有你自己才能打开阅读。", + "desc": "你可以在区块链上保存公开或私密的日志。", "open": "打开", "need_to_register": "你需要先在区块链上注册这个账号才能使用", "notes_not_found": "你还没有日志。你知道吗?写日志也有机会中奖哦。", @@ -696,8 +699,43 @@ "lucky_number": "幸运数字", "odds": "几率" }, + "scolorball":{ + "desc": "选择若干个红球,进行组合投注,中奖概率更高。" + }, + "placeholder":{ + "desc": "更多游戏,敬请期待。" + }, + "dcolorball":{ + "desc": "选择若干个红色球和1个蓝色球,进行组合投注。", + "game_mode": "选择玩法", + "section_red": "红区", + "section_blue": "蓝区", + "rand_select": "机选", + "clear_selected": "清除已选", + "clear_all": "重新选择", + "your_bet": "你的选择", + "mode":{ + "label_2/5": "2/5 五选二", + "label_5/10": "5/10 十选五", + "label_5/15": "5/15 十五选五", + "hint_2/5": "5个红球中选择2个,4个蓝球中选择1个,每注包含2个红球1个蓝球", + "hint_5/10": "10个红球中选择5个,4个蓝球中选择1个,每注包含5个红球1个蓝球", + "hint_5/15": "15个红球中选择5个,4个蓝球中选择1个,每注包含5个红球1个蓝球" + } + }, + "scolorball":{ + "desc": "选择若干个红色球,进行组合投注。", + "mode":{ + "label_2/4": "2/4 四选二", + "label_3/6": "3/6 六选三", + "label_4/8": "4/8 八选四", + "hint_2/4": "4个红球中选择2个,每注包含2个红球", + "hint_3/6": "6个红球中选择3个,每注包含3个红球", + "hint_4/8": "8个红球中选择4个,每注包含4个红球" + } + }, "packet":{ - "desc": "大家一起来发红包、抢红包", + "desc": "大家一起来发红包、抢红包,看谁的手气好。", "type":{ "created": "最近创建", "claimed": "最近领取" diff --git a/app/templates/apps.html b/app/templates/apps.html index 1af19c4f..b65b2339 100644 --- a/app/templates/apps.html +++ b/app/templates/apps.html @@ -1,7 +1,8 @@ -
- - Washed Out +
+ + Washed Out + Washed Out {{app.heading|translate}} @@ -11,7 +12,7 @@

{{(app.id+'.desc')|translate}}

- {{'btn.start'|translate}} + {{'btn.start'|translate}}
diff --git a/app/templates/dcolorball/dcolorball.html b/app/templates/dcolorball/dcolorball.html new file mode 100644 index 00000000..8fff5543 --- /dev/null +++ b/app/templates/dcolorball/dcolorball.html @@ -0,0 +1,131 @@ + + +
+
+
+
+
+

index.dcolorball

+
+
+
+
+

dcolorball.desc

+
+
+ + + +
+

dcolorball.game_mode

+ + + + {{('dcolorball.mode.label_' + mode.label) | translate}} + + + +
+

{{'dcolorball.mode.hint_'+currentMode | translate}}

+ +
+ + +
+
+

{{('dcolorball.section_'+section) | translate}}

+ + {{'dcolorball.rand_select' | translate}} + + {{'dcolorball.clear_selected' | translate}} +
+ +
+
+
{{i}}
+
+
+
+ +
+ +
+ + + + +
+
+ 余额 +

{{ current_balance ? (current_balance | formatAsset) : 0 + ' ' + chip_asset_name}}

+
+ + + + + {{'game.buy_chip'|translate}} + {{'game.withdraw'|translate}} + +
+ +
+ + + + + + +
{{'game.bet_amount'|translate}}
+
+
+ + +
+
+
{{'dcolorball.your_bet'|translate}}
+ +
+
+
+
{{i}}
+
+
+
+
+
{{i}}
+
+
+
+ +
{{'game.bet'|translate}} {{ bet_amount + ' ' + chip_asset_name}}
+
+ + + + + {{'game.bet'|translate}} + {{'dcolorball.clear_selected' | translate}} + +
+ + + +
+
+ + + + + +
+ +
+
+
+ diff --git a/app/templates/dcolorball/purchase.html b/app/templates/dcolorball/purchase.html new file mode 100644 index 00000000..348fa9b3 --- /dev/null +++ b/app/templates/dcolorball/purchase.html @@ -0,0 +1,62 @@ + + + + +
+

{{'game.buy_chip'|translate}}

+ + + + +
+
+ + +
+ +
+ +
+
+ + + + + + + + + +
+ +
+ + + + +
+
{{'market.tip.insufficient_balances'|translate}}
+
+
+ + + + + + +
+ +
+
+
+
+ + + + + {{'btn.cancel'|translate}} + {{'game.buy_chip'|translate}} + +
diff --git a/app/templates/dcolorball/transactions.html b/app/templates/dcolorball/transactions.html new file mode 100644 index 00000000..e365aad4 --- /dev/null +++ b/app/templates/dcolorball/transactions.html @@ -0,0 +1,134 @@ +
+
+
+

+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
th.from th.toth.fee{{game.bet_amount}}th.balance{{'game.result'|translate}}th.date
+ {{ t.ledger_entries[0].from }} + {{ t.fee | formatAsset}} + + {{ entry.amount_asset | formatAsset }} + + + + + {{ balance | formatAsset}}
+
+
+
+ + + +
+
+ + WIN: {{ win_amount / current_balance.precision }} + + + LOSE: {{ win_amount / current_balance.precision }} + +
+ +
+
+
+
{{i}}
+
+
+
+
+
{{i}}
+
+
+
+ +
+ {{'game.result'|translate}} ({{'wheel.odds'|translate}}:1/{{t.reveal.data.odds}}): +
+ +
+
+
+
{{i}}
+
+
+
+
+
{{i}}
+
+
+
+ +
+ + + +
+
+ {{'game.waiting_to_draw'|translate}} ({{'game.remain_blocks'|translate:"{remain:"+calculate_waiting_blocks(t)+"}"}}) +
+ +
+
+
+
{{i}}
+
+
+
+
+
{{i}}
+
+
+
+
+ +
+ {{t.pretty_time}}
+ + tip.pending + tip.pending (rebroadcasted) +
Expires {{t.expiration_pretty_time}} +
+ {{t.error.message}} + account.transaction_virtual + account.transaction_details +
{{warning}}
+
diff --git a/app/templates/scolorball/purchase.html b/app/templates/scolorball/purchase.html new file mode 100644 index 00000000..348fa9b3 --- /dev/null +++ b/app/templates/scolorball/purchase.html @@ -0,0 +1,62 @@ + + + + +
+

{{'game.buy_chip'|translate}}

+ + + + +
+
+ + +
+ +
+ +
+
+ + + + + + + + + +
+ +
+ + + + +
+
{{'market.tip.insufficient_balances'|translate}}
+
+
+ + + + + + +
+ +
+
+
+
+ + + + + {{'btn.cancel'|translate}} + {{'game.buy_chip'|translate}} + +
diff --git a/app/templates/scolorball/scolorball.html b/app/templates/scolorball/scolorball.html new file mode 100644 index 00000000..a800c286 --- /dev/null +++ b/app/templates/scolorball/scolorball.html @@ -0,0 +1,131 @@ + + +
+
+
+
+
+

index.scolorball

+
+
+
+
+

scolorball.desc

+
+
+ + + +
+

dcolorball.game_mode

+ + + + {{('scolorball.mode.label_' + mode.label) | translate}} + + + +
+

{{'scolorball.mode.hint_'+currentMode | translate}}

+ +
+ + +
+
+

{{('dcolorball.section_'+section) | translate}}

+ + {{'dcolorball.rand_select' | translate}} + + {{'dcolorball.clear_selected' | translate}} +
+ +
+
+
{{i}}
+
+
+
+ +
+ +
+ + + + +
+
+ 余额 +

{{ current_balance ? (current_balance | formatAsset) : 0 + ' ' + chip_asset_name}}

+
+ + + + + {{'game.buy_chip'|translate}} + {{'game.withdraw'|translate}} + +
+ +
+ + + + + + +
{{'game.bet_amount'|translate}}
+
+
+ + +
+
+
{{'dcolorball.your_bet'|translate}}
+ +
+
+
+
{{i}}
+
+
+
+
+
{{i}}
+
+
+
+ +
{{'game.bet'|translate}} {{ bet_amount + ' ' + chip_asset_name}}
+
+ + + + + {{'game.bet'|translate}} + {{'dcolorball.clear_selected' | translate}} + +
+ + + +
+
+ + + + + +
+ +
+
+
+ diff --git a/app/templates/scolorball/transactions.html b/app/templates/scolorball/transactions.html new file mode 100644 index 00000000..b1791645 --- /dev/null +++ b/app/templates/scolorball/transactions.html @@ -0,0 +1,119 @@ +
+
+
+

+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
th.from th.toth.fee{{game.bet_amount}}th.balance{{'game.result'|translate}}th.date
+ {{ t.ledger_entries[0].from }} + {{ t.fee | formatAsset}} + + {{ entry.amount_asset | formatAsset }} + + + + + {{ balance | formatAsset}}
+
+
+
+ + + +
+
+ + WIN: {{ win_amount / current_balance.precision }} + + + LOSE: {{ win_amount / current_balance.precision }} + +
+ +
+
+
+
{{i}}
+
+
+
+ +
+ {{'game.result'|translate}} ({{'wheel.odds'|translate}}:1/{{t.reveal.data.odds}}): +
+ +
+
+
+
{{i}}
+
+
+
+ +
+ + + +
+
+ {{'game.waiting_to_draw'|translate}} ({{'game.remain_blocks'|translate:"{remain:"+calculate_waiting_blocks(t)+"}"}}) +
+ +
+
+
+
{{i}}
+
+
+
+
+ +
+ {{t.pretty_time}}
+ + tip.pending + tip.pending (rebroadcasted) +
Expires {{t.expiration_pretty_time}} +
+ {{t.error.message}} + account.transaction_virtual + account.transaction_details +
{{warning}}
+
diff --git a/config/files.js b/config/files.js index 59996782..b6c11b87 100644 --- a/config/files.js +++ b/config/files.js @@ -49,6 +49,9 @@ module.exports = function(lineman) { "node_modules/angular-touch/angular-touch.js", //angular-material doesn't like this, but angular-carousel depends on it "node_modules/angular-carousel/dist/angular-carousel.js", + //js-combinatorics + "node_modules/js-combinatorics/combinatorics.js", + //games "vendor/js/games/TweenMax.min.js", "vendor/js/games/Winwheel.js" diff --git a/package.json b/package.json index 2f5c097c..ef6c2a8a 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "angular-aria": "^1.5.2", "angular-carousel": "^1.0.1", "angular-growl-v2": "~0.7.5", - "angular-material": "^1.0.7", + "angular-material": "^1.0.9", "angular-messages": "^1.5.2", "angular-pageslide-directive": "^1.0.6", "angular-resource": "^1.5.2", @@ -28,7 +28,8 @@ "moment-timezone": "^0.5.2", "ng-idle": "^1.2.0", "ng-material-floating-button": "^0.6.2", - "stacktrace-js": "^1.0.4" + "stacktrace-js": "^1.0.4", + "js-combinatorics": "0.5.2" }, "scripts": { "start": "lineman run",