2015-03-20 14:21:44 -07:00

389 lines
13 KiB
JavaScript

var BOT_CONFIG = require('../../config/bots.json');
var logger = require('pomelo-logger').getLogger('game-log', __filename);
var UserStore = require('../../app/persistence/users');
Hand = require('hoyle').Hand;
var BotService = function(app){
this.app = app;
this.channelService = app.get('channelService');
this.tableService = app.get('tableService');
this.tableInstance = {};
this.tableInstanceActive = {};
this.bots = {};
this.config = BOT_CONFIG.config;
};
module.exports = BotService;
BotService.prototype.start = function(cb){
var me = this;
if(!me.config.enabled){
return cb();
}
me.registerBots(BOT_CONFIG.bots, function(){
logger.info('all bots registered');
me.checkAvailability();
cb();
});
};
BotService.prototype.registerBots = function(bots, cb){
var me = this, i = 0;
if(!bots.length){
cb();
}
function createIfNotExist(){
bots[i].chips = 100000;
UserStore.create(bots[i], function(e, user){
me.bots[user.id] = user;
me.bots[user.id].available = true;
if(++i == bots.length){
return cb();
}
createIfNotExist();
});
}
createIfNotExist();
};
BotService.prototype.checkAvailability = function(){
var me = this;
setInterval(function(){
var bot = me.getAvailableBot();
var table = me.getAvailableTable();
if(bot && table && (!me.config.minBots || me.config.minBots > me.getActiveBots()) && !me.config.banBots){
me.joinGame(bot, table);
}
}, (1000 * getRandomInt(me.config.joinInterval.min, (me.config.joinInterval.max))));
};
BotService.prototype.getById = function(id){
return this.bots[id];
};
BotService.prototype.getAvailableBot = function(){
var bot;
for(var i in this.bots){
if(this.bots[i].available){
bot = this.bots[i];
}
}
return bot;
};
BotService.prototype.getActiveBots = function(){
var ctr = 0;
for(var i in this.bots){
if(!this.bots[i].available){
ctr += 1;
}
}
return ctr;
};
BotService.prototype.getAvailableTable = function(){
var table;
for(var i in this.tableService.tables){
if(!this.tableInstanceActive[i] && (this.tableService.tables[i].state == 'JOIN' && this.tableService.tables[i].table.playersToAdd.length < this.tableService.tables[i].table.maxPlayers) ||
(this.tableService.tables[i].state == 'IN_PROGRESS' && (this.tableService.tables[i].table.playersToAdd + this.tableService.tables[i].table.players.length) < this.tableService.tables[i].table.maxPlayers)){
table = this.tableService.tables[i];
break;
}
}
return table;
};
BotService.prototype.joinGame = function(bot, table){
var me = this;
me.tableService.addPlayer(table.id, bot.id, me.config.buyIn || 1000, function(e){
if(e){
return logger.error('bot error joining game', e);
}
bot.available = false;
bot.games = getRandomInt(me.config.gamesToPlay.min, me.config.gamesToPlay.max);
bot.tid = table.id;
logger.debug('bot '+bot.username+' joining table '+table.id+' for '+bot.games+' games');
me.tableInstance[table.id] = me.tableInstance[table.id] || 0;
me.tableInstance[table.id] += 1;
me.listen(table.id);
});
};
BotService.prototype.startGame = function(table, tid){
var me = this;
var interval = setInterval(function(){
if(table.state == 'IN_PROGRESS'){
clearInterval(interval);
}
if(table.table.playersToAdd.length >= (me.config.minPlayers ? me.config.minPlayers : 0) && !me.config.banBots){
table.tableService.startGame(tid, function(e){
if(e){
return logger.debug('cant start game yet', e);
}
clearInterval(interval);
});
}
}, (1000 * getRandomInt(7, 20)));
}
BotService.prototype.leaveGame = function(tid, uid, cb){
var me = this;
me.tableService.removeMember(tid, uid, function(){
cb();
});
};
BotService.prototype.listen = function(tid){
var me = this;
if(me.tableInstance[tid] > 1){
return false;
}
logger.debug('initializing listeners for table '+tid);
var table = me.tableService.getTable(tid);
var playerJoinedListener = function(){
logger.debug('playerJoined');
me.startGame(table, tid);
};
var newRoundListener = function(){
logger.debug('newRound');
me.moveIfTurn(table);
};
var turnStartListener = function(){
logger.debug('turnStart');
me.moveIfTurn(table);
};
var gameInitListener = function(){
logger.debug('gameInit');
setTimeout(function(){
me.removeAllBots(tid);
}, 300);
};
table.table.eventEmitter.on('playerJoined', playerJoinedListener);
table.table.eventEmitter.on('newRound', newRoundListener);
table.table.eventEmitter.on('turnStart', turnStartListener);
table.table.eventEmitter.on('gameInit', gameInitListener);
};
BotService.prototype.moveIfTurn = function(table){
var me = this, pid;
if(typeof table.table.currentPlayer === 'number' && table.table.players[table.table.currentPlayer])
pid = table.table.players[table.table.currentPlayer].id;
if(this.bots[pid]){
logger.debug('starting move: '+this.bots[pid].username);
table.table.stopTimer();
setTimeout(function(){
if(!table.table || !table.table.game) return false;
var board = table.table.game.board || [];
if(board.length < 3){
table.table.players[table.table.currentPlayer].Call();
}else{
performMove(table);
}
logger.debug('completed move.');
table.tableService.handleGameState(table.id, function(e){
if(e){
logger.error(e);
}
});
}, (getRandomInt(me.config.actionInterval.min, me.config.actionInterval.max) * 1000));
}
};
function performMove(table){
var currentPlayer = table.table.players[table.table.currentPlayer];
var myBet = table.table.game.bets[table.table.currentPlayer];
var myHand = Hand.make(getHand(table.table.game.board.concat(table.table.players[table.table.currentPlayer].cards)));
var maxBet = myBet;
var maxRank = myHand.rank;
var winningPlayer = currentPlayer.playerName;
var hands = [];
for(var i=0;i<table.table.players.length;++i){
var bet = table.table.game.bets[i];
var hand = Hand.make(getHand(table.table.game.board.concat(table.table.players[i].cards)));
if(bet > maxBet) maxBet = bet;
if(hand.rank > maxRank){
maxRank = hand.rank;
winningPlayer = table.table.players[i].playerName;
}
hands.push(hand);
}
logger.debug('has best hand: '+ winningPlayer);
var isWinner = false;
var winners = Hand.pickWinners(hands);
var diff = maxBet - myBet;
for(var i=0;i<winners.length;++i){
if(winners[i] === myHand) isWinner = true;
}
if(myHand.rank < maxRank){
if(myBet >= maxBet){
if(getRandomInt(1, 51) > 47){
currentPlayer.AllIn();
}else if(getRandomInt(1, 10) > 7){
currentPlayer.Bet(getRandomInt(2, 53));
}else{
currentPlayer.Call();
}
}else if(myBet < maxBet){
if(diff > getRandomInt(4, 61)){
if(getRandomInt(1, 73) > 71){
currentPlayer.AllIn();
}else if(getRandomInt(1, 10) > 8){
currentPlayer.Bet(getRandomInt(2, 36));
}else if(getRandomInt(1, 10) > 2){
currentPlayer.Fold();
}else{
currentPlayer.Call();
}
}else{
if(getRandomInt(1, 73) > 70){
currentPlayer.AllIn();
}else if(getRandomInt(1, 10) > 7){
currentPlayer.Bet(getRandomInt(2, 43));
}else if(diff > getRandomInt(1, 7) && getRandomInt(1, 10) > 6){
currentPlayer.Fold();
}else{
currentPlayer.Call();
}
}
}else{
currentPlayer.Call();
}
}else{
if(myBet >= maxBet){
if(getRandomInt(1, 10) == 10){
currentPlayer.AllIn();
}else if(getRandomInt(1, 10) > 4){
currentPlayer.Bet(getRandomInt(1, 271));
}else{
currentPlayer.Call();
}
}else if(myBet < maxBet){
if(getRandomInt(1, 10) == 10){
currentPlayer.AllIn();
}else if(getRandomInt(1, 10) > 4){
currentPlayer.Bet(getRandomInt(2, 189));
}else if(diff > getRandomInt(71, 104) && getRandomInt(1, 10) > 8){
currentPlayer.Fold();
}else{
currentPlayer.Call();
}
}else{
currentPlayer.Call();
}
}
}
function getHand(hand){
for(var j=0;j<hand.length;++j){
hand[j] = hand[j].split('');
hand[j] = hand[j][0]+hand[j][1].toLowerCase();
}
return hand;
}
function getRandomInt(min, max){
return Math.floor(Math.random() * (max - min + 1)) + min;
}
BotService.prototype.removeAllBots = function(tid, banBots){
var me = this, botAry = [];
me.config.banBots = banBots;
var table = me.tableService.getTable(tid);
for(var id in me.bots){
if(me.bots[id].tid === tid)
botAry.push(id);
}
if(me.config.validateChips){
me.tableInstanceActive[tid] = true;
}
me.removeBot(botAry, 0, tid, banBots, function(){
if(me.tableInstance[tid] === 0){
logger.debug('removing listeners for table '+tid);
table.table.eventEmitter.removeAllListeners('playerJoined');
table.table.eventEmitter.removeAllListeners('newRound');
table.table.eventEmitter.removeAllListeners('turnStart');
table.table.eventEmitter.removeAllListeners('gameInit');
}
if(me.config.validateChips){
me.validateTotalChips(function(e){
if(!e){
me.tableInstanceActive[tid] = false;
me.startGame(table, tid);
}
});
}else{
me.startGame(table, tid);
}
});
};
BotService.prototype.removeBot = function(botAry, i, tid, banBots, cb){
var me = this;
if(i === botAry.length){
return cb();
}
var bid = botAry[i];
if(me.bots[bid].tid === tid){
me.bots[bid].games -= 1;
var user = me.tableService.getPlayerJSON(tid, bid, 'players') || me.tableService.getPlayerJSON(tid, bid, 'playersToAdd') || me.tableService.getPlayerJSON(tid, bid, 'previousPlayers');
console.log(me.tableService.getTable(tid));
console.log(tid, bid, user);
if(me.bots[bid].games === 0 || (user && user.chips === 0) || banBots === true){
logger.debug('bot '+me.bots[bid].username+' ('+me.bots[bid].id+') left game '+tid+' with '+((user && user.chips) ? user.chips : 0)+' chips', user);
me.leaveGame(tid, bid, function(){
me.bots[bid].available = true;
delete me.bots[bid].tid;
me.tableInstance[tid] -= 1;
me.removeBot(botAry, (i+1), tid, banBots, cb);
});
}else{
me.removeBot(botAry, (i+1), tid, banBots, cb);
}
}
};
// validate chips per all bots
BotService.prototype.validateTotalChips = function(cb){
var total = 0;
var ids = [];
for(var i in this.bots){
ids.push(this.bots[i].id);
}
UserStore.getByIds(ids, function(e, users){
for(var user in users){
total += users[user].chips;
}
var correctTotal = (ids.length * 100000);
if(total != correctTotal){
logger.error('INCORRECT TOTAL CHIPS! '+correctTotal+' - '+total+' = '+(correctTotal - total));
return cb('incorrect-total');
}
logger.debug('correct total chips '+total+'/'+correctTotal);
cb();
});
}
// validate chips per table
BotService.prototype.validateChips = function(table, cb){
var me = this;
var total = 0;
var ids = [];
for(var i=0;i<table.table.playersToAdd.length;++i){
ids.push(table.table.playersToAdd[i].id);
}
UserStore.getByIds(ids, function(e, users){
for(var user in users){
total += users[user].chips;
}
total += (ids.length * me.config.buyIn);
var correctTotal = (ids.length * 100000);
if(total != correctTotal){
logger.error('INCORRECT GAME CHIPS! '+correctTotal+' - '+total+' = '+(correctTotal - total));
return cb('incorrect-total');
}
cb();
});
}