initial commit of poker game
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
npm-debug.log
|
||||||
|
.idea
|
||||||
|
node_modules
|
||||||
|
game-server/logs
|
36
game-server/app.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
var pomelo = require('pomelo');
|
||||||
|
var routeUtil = require('./app/util/routeUtil');
|
||||||
|
var abuseFilter = require('./app/servers/game/filter/abuseFilter');
|
||||||
|
var tableComponent = require('./app/components/tableComponent');
|
||||||
|
var botComponent = require('./app/components/botComponent');
|
||||||
|
var stateComponent = require('./app/components/stateComponent');
|
||||||
|
var ChatService = require('./app/services/chatService');
|
||||||
|
|
||||||
|
var app = pomelo.createApp();
|
||||||
|
app.set('name', 'poker-game-stack');
|
||||||
|
|
||||||
|
app.configure('production|development', function(){
|
||||||
|
app.route('game', routeUtil.game);
|
||||||
|
app.filter(pomelo.timeout());
|
||||||
|
app.set('session', require('../shared/config/session.json'));
|
||||||
|
});
|
||||||
|
|
||||||
|
app.configure('production|development', 'game', function(){
|
||||||
|
app.filter(abuseFilter());
|
||||||
|
app.load(tableComponent);
|
||||||
|
app.load(botComponent);
|
||||||
|
// app.load(stateComponent);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.configure('production|development', 'chat', function(){
|
||||||
|
app.set('chatService', new ChatService(app));
|
||||||
|
});
|
||||||
|
|
||||||
|
//var timeReport = require('./app/module/timeReport');
|
||||||
|
//app.registerAdmin(timeReport, {app: app});
|
||||||
|
|
||||||
|
app.start();
|
||||||
|
|
||||||
|
process.on('uncaughtException', function(err){
|
||||||
|
console.error('Caught exception: ' + err.stack);
|
||||||
|
});
|
8
game-server/app/components/botComponent.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
var BotService = require('../services/botService');
|
||||||
|
|
||||||
|
module.exports = function(app, opts){
|
||||||
|
var service = new BotService(app, opts);
|
||||||
|
app.set('botService', service, true);
|
||||||
|
service.name = '__bot__';
|
||||||
|
return service;
|
||||||
|
};
|
8
game-server/app/components/stateComponent.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
var BotService = require('../services/stateService');
|
||||||
|
|
||||||
|
module.exports = function(app, opts){
|
||||||
|
var service = new StateService(app, opts);
|
||||||
|
app.set('stateService', service, true);
|
||||||
|
service.name = '__state__';
|
||||||
|
return service;
|
||||||
|
};
|
8
game-server/app/components/tableComponent.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
var TableService = require('../services/tableService');
|
||||||
|
|
||||||
|
module.exports = function(app, opts){
|
||||||
|
var service = new TableService(app, opts);
|
||||||
|
app.set('tableService', service, true);
|
||||||
|
service.name = '__table__';
|
||||||
|
return service;
|
||||||
|
};
|
83
game-server/app/game/game.js
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
var logger = require('pomelo-logger').getLogger('game-log', __filename);
|
||||||
|
var uuid = require('node-uuid');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = Game = function(smallBlind, bigBlind){
|
||||||
|
this.id = uuid.v1();
|
||||||
|
this.smallBlind = smallBlind;
|
||||||
|
this.bigBlind = bigBlind;
|
||||||
|
this.pot = 0;
|
||||||
|
this.roundName = 'Deal'; //Start the first round
|
||||||
|
this.betName = 'bet'; //bet,raise,re-raise,cap
|
||||||
|
this.bets = [];
|
||||||
|
this.roundBets = [];
|
||||||
|
this.deck = [];
|
||||||
|
this.board = [];
|
||||||
|
fillDeck(this.deck);
|
||||||
|
}
|
||||||
|
|
||||||
|
function fillDeck(deck){
|
||||||
|
deck.push('AS');
|
||||||
|
deck.push('KS');
|
||||||
|
deck.push('QS');
|
||||||
|
deck.push('JS');
|
||||||
|
deck.push('TS');
|
||||||
|
deck.push('9S');
|
||||||
|
deck.push('8S');
|
||||||
|
deck.push('7S');
|
||||||
|
deck.push('6S');
|
||||||
|
deck.push('5S');
|
||||||
|
deck.push('4S');
|
||||||
|
deck.push('3S');
|
||||||
|
deck.push('2S');
|
||||||
|
deck.push('AH');
|
||||||
|
deck.push('KH');
|
||||||
|
deck.push('QH');
|
||||||
|
deck.push('JH');
|
||||||
|
deck.push('TH');
|
||||||
|
deck.push('9H');
|
||||||
|
deck.push('8H');
|
||||||
|
deck.push('7H');
|
||||||
|
deck.push('6H');
|
||||||
|
deck.push('5H');
|
||||||
|
deck.push('4H');
|
||||||
|
deck.push('3H');
|
||||||
|
deck.push('2H');
|
||||||
|
deck.push('AD');
|
||||||
|
deck.push('KD');
|
||||||
|
deck.push('QD');
|
||||||
|
deck.push('JD');
|
||||||
|
deck.push('TD');
|
||||||
|
deck.push('9D');
|
||||||
|
deck.push('8D');
|
||||||
|
deck.push('7D');
|
||||||
|
deck.push('6D');
|
||||||
|
deck.push('5D');
|
||||||
|
deck.push('4D');
|
||||||
|
deck.push('3D');
|
||||||
|
deck.push('2D');
|
||||||
|
deck.push('AC');
|
||||||
|
deck.push('KC');
|
||||||
|
deck.push('QC');
|
||||||
|
deck.push('JC');
|
||||||
|
deck.push('TC');
|
||||||
|
deck.push('9C');
|
||||||
|
deck.push('8C');
|
||||||
|
deck.push('7C');
|
||||||
|
deck.push('6C');
|
||||||
|
deck.push('5C');
|
||||||
|
deck.push('4C');
|
||||||
|
deck.push('3C');
|
||||||
|
deck.push('2C');
|
||||||
|
|
||||||
|
//Shuffle the deck array with Fisher-Yates
|
||||||
|
var i, j, tempi, tempj;
|
||||||
|
for(i=0;i< deck.length;i+=1){
|
||||||
|
j = Math.floor(Math.random() * (i + 1));
|
||||||
|
tempi = deck[i];
|
||||||
|
tempj = deck[j];
|
||||||
|
deck[i] = tempj;
|
||||||
|
deck[j] = tempi;
|
||||||
|
}
|
||||||
|
}
|
446
game-server/app/game/hand.js
Normal file
@ -0,0 +1,446 @@
|
|||||||
|
module.exports = Hand = function(cards){
|
||||||
|
this.cards = cards;
|
||||||
|
var result = rankHandInt({
|
||||||
|
cards : cards
|
||||||
|
});
|
||||||
|
this.rank = result.rank;
|
||||||
|
this.message = result.message;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
function rankHandInt(hand){
|
||||||
|
var rank, message, handRanks, handSuits, ranks, suits, cards, result, i;
|
||||||
|
rank = 0.0000;
|
||||||
|
message = '';
|
||||||
|
handRanks = [];
|
||||||
|
handSuits = [];
|
||||||
|
|
||||||
|
for(i=0;i< hand.cards.length;i+=1){
|
||||||
|
handRanks[i] = hand.cards[i].substr(0, 1);
|
||||||
|
handSuits[i] = hand.cards[i].substr(1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ranks = handRanks.sort().toString().replace(/\W/g, "");
|
||||||
|
suits = handSuits.sort().toString().replace(/\W/g, "");
|
||||||
|
cards = hand.cards.toString();
|
||||||
|
|
||||||
|
//Four of a kind
|
||||||
|
if(rank === 0){
|
||||||
|
if(ranks.indexOf('AAAA') > -1){rank = 292 + rankKickers(ranks.replace('AAAA', ''), 1); }
|
||||||
|
if(ranks.indexOf('KKKK') > -1 && rank === 0){rank = 291 + rankKickers(ranks.replace('KKKK', ''), 1); }
|
||||||
|
if(ranks.indexOf('QQQQ') > -1 && rank === 0){rank = 290 + rankKickers(ranks.replace('QQQQ', ''), 1); }
|
||||||
|
if(ranks.indexOf('JJJJ') > -1 && rank === 0){rank = 289 + rankKickers(ranks.replace('JJJJ', ''), 1); }
|
||||||
|
if(ranks.indexOf('TTTT') > -1 && rank === 0){rank = 288 + rankKickers(ranks.replace('TTTT', ''), 1); }
|
||||||
|
if(ranks.indexOf('9999') > -1 && rank === 0){rank = 287 + rankKickers(ranks.replace('9999', ''), 1); }
|
||||||
|
if(ranks.indexOf('8888') > -1 && rank === 0){rank = 286 + rankKickers(ranks.replace('8888', ''), 1); }
|
||||||
|
if(ranks.indexOf('7777') > -1 && rank === 0){rank = 285 + rankKickers(ranks.replace('7777', ''), 1); }
|
||||||
|
if(ranks.indexOf('6666') > -1 && rank === 0){rank = 284 + rankKickers(ranks.replace('6666', ''), 1); }
|
||||||
|
if(ranks.indexOf('5555') > -1 && rank === 0){rank = 283 + rankKickers(ranks.replace('5555', ''), 1); }
|
||||||
|
if(ranks.indexOf('4444') > -1 && rank === 0){rank = 282 + rankKickers(ranks.replace('4444', ''), 1); }
|
||||||
|
if(ranks.indexOf('3333') > -1 && rank === 0){rank = 281 + rankKickers(ranks.replace('3333', ''), 1); }
|
||||||
|
if(ranks.indexOf('2222') > -1 && rank === 0){rank = 280 + rankKickers(ranks.replace('2222', ''), 1); }
|
||||||
|
if(rank !== 0){message = 'Four of a kind'; }
|
||||||
|
}
|
||||||
|
|
||||||
|
//Full House
|
||||||
|
if(rank === 0){
|
||||||
|
if(ranks.indexOf('AAA') > -1 && ranks.indexOf('KK') > -1){rank = 279; }
|
||||||
|
if(ranks.indexOf('AAA') > -1 && ranks.indexOf('QQ') > -1 && rank === 0){rank = 278; }
|
||||||
|
if(ranks.indexOf('AAA') > -1 && ranks.indexOf('JJ') > -1 && rank === 0){rank = 277; }
|
||||||
|
if(ranks.indexOf('AAA') > -1 && ranks.indexOf('TT') > -1 && rank === 0){rank = 276; }
|
||||||
|
if(ranks.indexOf('AAA') > -1 && ranks.indexOf('99') > -1 && rank === 0){rank = 275; }
|
||||||
|
if(ranks.indexOf('AAA') > -1 && ranks.indexOf('88') > -1 && rank === 0){rank = 274; }
|
||||||
|
if(ranks.indexOf('AAA') > -1 && ranks.indexOf('77') > -1 && rank === 0){rank = 273; }
|
||||||
|
if(ranks.indexOf('AAA') > -1 && ranks.indexOf('66') > -1 && rank === 0){rank = 272; }
|
||||||
|
if(ranks.indexOf('AAA') > -1 && ranks.indexOf('55') > -1 && rank === 0){rank = 271; }
|
||||||
|
if(ranks.indexOf('AAA') > -1 && ranks.indexOf('44') > -1 && rank === 0){rank = 270; }
|
||||||
|
if(ranks.indexOf('AAA') > -1 && ranks.indexOf('33') > -1 && rank === 0){rank = 269; }
|
||||||
|
if(ranks.indexOf('AAA') > -1 && ranks.indexOf('22') > -1 && rank === 0){rank = 268; }
|
||||||
|
if(ranks.indexOf('KKK') > -1 && ranks.indexOf('AA') > -1 && rank === 0){rank = 267; }
|
||||||
|
if(ranks.indexOf('KKK') > -1 && ranks.indexOf('QQ') > -1 && rank === 0){rank = 266; }
|
||||||
|
if(ranks.indexOf('KKK') > -1 && ranks.indexOf('JJ') > -1 && rank === 0){rank = 265; }
|
||||||
|
if(ranks.indexOf('KKK') > -1 && ranks.indexOf('TT') > -1 && rank === 0){rank = 264; }
|
||||||
|
if(ranks.indexOf('KKK') > -1 && ranks.indexOf('99') > -1 && rank === 0){rank = 263; }
|
||||||
|
if(ranks.indexOf('KKK') > -1 && ranks.indexOf('88') > -1 && rank === 0){rank = 262; }
|
||||||
|
if(ranks.indexOf('KKK') > -1 && ranks.indexOf('77') > -1 && rank === 0){rank = 261; }
|
||||||
|
if(ranks.indexOf('KKK') > -1 && ranks.indexOf('66') > -1 && rank === 0){rank = 260; }
|
||||||
|
if(ranks.indexOf('KKK') > -1 && ranks.indexOf('55') > -1 && rank === 0){rank = 259; }
|
||||||
|
if(ranks.indexOf('KKK') > -1 && ranks.indexOf('44') > -1 && rank === 0){rank = 258; }
|
||||||
|
if(ranks.indexOf('KKK') > -1 && ranks.indexOf('33') > -1 && rank === 0){rank = 257; }
|
||||||
|
if(ranks.indexOf('KKK') > -1 && ranks.indexOf('22') > -1 && rank === 0){rank = 256; }
|
||||||
|
if(ranks.indexOf('QQQ') > -1 && ranks.indexOf('AA') > -1 && rank === 0){rank = 255; }
|
||||||
|
if(ranks.indexOf('QQQ') > -1 && ranks.indexOf('KK') > -1 && rank === 0){rank = 254; }
|
||||||
|
if(ranks.indexOf('QQQ') > -1 && ranks.indexOf('JJ') > -1 && rank === 0){rank = 253; }
|
||||||
|
if(ranks.indexOf('QQQ') > -1 && ranks.indexOf('TT') > -1 && rank === 0){rank = 252; }
|
||||||
|
if(ranks.indexOf('QQQ') > -1 && ranks.indexOf('99') > -1 && rank === 0){rank = 251; }
|
||||||
|
if(ranks.indexOf('QQQ') > -1 && ranks.indexOf('88') > -1 && rank === 0){rank = 250; }
|
||||||
|
if(ranks.indexOf('QQQ') > -1 && ranks.indexOf('77') > -1 && rank === 0){rank = 249; }
|
||||||
|
if(ranks.indexOf('QQQ') > -1 && ranks.indexOf('66') > -1 && rank === 0){rank = 248; }
|
||||||
|
if(ranks.indexOf('QQQ') > -1 && ranks.indexOf('55') > -1 && rank === 0){rank = 247; }
|
||||||
|
if(ranks.indexOf('QQQ') > -1 && ranks.indexOf('44') > -1 && rank === 0){rank = 246; }
|
||||||
|
if(ranks.indexOf('QQQ') > -1 && ranks.indexOf('33') > -1 && rank === 0){rank = 245; }
|
||||||
|
if(ranks.indexOf('QQQ') > -1 && ranks.indexOf('22') > -1 && rank === 0){rank = 244; }
|
||||||
|
if(ranks.indexOf('JJJ') > -1 && ranks.indexOf('AA') > -1 && rank === 0){rank = 243; }
|
||||||
|
if(ranks.indexOf('JJJ') > -1 && ranks.indexOf('KK') > -1 && rank === 0){rank = 242; }
|
||||||
|
if(ranks.indexOf('JJJ') > -1 && ranks.indexOf('QQ') > -1 && rank === 0){rank = 241; }
|
||||||
|
if(ranks.indexOf('JJJ') > -1 && ranks.indexOf('TT') > -1 && rank === 0){rank = 240; }
|
||||||
|
if(ranks.indexOf('JJJ') > -1 && ranks.indexOf('99') > -1 && rank === 0){rank = 239; }
|
||||||
|
if(ranks.indexOf('JJJ') > -1 && ranks.indexOf('88') > -1 && rank === 0){rank = 238; }
|
||||||
|
if(ranks.indexOf('JJJ') > -1 && ranks.indexOf('77') > -1 && rank === 0){rank = 237; }
|
||||||
|
if(ranks.indexOf('JJJ') > -1 && ranks.indexOf('66') > -1 && rank === 0){rank = 236; }
|
||||||
|
if(ranks.indexOf('JJJ') > -1 && ranks.indexOf('55') > -1 && rank === 0){rank = 235; }
|
||||||
|
if(ranks.indexOf('JJJ') > -1 && ranks.indexOf('44') > -1 && rank === 0){rank = 234; }
|
||||||
|
if(ranks.indexOf('JJJ') > -1 && ranks.indexOf('33') > -1 && rank === 0){rank = 233; }
|
||||||
|
if(ranks.indexOf('JJJ') > -1 && ranks.indexOf('22') > -1 && rank === 0){rank = 232; }
|
||||||
|
if(ranks.indexOf('TTT') > -1 && ranks.indexOf('AA') > -1 && rank === 0){rank = 231; }
|
||||||
|
if(ranks.indexOf('TTT') > -1 && ranks.indexOf('KK') > -1 && rank === 0){rank = 230; }
|
||||||
|
if(ranks.indexOf('TTT') > -1 && ranks.indexOf('QQ') > -1 && rank === 0){rank = 229; }
|
||||||
|
if(ranks.indexOf('TTT') > -1 && ranks.indexOf('JJ') > -1 && rank === 0){rank = 228; }
|
||||||
|
if(ranks.indexOf('TTT') > -1 && ranks.indexOf('99') > -1 && rank === 0){rank = 227; }
|
||||||
|
if(ranks.indexOf('TTT') > -1 && ranks.indexOf('88') > -1 && rank === 0){rank = 226; }
|
||||||
|
if(ranks.indexOf('TTT') > -1 && ranks.indexOf('77') > -1 && rank === 0){rank = 225; }
|
||||||
|
if(ranks.indexOf('TTT') > -1 && ranks.indexOf('66') > -1 && rank === 0){rank = 224; }
|
||||||
|
if(ranks.indexOf('TTT') > -1 && ranks.indexOf('55') > -1 && rank === 0){rank = 223; }
|
||||||
|
if(ranks.indexOf('TTT') > -1 && ranks.indexOf('44') > -1 && rank === 0){rank = 222; }
|
||||||
|
if(ranks.indexOf('TTT') > -1 && ranks.indexOf('33') > -1 && rank === 0){rank = 221; }
|
||||||
|
if(ranks.indexOf('TTT') > -1 && ranks.indexOf('22') > -1 && rank === 0){rank = 220; }
|
||||||
|
if(ranks.indexOf('999') > -1 && ranks.indexOf('AA') > -1 && rank === 0){rank = 219; }
|
||||||
|
if(ranks.indexOf('999') > -1 && ranks.indexOf('KK') > -1 && rank === 0){rank = 218; }
|
||||||
|
if(ranks.indexOf('999') > -1 && ranks.indexOf('QQ') > -1 && rank === 0){rank = 217; }
|
||||||
|
if(ranks.indexOf('999') > -1 && ranks.indexOf('JJ') > -1 && rank === 0){rank = 216; }
|
||||||
|
if(ranks.indexOf('999') > -1 && ranks.indexOf('TT') > -1 && rank === 0){rank = 215; }
|
||||||
|
if(ranks.indexOf('999') > -1 && ranks.indexOf('88') > -1 && rank === 0){rank = 214; }
|
||||||
|
if(ranks.indexOf('999') > -1 && ranks.indexOf('77') > -1 && rank === 0){rank = 213; }
|
||||||
|
if(ranks.indexOf('999') > -1 && ranks.indexOf('66') > -1 && rank === 0){rank = 212; }
|
||||||
|
if(ranks.indexOf('999') > -1 && ranks.indexOf('55') > -1 && rank === 0){rank = 211; }
|
||||||
|
if(ranks.indexOf('999') > -1 && ranks.indexOf('44') > -1 && rank === 0){rank = 210; }
|
||||||
|
if(ranks.indexOf('999') > -1 && ranks.indexOf('33') > -1 && rank === 0){rank = 209; }
|
||||||
|
if(ranks.indexOf('999') > -1 && ranks.indexOf('22') > -1 && rank === 0){rank = 208; }
|
||||||
|
if(ranks.indexOf('888') > -1 && ranks.indexOf('AA') > -1 && rank === 0){rank = 207; }
|
||||||
|
if(ranks.indexOf('888') > -1 && ranks.indexOf('KK') > -1 && rank === 0){rank = 206; }
|
||||||
|
if(ranks.indexOf('888') > -1 && ranks.indexOf('QQ') > -1 && rank === 0){rank = 205; }
|
||||||
|
if(ranks.indexOf('888') > -1 && ranks.indexOf('JJ') > -1 && rank === 0){rank = 204; }
|
||||||
|
if(ranks.indexOf('888') > -1 && ranks.indexOf('TT') > -1 && rank === 0){rank = 203; }
|
||||||
|
if(ranks.indexOf('888') > -1 && ranks.indexOf('99') > -1 && rank === 0){rank = 202; }
|
||||||
|
if(ranks.indexOf('888') > -1 && ranks.indexOf('77') > -1 && rank === 0){rank = 201; }
|
||||||
|
if(ranks.indexOf('888') > -1 && ranks.indexOf('66') > -1 && rank === 0){rank = 200; }
|
||||||
|
if(ranks.indexOf('888') > -1 && ranks.indexOf('55') > -1 && rank === 0){rank = 199; }
|
||||||
|
if(ranks.indexOf('888') > -1 && ranks.indexOf('44') > -1 && rank === 0){rank = 198; }
|
||||||
|
if(ranks.indexOf('888') > -1 && ranks.indexOf('33') > -1 && rank === 0){rank = 197; }
|
||||||
|
if(ranks.indexOf('888') > -1 && ranks.indexOf('22') > -1 && rank === 0){rank = 196; }
|
||||||
|
if(ranks.indexOf('777') > -1 && ranks.indexOf('AA') > -1 && rank === 0){rank = 195; }
|
||||||
|
if(ranks.indexOf('777') > -1 && ranks.indexOf('KK') > -1 && rank === 0){rank = 194; }
|
||||||
|
if(ranks.indexOf('777') > -1 && ranks.indexOf('QQ') > -1 && rank === 0){rank = 193; }
|
||||||
|
if(ranks.indexOf('777') > -1 && ranks.indexOf('JJ') > -1 && rank === 0){rank = 192; }
|
||||||
|
if(ranks.indexOf('777') > -1 && ranks.indexOf('TT') > -1 && rank === 0){rank = 191; }
|
||||||
|
if(ranks.indexOf('777') > -1 && ranks.indexOf('99') > -1 && rank === 0){rank = 190; }
|
||||||
|
if(ranks.indexOf('777') > -1 && ranks.indexOf('88') > -1 && rank === 0){rank = 189; }
|
||||||
|
if(ranks.indexOf('777') > -1 && ranks.indexOf('66') > -1 && rank === 0){rank = 188; }
|
||||||
|
if(ranks.indexOf('777') > -1 && ranks.indexOf('55') > -1 && rank === 0){rank = 187; }
|
||||||
|
if(ranks.indexOf('777') > -1 && ranks.indexOf('44') > -1 && rank === 0){rank = 186; }
|
||||||
|
if(ranks.indexOf('777') > -1 && ranks.indexOf('33') > -1 && rank === 0){rank = 185; }
|
||||||
|
if(ranks.indexOf('777') > -1 && ranks.indexOf('22') > -1 && rank === 0){rank = 184; }
|
||||||
|
if(ranks.indexOf('666') > -1 && ranks.indexOf('AA') > -1 && rank === 0){rank = 183; }
|
||||||
|
if(ranks.indexOf('666') > -1 && ranks.indexOf('KK') > -1 && rank === 0){rank = 182; }
|
||||||
|
if(ranks.indexOf('666') > -1 && ranks.indexOf('QQ') > -1 && rank === 0){rank = 181; }
|
||||||
|
if(ranks.indexOf('666') > -1 && ranks.indexOf('JJ') > -1 && rank === 0){rank = 180; }
|
||||||
|
if(ranks.indexOf('666') > -1 && ranks.indexOf('TT') > -1 && rank === 0){rank = 179; }
|
||||||
|
if(ranks.indexOf('666') > -1 && ranks.indexOf('99') > -1 && rank === 0){rank = 178; }
|
||||||
|
if(ranks.indexOf('666') > -1 && ranks.indexOf('88') > -1 && rank === 0){rank = 177; }
|
||||||
|
if(ranks.indexOf('666') > -1 && ranks.indexOf('77') > -1 && rank === 0){rank = 176; }
|
||||||
|
if(ranks.indexOf('666') > -1 && ranks.indexOf('55') > -1 && rank === 0){rank = 175; }
|
||||||
|
if(ranks.indexOf('666') > -1 && ranks.indexOf('44') > -1 && rank === 0){rank = 174; }
|
||||||
|
if(ranks.indexOf('666') > -1 && ranks.indexOf('33') > -1 && rank === 0){rank = 173; }
|
||||||
|
if(ranks.indexOf('666') > -1 && ranks.indexOf('22') > -1 && rank === 0){rank = 172; }
|
||||||
|
if(ranks.indexOf('555') > -1 && ranks.indexOf('AA') > -1 && rank === 0){rank = 171; }
|
||||||
|
if(ranks.indexOf('555') > -1 && ranks.indexOf('KK') > -1 && rank === 0){rank = 170; }
|
||||||
|
if(ranks.indexOf('555') > -1 && ranks.indexOf('QQ') > -1 && rank === 0){rank = 169; }
|
||||||
|
if(ranks.indexOf('555') > -1 && ranks.indexOf('JJ') > -1 && rank === 0){rank = 168; }
|
||||||
|
if(ranks.indexOf('555') > -1 && ranks.indexOf('TT') > -1 && rank === 0){rank = 167; }
|
||||||
|
if(ranks.indexOf('555') > -1 && ranks.indexOf('99') > -1 && rank === 0){rank = 166; }
|
||||||
|
if(ranks.indexOf('555') > -1 && ranks.indexOf('88') > -1 && rank === 0){rank = 165; }
|
||||||
|
if(ranks.indexOf('555') > -1 && ranks.indexOf('77') > -1 && rank === 0){rank = 164; }
|
||||||
|
if(ranks.indexOf('555') > -1 && ranks.indexOf('66') > -1 && rank === 0){rank = 163; }
|
||||||
|
if(ranks.indexOf('555') > -1 && ranks.indexOf('44') > -1 && rank === 0){rank = 162; }
|
||||||
|
if(ranks.indexOf('555') > -1 && ranks.indexOf('33') > -1 && rank === 0){rank = 161; }
|
||||||
|
if(ranks.indexOf('555') > -1 && ranks.indexOf('22') > -1 && rank === 0){rank = 160; }
|
||||||
|
if(ranks.indexOf('444') > -1 && ranks.indexOf('AA') > -1 && rank === 0){rank = 159; }
|
||||||
|
if(ranks.indexOf('444') > -1 && ranks.indexOf('KK') > -1 && rank === 0){rank = 158; }
|
||||||
|
if(ranks.indexOf('444') > -1 && ranks.indexOf('QQ') > -1 && rank === 0){rank = 157; }
|
||||||
|
if(ranks.indexOf('444') > -1 && ranks.indexOf('JJ') > -1 && rank === 0){rank = 156; }
|
||||||
|
if(ranks.indexOf('444') > -1 && ranks.indexOf('TT') > -1 && rank === 0){rank = 155; }
|
||||||
|
if(ranks.indexOf('444') > -1 && ranks.indexOf('99') > -1 && rank === 0){rank = 154; }
|
||||||
|
if(ranks.indexOf('444') > -1 && ranks.indexOf('88') > -1 && rank === 0){rank = 153; }
|
||||||
|
if(ranks.indexOf('444') > -1 && ranks.indexOf('77') > -1 && rank === 0){rank = 152; }
|
||||||
|
if(ranks.indexOf('444') > -1 && ranks.indexOf('66') > -1 && rank === 0){rank = 151; }
|
||||||
|
if(ranks.indexOf('444') > -1 && ranks.indexOf('55') > -1 && rank === 0){rank = 150; }
|
||||||
|
if(ranks.indexOf('444') > -1 && ranks.indexOf('33') > -1 && rank === 0){rank = 149; }
|
||||||
|
if(ranks.indexOf('444') > -1 && ranks.indexOf('22') > -1 && rank === 0){rank = 148; }
|
||||||
|
if(ranks.indexOf('333') > -1 && ranks.indexOf('AA') > -1 && rank === 0){rank = 147; }
|
||||||
|
if(ranks.indexOf('333') > -1 && ranks.indexOf('KK') > -1 && rank === 0){rank = 146; }
|
||||||
|
if(ranks.indexOf('333') > -1 && ranks.indexOf('QQ') > -1 && rank === 0){rank = 145; }
|
||||||
|
if(ranks.indexOf('333') > -1 && ranks.indexOf('JJ') > -1 && rank === 0){rank = 144; }
|
||||||
|
if(ranks.indexOf('333') > -1 && ranks.indexOf('TT') > -1 && rank === 0){rank = 143; }
|
||||||
|
if(ranks.indexOf('333') > -1 && ranks.indexOf('99') > -1 && rank === 0){rank = 142; }
|
||||||
|
if(ranks.indexOf('333') > -1 && ranks.indexOf('88') > -1 && rank === 0){rank = 141; }
|
||||||
|
if(ranks.indexOf('333') > -1 && ranks.indexOf('77') > -1 && rank === 0){rank = 140; }
|
||||||
|
if(ranks.indexOf('333') > -1 && ranks.indexOf('66') > -1 && rank === 0){rank = 139; }
|
||||||
|
if(ranks.indexOf('333') > -1 && ranks.indexOf('55') > -1 && rank === 0){rank = 138; }
|
||||||
|
if(ranks.indexOf('333') > -1 && ranks.indexOf('44') > -1 && rank === 0){rank = 137; }
|
||||||
|
if(ranks.indexOf('333') > -1 && ranks.indexOf('22') > -1 && rank === 0){rank = 136; }
|
||||||
|
if(ranks.indexOf('222') > -1 && ranks.indexOf('AA') > -1 && rank === 0){rank = 135; }
|
||||||
|
if(ranks.indexOf('222') > -1 && ranks.indexOf('KK') > -1 && rank === 0){rank = 134; }
|
||||||
|
if(ranks.indexOf('222') > -1 && ranks.indexOf('QQ') > -1 && rank === 0){rank = 133; }
|
||||||
|
if(ranks.indexOf('222') > -1 && ranks.indexOf('JJ') > -1 && rank === 0){rank = 132; }
|
||||||
|
if(ranks.indexOf('222') > -1 && ranks.indexOf('TT') > -1 && rank === 0){rank = 131; }
|
||||||
|
if(ranks.indexOf('222') > -1 && ranks.indexOf('99') > -1 && rank === 0){rank = 130; }
|
||||||
|
if(ranks.indexOf('222') > -1 && ranks.indexOf('88') > -1 && rank === 0){rank = 129; }
|
||||||
|
if(ranks.indexOf('222') > -1 && ranks.indexOf('77') > -1 && rank === 0){rank = 128; }
|
||||||
|
if(ranks.indexOf('222') > -1 && ranks.indexOf('66') > -1 && rank === 0){rank = 127; }
|
||||||
|
if(ranks.indexOf('222') > -1 && ranks.indexOf('55') > -1 && rank === 0){rank = 126; }
|
||||||
|
if(ranks.indexOf('222') > -1 && ranks.indexOf('44') > -1 && rank === 0){rank = 125; }
|
||||||
|
if(ranks.indexOf('222') > -1 && ranks.indexOf('33') > -1 && rank === 0){rank = 124; }
|
||||||
|
if(rank !== 0){message = 'Full House'; }
|
||||||
|
}
|
||||||
|
|
||||||
|
//Flush
|
||||||
|
if(rank === 0){
|
||||||
|
if(suits.indexOf('CCCCC') > -1 || suits.indexOf('DDDDD') > -1 || suits.indexOf('HHHHH') > -1 || suits.indexOf('SSSSS') > -1){rank = 123; message = 'Flush';}
|
||||||
|
|
||||||
|
//Straight flush
|
||||||
|
if(cards.indexOf('TC') > -1 && cards.indexOf('JC') > -1 && cards.indexOf('QC') > -1 && cards.indexOf('KC') > -1 && cards.indexOf('AC') > -1 && rank === 123){rank = 302; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('TD') > -1 && cards.indexOf('JD') > -1 && cards.indexOf('QD') > -1 && cards.indexOf('KD') > -1 && cards.indexOf('AD') > -1 && rank === 123){rank = 302; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('TH') > -1 && cards.indexOf('JH') > -1 && cards.indexOf('QH') > -1 && cards.indexOf('KH') > -1 && cards.indexOf('AH') > -1 && rank === 123){rank = 302; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('TS') > -1 && cards.indexOf('JS') > -1 && cards.indexOf('QS') > -1 && cards.indexOf('KS') > -1 && cards.indexOf('AS') > -1 && rank === 123){rank = 302; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('9C') > -1 && cards.indexOf('TC') > -1 && cards.indexOf('JC') > -1 && cards.indexOf('QC') > -1 && cards.indexOf('KC') > -1 && rank === 123){rank = 301; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('9D') > -1 && cards.indexOf('TD') > -1 && cards.indexOf('JD') > -1 && cards.indexOf('QD') > -1 && cards.indexOf('KD') > -1 && rank === 123){rank = 301; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('9H') > -1 && cards.indexOf('TH') > -1 && cards.indexOf('JH') > -1 && cards.indexOf('QH') > -1 && cards.indexOf('KH') > -1 && rank === 123){rank = 301; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('9S') > -1 && cards.indexOf('TS') > -1 && cards.indexOf('JS') > -1 && cards.indexOf('QS') > -1 && cards.indexOf('KS') > -1 && rank === 123){rank = 301; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('8C') > -1 && cards.indexOf('9C') > -1 && cards.indexOf('TC') > -1 && cards.indexOf('JC') > -1 && cards.indexOf('QC') > -1 && rank === 123){rank = 300; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('8D') > -1 && cards.indexOf('9D') > -1 && cards.indexOf('TD') > -1 && cards.indexOf('JD') > -1 && cards.indexOf('QD') > -1 && rank === 123){rank = 300; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('8H') > -1 && cards.indexOf('9H') > -1 && cards.indexOf('TH') > -1 && cards.indexOf('JH') > -1 && cards.indexOf('QH') > -1 && rank === 123){rank = 300; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('8S') > -1 && cards.indexOf('9S') > -1 && cards.indexOf('TS') > -1 && cards.indexOf('JS') > -1 && cards.indexOf('QS') > -1 && rank === 123){rank = 300; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('7C') > -1 && cards.indexOf('8C') > -1 && cards.indexOf('9C') > -1 && cards.indexOf('TC') > -1 && cards.indexOf('JC') > -1 && rank === 123){rank = 299; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('7D') > -1 && cards.indexOf('8D') > -1 && cards.indexOf('9D') > -1 && cards.indexOf('TD') > -1 && cards.indexOf('JD') > -1 && rank === 123){rank = 299; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('7H') > -1 && cards.indexOf('8H') > -1 && cards.indexOf('9H') > -1 && cards.indexOf('TH') > -1 && cards.indexOf('JH') > -1 && rank === 123){rank = 299; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('7S') > -1 && cards.indexOf('8S') > -1 && cards.indexOf('9S') > -1 && cards.indexOf('TS') > -1 && cards.indexOf('JS') > -1 && rank === 123){rank = 299; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('6C') > -1 && cards.indexOf('7C') > -1 && cards.indexOf('8C') > -1 && cards.indexOf('9C') > -1 && cards.indexOf('TC') > -1 && rank === 123){rank = 298; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('6D') > -1 && cards.indexOf('7D') > -1 && cards.indexOf('8D') > -1 && cards.indexOf('9D') > -1 && cards.indexOf('TD') > -1 && rank === 123){rank = 298; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('6H') > -1 && cards.indexOf('7H') > -1 && cards.indexOf('8H') > -1 && cards.indexOf('9H') > -1 && cards.indexOf('TH') > -1 && rank === 123){rank = 298; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('6S') > -1 && cards.indexOf('7S') > -1 && cards.indexOf('8S') > -1 && cards.indexOf('9S') > -1 && cards.indexOf('TS') > -1 && rank === 123){rank = 298; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('5C') > -1 && cards.indexOf('6C') > -1 && cards.indexOf('7C') > -1 && cards.indexOf('8C') > -1 && cards.indexOf('9C') > -1 && rank === 123){rank = 297; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('5D') > -1 && cards.indexOf('6D') > -1 && cards.indexOf('7D') > -1 && cards.indexOf('8D') > -1 && cards.indexOf('9D') > -1 && rank === 123){rank = 297; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('5H') > -1 && cards.indexOf('6H') > -1 && cards.indexOf('7H') > -1 && cards.indexOf('8H') > -1 && cards.indexOf('9H') > -1 && rank === 123){rank = 297; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('5S') > -1 && cards.indexOf('6S') > -1 && cards.indexOf('7S') > -1 && cards.indexOf('8S') > -1 && cards.indexOf('9S') > -1 && rank === 123){rank = 297; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('4C') > -1 && cards.indexOf('5C') > -1 && cards.indexOf('6C') > -1 && cards.indexOf('7C') > -1 && cards.indexOf('8C') > -1 && rank === 123){rank = 296; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('4D') > -1 && cards.indexOf('5D') > -1 && cards.indexOf('6D') > -1 && cards.indexOf('7D') > -1 && cards.indexOf('8D') > -1 && rank === 123){rank = 296; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('4H') > -1 && cards.indexOf('5H') > -1 && cards.indexOf('6H') > -1 && cards.indexOf('7H') > -1 && cards.indexOf('8H') > -1 && rank === 123){rank = 296; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('4S') > -1 && cards.indexOf('5S') > -1 && cards.indexOf('6S') > -1 && cards.indexOf('7S') > -1 && cards.indexOf('8S') > -1 && rank === 123){rank = 296; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('3C') > -1 && cards.indexOf('4C') > -1 && cards.indexOf('5C') > -1 && cards.indexOf('6C') > -1 && cards.indexOf('7C') > -1 && rank === 123){rank = 295; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('3D') > -1 && cards.indexOf('4D') > -1 && cards.indexOf('5D') > -1 && cards.indexOf('6D') > -1 && cards.indexOf('7D') > -1 && rank === 123){rank = 295; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('3H') > -1 && cards.indexOf('4H') > -1 && cards.indexOf('5H') > -1 && cards.indexOf('6H') > -1 && cards.indexOf('7H') > -1 && rank === 123){rank = 295; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('3S') > -1 && cards.indexOf('4S') > -1 && cards.indexOf('5S') > -1 && cards.indexOf('6S') > -1 && cards.indexOf('7S') > -1 && rank === 123){rank = 295; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('2C') > -1 && cards.indexOf('3C') > -1 && cards.indexOf('4C') > -1 && cards.indexOf('5C') > -1 && cards.indexOf('6C') > -1 && rank === 123){rank = 294; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('2D') > -1 && cards.indexOf('3D') > -1 && cards.indexOf('4D') > -1 && cards.indexOf('5D') > -1 && cards.indexOf('6D') > -1 && rank === 123){rank = 294; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('2H') > -1 && cards.indexOf('3H') > -1 && cards.indexOf('4H') > -1 && cards.indexOf('5H') > -1 && cards.indexOf('6H') > -1 && rank === 123){rank = 294; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('2S') > -1 && cards.indexOf('3S') > -1 && cards.indexOf('4S') > -1 && cards.indexOf('5S') > -1 && cards.indexOf('6S') > -1 && rank === 123){rank = 294; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('AC') > -1 && cards.indexOf('2C') > -1 && cards.indexOf('3C') > -1 && cards.indexOf('4C') > -1 && cards.indexOf('5C') > -1 && rank === 123){rank = 293; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('AS') > -1 && cards.indexOf('2S') > -1 && cards.indexOf('3S') > -1 && cards.indexOf('4S') > -1 && cards.indexOf('5S') > -1 && rank === 123){rank = 293; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('AH') > -1 && cards.indexOf('2H') > -1 && cards.indexOf('3H') > -1 && cards.indexOf('4H') > -1 && cards.indexOf('5H') > -1 && rank === 123){rank = 293; message = 'Straight Flush';}
|
||||||
|
if(cards.indexOf('AD') > -1 && cards.indexOf('2D') > -1 && cards.indexOf('3D') > -1 && cards.indexOf('4D') > -1 && cards.indexOf('5D') > -1 && rank === 123){rank = 293; message = 'Straight Flush';}
|
||||||
|
if(rank === 123){rank = rank + rankKickers(ranks, 5);}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Straight
|
||||||
|
if(rank === 0){
|
||||||
|
if(cards.indexOf('T') > -1 && cards.indexOf('J') > -1 && cards.indexOf('Q') > -1 && cards.indexOf('K') > -1 && cards.indexOf('A') > -1){rank = 122; }
|
||||||
|
if(cards.indexOf('9') > -1 && cards.indexOf('T') > -1 && cards.indexOf('J') > -1 && cards.indexOf('Q') > -1 && cards.indexOf('K') > -1 && rank === 0){rank = 121; }
|
||||||
|
if(cards.indexOf('8') > -1 && cards.indexOf('9') > -1 && cards.indexOf('T') > -1 && cards.indexOf('J') > -1 && cards.indexOf('Q') > -1 && rank === 0){rank = 120; }
|
||||||
|
if(cards.indexOf('7') > -1 && cards.indexOf('8') > -1 && cards.indexOf('9') > -1 && cards.indexOf('T') > -1 && cards.indexOf('J') > -1 && rank === 0){rank = 119; }
|
||||||
|
if(cards.indexOf('6') > -1 && cards.indexOf('7') > -1 && cards.indexOf('8') > -1 && cards.indexOf('9') > -1 && cards.indexOf('T') > -1 && rank === 0){rank = 118; }
|
||||||
|
if(cards.indexOf('5') > -1 && cards.indexOf('6') > -1 && cards.indexOf('7') > -1 && cards.indexOf('8') > -1 && cards.indexOf('9') > -1 && rank === 0){rank = 117; }
|
||||||
|
if(cards.indexOf('4') > -1 && cards.indexOf('5') > -1 && cards.indexOf('6') > -1 && cards.indexOf('7') > -1 && cards.indexOf('8') > -1 && rank === 0){rank = 116; }
|
||||||
|
if(cards.indexOf('3') > -1 && cards.indexOf('4') > -1 && cards.indexOf('5') > -1 && cards.indexOf('6') > -1 && cards.indexOf('7') > -1 && rank === 0){rank = 115; }
|
||||||
|
if(cards.indexOf('2') > -1 && cards.indexOf('3') > -1 && cards.indexOf('4') > -1 && cards.indexOf('5') > -1 && cards.indexOf('6') > -1 && rank === 0){rank = 114; }
|
||||||
|
if(cards.indexOf('A') > -1 && cards.indexOf('2') > -1 && cards.indexOf('3') > -1 && cards.indexOf('4') > -1 && cards.indexOf('5') > -1 && rank === 0){rank = 113; }
|
||||||
|
if(rank !== 0){message = 'Straight'; }
|
||||||
|
}
|
||||||
|
|
||||||
|
//Three of a kind
|
||||||
|
if(rank === 0){
|
||||||
|
if(ranks.indexOf('AAA') > -1){rank = 112 + rankKickers(ranks.replace('AAA', ''), 2); }
|
||||||
|
if(ranks.indexOf('KKK') > -1 && rank === 0){rank = 111 + rankKickers(ranks.replace('KKK', ''), 2); }
|
||||||
|
if(ranks.indexOf('QQQ') > -1 && rank === 0){rank = 110 + rankKickers(ranks.replace('QQQ', ''), 2); }
|
||||||
|
if(ranks.indexOf('JJJ') > -1 && rank === 0){rank = 109 + rankKickers(ranks.replace('JJJ', ''), 2); }
|
||||||
|
if(ranks.indexOf('TTT') > -1 && rank === 0){rank = 108 + rankKickers(ranks.replace('TTT', ''), 2); }
|
||||||
|
if(ranks.indexOf('999') > -1 && rank === 0){rank = 107 + rankKickers(ranks.replace('999', ''), 2); }
|
||||||
|
if(ranks.indexOf('888') > -1 && rank === 0){rank = 106 + rankKickers(ranks.replace('888', ''), 2); }
|
||||||
|
if(ranks.indexOf('777') > -1 && rank === 0){rank = 105 + rankKickers(ranks.replace('777', ''), 2); }
|
||||||
|
if(ranks.indexOf('666') > -1 && rank === 0){rank = 104 + rankKickers(ranks.replace('666', ''), 2); }
|
||||||
|
if(ranks.indexOf('555') > -1 && rank === 0){rank = 103 + rankKickers(ranks.replace('555', ''), 2); }
|
||||||
|
if(ranks.indexOf('444') > -1 && rank === 0){rank = 102 + rankKickers(ranks.replace('444', ''), 2); }
|
||||||
|
if(ranks.indexOf('333') > -1 && rank === 0){rank = 101 + rankKickers(ranks.replace('333', ''), 2); }
|
||||||
|
if(ranks.indexOf('222') > -1 && rank === 0){rank = 100 + rankKickers(ranks.replace('222', ''), 2); }
|
||||||
|
if(rank !== 0){message = 'Three of a Kind'; }
|
||||||
|
}
|
||||||
|
|
||||||
|
//Two pair
|
||||||
|
if(rank === 0){
|
||||||
|
if(ranks.indexOf('AA') > -1 && ranks.indexOf('KK') > -1){rank = 99 + rankKickers(ranks.replace('AA', '').replace('KK', ''), 1); }
|
||||||
|
if(ranks.indexOf('AA') > -1 && ranks.indexOf('QQ') > -1 && rank === 0){rank = 98 + rankKickers(ranks.replace('AA', '').replace('QQ', ''), 1); }
|
||||||
|
if(ranks.indexOf('AA') > -1 && ranks.indexOf('JJ') > -1 && rank === 0){rank = 97 + rankKickers(ranks.replace('AA', '').replace('JJ', ''), 1); }
|
||||||
|
if(ranks.indexOf('AA') > -1 && ranks.indexOf('TT') > -1 && rank === 0){rank = 96 + rankKickers(ranks.replace('AA', '').replace('TT', ''), 1); }
|
||||||
|
if(ranks.indexOf('AA') > -1 && ranks.indexOf('99') > -1 && rank === 0){rank = 95 + rankKickers(ranks.replace('AA', '').replace('99', ''), 1); }
|
||||||
|
if(ranks.indexOf('AA') > -1 && ranks.indexOf('88') > -1 && rank === 0){rank = 94 + rankKickers(ranks.replace('AA', '').replace('88', ''), 1); }
|
||||||
|
if(ranks.indexOf('AA') > -1 && ranks.indexOf('77') > -1 && rank === 0){rank = 93 + rankKickers(ranks.replace('AA', '').replace('77', ''), 1); }
|
||||||
|
if(ranks.indexOf('AA') > -1 && ranks.indexOf('66') > -1 && rank === 0){rank = 92 + rankKickers(ranks.replace('AA', '').replace('66', ''), 1); }
|
||||||
|
if(ranks.indexOf('AA') > -1 && ranks.indexOf('55') > -1 && rank === 0){rank = 91 + rankKickers(ranks.replace('AA', '').replace('55', ''), 1); }
|
||||||
|
if(ranks.indexOf('AA') > -1 && ranks.indexOf('44') > -1 && rank === 0){rank = 90 + rankKickers(ranks.replace('AA', '').replace('44', ''), 1); }
|
||||||
|
if(ranks.indexOf('AA') > -1 && ranks.indexOf('33') > -1 && rank === 0){rank = 89 + rankKickers(ranks.replace('AA', '').replace('33', ''), 1); }
|
||||||
|
if(ranks.indexOf('AA') > -1 && ranks.indexOf('22') > -1 && rank === 0){rank = 88 + rankKickers(ranks.replace('AA', '').replace('22', ''), 1); }
|
||||||
|
if(ranks.indexOf('KK') > -1 && ranks.indexOf('QQ') > -1 && rank === 0){rank = 87 + rankKickers(ranks.replace('KK', '').replace('QQ', ''), 1); }
|
||||||
|
if(ranks.indexOf('KK') > -1 && ranks.indexOf('JJ') > -1 && rank === 0){rank = 86 + rankKickers(ranks.replace('KK', '').replace('JJ', ''), 1); }
|
||||||
|
if(ranks.indexOf('KK') > -1 && ranks.indexOf('TT') > -1 && rank === 0){rank = 85 + rankKickers(ranks.replace('KK', '').replace('TT', ''), 1); }
|
||||||
|
if(ranks.indexOf('KK') > -1 && ranks.indexOf('99') > -1 && rank === 0){rank = 84 + rankKickers(ranks.replace('KK', '').replace('99', ''), 1); }
|
||||||
|
if(ranks.indexOf('KK') > -1 && ranks.indexOf('88') > -1 && rank === 0){rank = 83 + rankKickers(ranks.replace('KK', '').replace('88', ''), 1); }
|
||||||
|
if(ranks.indexOf('KK') > -1 && ranks.indexOf('77') > -1 && rank === 0){rank = 82 + rankKickers(ranks.replace('KK', '').replace('77', ''), 1); }
|
||||||
|
if(ranks.indexOf('KK') > -1 && ranks.indexOf('66') > -1 && rank === 0){rank = 81 + rankKickers(ranks.replace('KK', '').replace('66', ''), 1); }
|
||||||
|
if(ranks.indexOf('KK') > -1 && ranks.indexOf('55') > -1 && rank === 0){rank = 80 + rankKickers(ranks.replace('KK', '').replace('55', ''), 1); }
|
||||||
|
if(ranks.indexOf('KK') > -1 && ranks.indexOf('44') > -1 && rank === 0){rank = 79 + rankKickers(ranks.replace('KK', '').replace('44', ''), 1); }
|
||||||
|
if(ranks.indexOf('KK') > -1 && ranks.indexOf('33') > -1 && rank === 0){rank = 78 + rankKickers(ranks.replace('KK', '').replace('33', ''), 1); }
|
||||||
|
if(ranks.indexOf('KK') > -1 && ranks.indexOf('22') > -1 && rank === 0){rank = 77 + rankKickers(ranks.replace('KK', '').replace('22', ''), 1); }
|
||||||
|
if(ranks.indexOf('QQ') > -1 && ranks.indexOf('JJ') > -1 && rank === 0){rank = 76 + rankKickers(ranks.replace('QQ', '').replace('JJ', ''), 1); }
|
||||||
|
if(ranks.indexOf('QQ') > -1 && ranks.indexOf('TT') > -1 && rank === 0){rank = 75 + rankKickers(ranks.replace('QQ', '').replace('TT', ''), 1); }
|
||||||
|
if(ranks.indexOf('QQ') > -1 && ranks.indexOf('99') > -1 && rank === 0){rank = 74 + rankKickers(ranks.replace('QQ', '').replace('99', ''), 1); }
|
||||||
|
if(ranks.indexOf('QQ') > -1 && ranks.indexOf('88') > -1 && rank === 0){rank = 73 + rankKickers(ranks.replace('QQ', '').replace('88', ''), 1); }
|
||||||
|
if(ranks.indexOf('QQ') > -1 && ranks.indexOf('77') > -1 && rank === 0){rank = 72 + rankKickers(ranks.replace('QQ', '').replace('77', ''), 1); }
|
||||||
|
if(ranks.indexOf('QQ') > -1 && ranks.indexOf('66') > -1 && rank === 0){rank = 71 + rankKickers(ranks.replace('QQ', '').replace('66', ''), 1); }
|
||||||
|
if(ranks.indexOf('QQ') > -1 && ranks.indexOf('55') > -1 && rank === 0){rank = 70 + rankKickers(ranks.replace('QQ', '').replace('55', ''), 1); }
|
||||||
|
if(ranks.indexOf('QQ') > -1 && ranks.indexOf('44') > -1 && rank === 0){rank = 69 + rankKickers(ranks.replace('QQ', '').replace('44', ''), 1); }
|
||||||
|
if(ranks.indexOf('QQ') > -1 && ranks.indexOf('33') > -1 && rank === 0){rank = 68 + rankKickers(ranks.replace('QQ', '').replace('33', ''), 1); }
|
||||||
|
if(ranks.indexOf('QQ') > -1 && ranks.indexOf('22') > -1 && rank === 0){rank = 67 + rankKickers(ranks.replace('QQ', '').replace('22', ''), 1); }
|
||||||
|
if(ranks.indexOf('JJ') > -1 && ranks.indexOf('TT') > -1 && rank === 0){rank = 66 + rankKickers(ranks.replace('JJ', '').replace('TT', ''), 1); }
|
||||||
|
if(ranks.indexOf('JJ') > -1 && ranks.indexOf('99') > -1 && rank === 0){rank = 65 + rankKickers(ranks.replace('JJ', '').replace('99', ''), 1); }
|
||||||
|
if(ranks.indexOf('JJ') > -1 && ranks.indexOf('88') > -1 && rank === 0){rank = 64 + rankKickers(ranks.replace('JJ', '').replace('88', ''), 1); }
|
||||||
|
if(ranks.indexOf('JJ') > -1 && ranks.indexOf('77') > -1 && rank === 0){rank = 63 + rankKickers(ranks.replace('JJ', '').replace('77', ''), 1); }
|
||||||
|
if(ranks.indexOf('JJ') > -1 && ranks.indexOf('66') > -1 && rank === 0){rank = 62 + rankKickers(ranks.replace('JJ', '').replace('66', ''), 1); }
|
||||||
|
if(ranks.indexOf('JJ') > -1 && ranks.indexOf('55') > -1 && rank === 0){rank = 61 + rankKickers(ranks.replace('JJ', '').replace('55', ''), 1); }
|
||||||
|
if(ranks.indexOf('JJ') > -1 && ranks.indexOf('44') > -1 && rank === 0){rank = 60 + rankKickers(ranks.replace('JJ', '').replace('44', ''), 1); }
|
||||||
|
if(ranks.indexOf('JJ') > -1 && ranks.indexOf('33') > -1 && rank === 0){rank = 59 + rankKickers(ranks.replace('JJ', '').replace('33', ''), 1); }
|
||||||
|
if(ranks.indexOf('JJ') > -1 && ranks.indexOf('22') > -1 && rank === 0){rank = 58 + rankKickers(ranks.replace('JJ', '').replace('22', ''), 1); }
|
||||||
|
if(ranks.indexOf('TT') > -1 && ranks.indexOf('99') > -1 && rank === 0){rank = 57 + rankKickers(ranks.replace('TT', '').replace('99', ''), 1); }
|
||||||
|
if(ranks.indexOf('TT') > -1 && ranks.indexOf('88') > -1 && rank === 0){rank = 56 + rankKickers(ranks.replace('TT', '').replace('88', ''), 1); }
|
||||||
|
if(ranks.indexOf('TT') > -1 && ranks.indexOf('77') > -1 && rank === 0){rank = 55 + rankKickers(ranks.replace('TT', '').replace('77', ''), 1); }
|
||||||
|
if(ranks.indexOf('TT') > -1 && ranks.indexOf('66') > -1 && rank === 0){rank = 54 + rankKickers(ranks.replace('TT', '').replace('66', ''), 1); }
|
||||||
|
if(ranks.indexOf('TT') > -1 && ranks.indexOf('55') > -1 && rank === 0){rank = 53 + rankKickers(ranks.replace('TT', '').replace('55', ''), 1); }
|
||||||
|
if(ranks.indexOf('TT') > -1 && ranks.indexOf('44') > -1 && rank === 0){rank = 52 + rankKickers(ranks.replace('TT', '').replace('44', ''), 1); }
|
||||||
|
if(ranks.indexOf('TT') > -1 && ranks.indexOf('33') > -1 && rank === 0){rank = 51 + rankKickers(ranks.replace('TT', '').replace('33', ''), 1); }
|
||||||
|
if(ranks.indexOf('TT') > -1 && ranks.indexOf('22') > -1 && rank === 0){rank = 50 + rankKickers(ranks.replace('TT', '').replace('22', ''), 1); }
|
||||||
|
if(ranks.indexOf('99') > -1 && ranks.indexOf('88') > -1 && rank === 0){rank = 49 + rankKickers(ranks.replace('99', '').replace('88', ''), 1); }
|
||||||
|
if(ranks.indexOf('99') > -1 && ranks.indexOf('77') > -1 && rank === 0){rank = 48 + rankKickers(ranks.replace('99', '').replace('77', ''), 1); }
|
||||||
|
if(ranks.indexOf('99') > -1 && ranks.indexOf('66') > -1 && rank === 0){rank = 47 + rankKickers(ranks.replace('99', '').replace('66', ''), 1); }
|
||||||
|
if(ranks.indexOf('99') > -1 && ranks.indexOf('55') > -1 && rank === 0){rank = 46 + rankKickers(ranks.replace('99', '').replace('55', ''), 1); }
|
||||||
|
if(ranks.indexOf('99') > -1 && ranks.indexOf('44') > -1 && rank === 0){rank = 45 + rankKickers(ranks.replace('99', '').replace('44', ''), 1); }
|
||||||
|
if(ranks.indexOf('99') > -1 && ranks.indexOf('33') > -1 && rank === 0){rank = 44 + rankKickers(ranks.replace('99', '').replace('33', ''), 1); }
|
||||||
|
if(ranks.indexOf('99') > -1 && ranks.indexOf('22') > -1 && rank === 0){rank = 43 + rankKickers(ranks.replace('99', '').replace('22', ''), 1); }
|
||||||
|
if(ranks.indexOf('88') > -1 && ranks.indexOf('77') > -1 && rank === 0){rank = 42 + rankKickers(ranks.replace('88', '').replace('77', ''), 1); }
|
||||||
|
if(ranks.indexOf('88') > -1 && ranks.indexOf('66') > -1 && rank === 0){rank = 41 + rankKickers(ranks.replace('88', '').replace('66', ''), 1); }
|
||||||
|
if(ranks.indexOf('88') > -1 && ranks.indexOf('55') > -1 && rank === 0){rank = 40 + rankKickers(ranks.replace('88', '').replace('55', ''), 1); }
|
||||||
|
if(ranks.indexOf('88') > -1 && ranks.indexOf('44') > -1 && rank === 0){rank = 39 + rankKickers(ranks.replace('88', '').replace('44', ''), 1); }
|
||||||
|
if(ranks.indexOf('88') > -1 && ranks.indexOf('33') > -1 && rank === 0){rank = 38 + rankKickers(ranks.replace('88', '').replace('33', ''), 1); }
|
||||||
|
if(ranks.indexOf('88') > -1 && ranks.indexOf('22') > -1 && rank === 0){rank = 37 + rankKickers(ranks.replace('88', '').replace('22', ''), 1); }
|
||||||
|
if(ranks.indexOf('77') > -1 && ranks.indexOf('66') > -1 && rank === 0){rank = 36 + rankKickers(ranks.replace('77', '').replace('66', ''), 1); }
|
||||||
|
if(ranks.indexOf('77') > -1 && ranks.indexOf('55') > -1 && rank === 0){rank = 35 + rankKickers(ranks.replace('77', '').replace('55', ''), 1); }
|
||||||
|
if(ranks.indexOf('77') > -1 && ranks.indexOf('44') > -1 && rank === 0){rank = 34 + rankKickers(ranks.replace('77', '').replace('44', ''), 1); }
|
||||||
|
if(ranks.indexOf('77') > -1 && ranks.indexOf('33') > -1 && rank === 0){rank = 33 + rankKickers(ranks.replace('77', '').replace('33', ''), 1); }
|
||||||
|
if(ranks.indexOf('77') > -1 && ranks.indexOf('22') > -1 && rank === 0){rank = 32 + rankKickers(ranks.replace('77', '').replace('22', ''), 1); }
|
||||||
|
if(ranks.indexOf('66') > -1 && ranks.indexOf('55') > -1 && rank === 0){rank = 31 + rankKickers(ranks.replace('66', '').replace('55', ''), 1); }
|
||||||
|
if(ranks.indexOf('66') > -1 && ranks.indexOf('44') > -1 && rank === 0){rank = 30 + rankKickers(ranks.replace('66', '').replace('44', ''), 1); }
|
||||||
|
if(ranks.indexOf('66') > -1 && ranks.indexOf('33') > -1 && rank === 0){rank = 29 + rankKickers(ranks.replace('66', '').replace('33', ''), 1); }
|
||||||
|
if(ranks.indexOf('66') > -1 && ranks.indexOf('22') > -1 && rank === 0){rank = 28 + rankKickers(ranks.replace('66', '').replace('22', ''), 1); }
|
||||||
|
if(ranks.indexOf('55') > -1 && ranks.indexOf('44') > -1 && rank === 0){rank = 27 + rankKickers(ranks.replace('55', '').replace('44', ''), 1); }
|
||||||
|
if(ranks.indexOf('55') > -1 && ranks.indexOf('33') > -1 && rank === 0){rank = 26 + rankKickers(ranks.replace('55', '').replace('33', ''), 1); }
|
||||||
|
if(ranks.indexOf('55') > -1 && ranks.indexOf('22') > -1 && rank === 0){rank = 25 + rankKickers(ranks.replace('55', '').replace('22', ''), 1); }
|
||||||
|
if(ranks.indexOf('44') > -1 && ranks.indexOf('33') > -1 && rank === 0){rank = 24 + rankKickers(ranks.replace('44', '').replace('33', ''), 1); }
|
||||||
|
if(ranks.indexOf('44') > -1 && ranks.indexOf('22') > -1 && rank === 0){rank = 23 + rankKickers(ranks.replace('44', '').replace('22', ''), 1); }
|
||||||
|
if(ranks.indexOf('33') > -1 && ranks.indexOf('22') > -1 && rank === 0){rank = 22 + rankKickers(ranks.replace('33', '').replace('22', ''), 1); }
|
||||||
|
if(rank !== 0){message = 'Two Pair'; }
|
||||||
|
}
|
||||||
|
|
||||||
|
//One Pair
|
||||||
|
if(rank === 0){
|
||||||
|
if(ranks.indexOf('AA') > -1){rank = 21 + rankKickers(ranks.replace('AA', ''), 3); }
|
||||||
|
if(ranks.indexOf('KK') > -1 && rank === 0){rank = 20 + rankKickers(ranks.replace('KK', ''), 3); }
|
||||||
|
if(ranks.indexOf('QQ') > -1 && rank === 0){rank = 19 + rankKickers(ranks.replace('QQ', ''), 3); }
|
||||||
|
if(ranks.indexOf('JJ') > -1 && rank === 0){rank = 18 + rankKickers(ranks.replace('JJ', ''), 3); }
|
||||||
|
if(ranks.indexOf('TT') > -1 && rank === 0){rank = 17 + rankKickers(ranks.replace('TT', ''), 3); }
|
||||||
|
if(ranks.indexOf('99') > -1 && rank === 0){rank = 16 + rankKickers(ranks.replace('99', ''), 3); }
|
||||||
|
if(ranks.indexOf('88') > -1 && rank === 0){rank = 15 + rankKickers(ranks.replace('88', ''), 3); }
|
||||||
|
if(ranks.indexOf('77') > -1 && rank === 0){rank = 14 + rankKickers(ranks.replace('77', ''), 3); }
|
||||||
|
if(ranks.indexOf('66') > -1 && rank === 0){rank = 13 + rankKickers(ranks.replace('66', ''), 3); }
|
||||||
|
if(ranks.indexOf('55') > -1 && rank === 0){rank = 12 + rankKickers(ranks.replace('55', ''), 3); }
|
||||||
|
if(ranks.indexOf('44') > -1 && rank === 0){rank = 11 + rankKickers(ranks.replace('44', ''), 3); }
|
||||||
|
if(ranks.indexOf('33') > -1 && rank === 0){rank = 10 + rankKickers(ranks.replace('33', ''), 3); }
|
||||||
|
if(ranks.indexOf('22') > -1 && rank === 0){rank = 9 + rankKickers(ranks.replace('22', ''), 3); }
|
||||||
|
if(rank !== 0){message = 'Pair'; }
|
||||||
|
}
|
||||||
|
|
||||||
|
//High Card
|
||||||
|
if(rank === 0){
|
||||||
|
if(ranks.indexOf('A') > -1){rank = 8 + rankKickers(ranks.replace('A', ''), 4); }
|
||||||
|
if(ranks.indexOf('K') > -1 && rank === 0){rank = 7 + rankKickers(ranks.replace('K', ''), 4); }
|
||||||
|
if(ranks.indexOf('Q') > -1 && rank === 0){rank = 6 + rankKickers(ranks.replace('Q', ''), 4); }
|
||||||
|
if(ranks.indexOf('J') > -1 && rank === 0){rank = 5 + rankKickers(ranks.replace('J', ''), 4); }
|
||||||
|
if(ranks.indexOf('T') > -1 && rank === 0){rank = 4 + rankKickers(ranks.replace('T', ''), 4); }
|
||||||
|
if(ranks.indexOf('9') > -1 && rank === 0){rank = 3 + rankKickers(ranks.replace('9', ''), 4); }
|
||||||
|
if(ranks.indexOf('8') > -1 && rank === 0){rank = 2 + rankKickers(ranks.replace('8', ''), 4); }
|
||||||
|
if(ranks.indexOf('7') > -1 && rank === 0){rank = 1 + rankKickers(ranks.replace('7', ''), 4); }
|
||||||
|
if(rank !== 0){message = 'High Card'; }
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
rank : rank,
|
||||||
|
message : message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function rankKickers(ranks, noOfCards){
|
||||||
|
var i, kickerRank, myRanks, rank;
|
||||||
|
kickerRank = 0.0000;
|
||||||
|
myRanks = [];
|
||||||
|
rank = '';
|
||||||
|
for(i=0;i<= ranks.length;i+=1){
|
||||||
|
rank = ranks.substr(i, 1);
|
||||||
|
if(rank === 'A'){myRanks.push(0.2048); }
|
||||||
|
if(rank === 'K'){myRanks.push(0.1024); }
|
||||||
|
if(rank === 'Q'){myRanks.push(0.0512); }
|
||||||
|
if(rank === 'J'){myRanks.push(0.0256); }
|
||||||
|
if(rank === 'T'){myRanks.push(0.0128); }
|
||||||
|
if(rank === '9'){myRanks.push(0.0064); }
|
||||||
|
if(rank === '8'){myRanks.push(0.0032); }
|
||||||
|
if(rank === '7'){myRanks.push(0.0016); }
|
||||||
|
if(rank === '6'){myRanks.push(0.0008); }
|
||||||
|
if(rank === '5'){myRanks.push(0.0004); }
|
||||||
|
if(rank === '4'){myRanks.push(0.0002); }
|
||||||
|
if(rank === '3'){myRanks.push(0.0001); }
|
||||||
|
if(rank === '2'){myRanks.push(0.0000); }
|
||||||
|
}
|
||||||
|
myRanks.sort(function(a, b){
|
||||||
|
return b - a;
|
||||||
|
});
|
||||||
|
for(i=0;i< noOfCards;i+=1){
|
||||||
|
kickerRank += myRanks[i];
|
||||||
|
}
|
||||||
|
return kickerRank;
|
||||||
|
}
|
||||||
|
|
||||||
|
function rankHands(hands){
|
||||||
|
var x, myResult;
|
||||||
|
for(x = 0; x < hands.length; x += 1){
|
||||||
|
myResult = rankHandInt(hands[x]);
|
||||||
|
hands[x].rank = myResult.rank;
|
||||||
|
hands[x].message = myResult.message;
|
||||||
|
}
|
||||||
|
return hands;
|
||||||
|
}
|
470
game-server/app/game/player.js
Normal file
@ -0,0 +1,470 @@
|
|||||||
|
var logger = require('pomelo-logger').getLogger('game-log', __filename);
|
||||||
|
var Hand = require('./hand');
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = Player = function(playerName, chips, uid, table){
|
||||||
|
this.playerName = playerName;
|
||||||
|
this.id = uid;
|
||||||
|
this.chips = chips;
|
||||||
|
this.folded = false;
|
||||||
|
this.allIn = false;
|
||||||
|
this.talked = false;
|
||||||
|
this.table = table; //Circular reference to allow reference back to parent object.
|
||||||
|
this.cards = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
Player.prototype.GetChips = function(cash){
|
||||||
|
this.chips += cash;
|
||||||
|
};
|
||||||
|
|
||||||
|
Player.prototype.Check = function(){
|
||||||
|
var checkAllow, v, i;
|
||||||
|
checkAllow = true;
|
||||||
|
for(v=0;v<this.table.game.bets.length;v += 1){
|
||||||
|
if(this.table.game.bets[v] !== 0){
|
||||||
|
checkAllow = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(checkAllow){
|
||||||
|
for(i=0;i< this.table.players.length;i+=1){
|
||||||
|
if(this === this.table.players[i]){
|
||||||
|
this.table.game.bets[i] = 0;
|
||||||
|
this.talked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// attempt to progress the game
|
||||||
|
this.turnBet = {action: "check", playerName: this.playerName};
|
||||||
|
this.table.actions.push(this.turnBet);
|
||||||
|
progress(this.table);
|
||||||
|
}else{
|
||||||
|
logger.debug('check-not-allowed by '+this.id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Player.prototype.Fold = function(){
|
||||||
|
var i, bet;
|
||||||
|
//Move any current bet into the pot
|
||||||
|
for(i=0;i< this.table.players.length;i+=1){
|
||||||
|
if(this.id === this.table.players[i].id){
|
||||||
|
bet = parseInt(this.table.game.bets[i], 10);
|
||||||
|
this.table.game.roundBets[i] += bet;
|
||||||
|
this.table.game.bets[i] = 0;
|
||||||
|
this.table.game.pot += bet;
|
||||||
|
this.talked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Mark the player as folded
|
||||||
|
this.folded = true;
|
||||||
|
this.turnBet = {action: "fold", playerName: this.playerName};
|
||||||
|
this.table.actions.push(this.turnBet);
|
||||||
|
// attempt to progress the game
|
||||||
|
progress(this.table);
|
||||||
|
};
|
||||||
|
|
||||||
|
Player.prototype.Bet = function(bet){
|
||||||
|
var i = this.getIndex();
|
||||||
|
var maxBet = getMaxBet(this.table.game.bets);
|
||||||
|
var totalBet = bet + maxBet;
|
||||||
|
var currentBet = this.table.game.bets[i];
|
||||||
|
if((currentBet + this.chips) > totalBet){
|
||||||
|
this.table.players[i].chips -= (totalBet - currentBet);
|
||||||
|
this.table.game.bets[i] = totalBet; // this.table.game.bets[i] += totalBet;
|
||||||
|
this.talked = true;
|
||||||
|
// attempt to progress the game
|
||||||
|
this.turnBet = {action: "bet", playerName: this.playerName, amount: totalBet};
|
||||||
|
this.table.actions.push(this.turnBet);
|
||||||
|
progress(this.table);
|
||||||
|
}else{
|
||||||
|
logger.debug('forced-all-in: '+this.id);
|
||||||
|
this.AllIn();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Player.prototype.getIndex = function(){
|
||||||
|
var index;
|
||||||
|
for(i=0;i< this.table.players.length;i+=1){
|
||||||
|
if(this === this.table.players[i]){
|
||||||
|
index = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player.prototype.Call = function(){
|
||||||
|
var maxBet, i;
|
||||||
|
// console.log('bets', this.table.game.bets);
|
||||||
|
maxBet = getMaxBet(this.table.game.bets);
|
||||||
|
if(this.chips > maxBet){
|
||||||
|
// console.log('higher chips', this.chips, maxBet);
|
||||||
|
//Match the highest bet
|
||||||
|
for(i=0;i< this.table.players.length;i+=1){
|
||||||
|
if(this === this.table.players[i]){
|
||||||
|
if(this.table.game.bets[i] >= 0){
|
||||||
|
this.chips += this.table.game.bets[i];
|
||||||
|
}
|
||||||
|
this.chips -= maxBet;
|
||||||
|
this.table.game.bets[i] = maxBet;
|
||||||
|
this.talked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// attempt to progress the game
|
||||||
|
this.turnBet = {action: "call", playerName: this.playerName, amount: maxBet};
|
||||||
|
this.table.actions.push(this.turnBet);
|
||||||
|
progress(this.table);
|
||||||
|
}else{
|
||||||
|
logger.debug('forced-all-in: '+this.id);
|
||||||
|
this.AllIn();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Player.prototype.AllIn = function(){
|
||||||
|
var i, allInValue=0;
|
||||||
|
for(i=0;i< this.table.players.length;i+=1){
|
||||||
|
if(this === this.table.players[i]){
|
||||||
|
if(this.table.players[i].chips !== 0){
|
||||||
|
allInValue = this.table.players[i].chips;
|
||||||
|
this.table.game.bets[i] += this.table.players[i].chips;
|
||||||
|
this.table.players[i].chips = 0;
|
||||||
|
|
||||||
|
this.allIn = true;
|
||||||
|
this.talked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// attempt to progress the game
|
||||||
|
this.turnBet = {action: "allin", playerName: this.playerName, amount: allInValue};
|
||||||
|
this.table.actions.push(this.turnBet);
|
||||||
|
|
||||||
|
progress(this.table);
|
||||||
|
};
|
||||||
|
|
||||||
|
function progress(table){
|
||||||
|
var i, j, cards, hand;
|
||||||
|
if(table.game){
|
||||||
|
table.eventEmitter.emit("turnEnd");
|
||||||
|
table.stopTimer();
|
||||||
|
if(checkForEndOfGame(table)){
|
||||||
|
//Move all bets to the pot
|
||||||
|
for(i=0;i< table.game.bets.length;i+=1){
|
||||||
|
table.game.pot += parseInt(table.game.bets[i], 10);
|
||||||
|
table.game.roundBets[i] += parseInt(table.game.bets[i], 10);
|
||||||
|
}
|
||||||
|
completeGame(table);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(checkForEndOfRound(table) === true){
|
||||||
|
//Move all bets to the pot
|
||||||
|
for(i=0;i< table.game.bets.length;i+=1){
|
||||||
|
table.game.pot += parseInt(table.game.bets[i], 10);
|
||||||
|
table.game.roundBets[i] += parseInt(table.game.bets[i], 10);
|
||||||
|
}
|
||||||
|
if(table.game.roundName === 'River'){
|
||||||
|
completeGame(table);
|
||||||
|
return;
|
||||||
|
}else if(table.game.roundName === 'Turn'){
|
||||||
|
table.game.roundName = 'River';
|
||||||
|
table.game.deck.pop(); //Burn a card
|
||||||
|
table.game.board.push(table.game.deck.pop()); //Turn a card
|
||||||
|
//table.game.bets.splice(0,table.game.bets.length-1);
|
||||||
|
for(i=0;i< table.game.bets.length;i+=1){
|
||||||
|
table.game.bets[i] = 0;
|
||||||
|
}
|
||||||
|
for(i=0;i< table.players.length;i+=1){
|
||||||
|
table.players[i].talked = false;
|
||||||
|
}
|
||||||
|
table.eventEmitter.emit( "deal" );
|
||||||
|
}else if(table.game.roundName === 'Flop'){
|
||||||
|
table.game.roundName = 'Turn';
|
||||||
|
table.game.deck.pop(); //Burn a card
|
||||||
|
table.game.board.push(table.game.deck.pop()); //Turn a card
|
||||||
|
for(i=0;i< table.game.bets.length;i+=1){
|
||||||
|
table.game.bets[i] = 0;
|
||||||
|
}
|
||||||
|
for(i=0;i< table.players.length;i+=1){
|
||||||
|
table.players[i].talked = false;
|
||||||
|
}
|
||||||
|
table.eventEmitter.emit( "deal" );
|
||||||
|
}else if(table.game.roundName === 'Deal'){
|
||||||
|
table.game.roundName = 'Flop';
|
||||||
|
table.game.deck.pop(); //Burn a card
|
||||||
|
for(i=0;i< 3;i+=1){ //Turn three cards
|
||||||
|
table.game.board.push(table.game.deck.pop());
|
||||||
|
}
|
||||||
|
//table.game.bets.splice(0,table.game.bets.length-1);
|
||||||
|
for(i=0;i< table.game.bets.length;i+=1){
|
||||||
|
table.game.bets[i] = 0;
|
||||||
|
}
|
||||||
|
for(i=0;i< table.players.length;i+=1){
|
||||||
|
table.players[i].talked = false;
|
||||||
|
}
|
||||||
|
table.eventEmitter.emit( "deal" );
|
||||||
|
}
|
||||||
|
table.currentPlayer = getNextAvailablePlayer(table, table.startIndex, table.players.length);
|
||||||
|
if(typeof table.currentPlayer !== 'number' && table.game.roundName !== 'GameEnd'){
|
||||||
|
console.log('ALL IN GAME');
|
||||||
|
completeBoard(table);
|
||||||
|
return progress(table);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
table.eventEmitter.emit("turnStart");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function completeGame(table){
|
||||||
|
table.game.roundName = 'GameEnd';
|
||||||
|
table.game.bets.splice(0, table.game.bets.length);
|
||||||
|
//Evaluate each hand
|
||||||
|
for(j = 0; j < table.players.length; j += 1){
|
||||||
|
cards = table.players[j].cards.concat(table.game.board);
|
||||||
|
table.players[j].hand = new Hand(cards);
|
||||||
|
}
|
||||||
|
console.log('checkForWinner', table);
|
||||||
|
checkForWinner(table);
|
||||||
|
table.eventEmitter.emit('gameOver');
|
||||||
|
}
|
||||||
|
|
||||||
|
function completeBoard(table){
|
||||||
|
var i;
|
||||||
|
if(table.game.board.length == 0){
|
||||||
|
table.game.deck.pop();
|
||||||
|
for(i=0;i<3;i+=1){
|
||||||
|
table.game.board.push(table.game.deck.pop());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(table.game.board.length == 3){
|
||||||
|
table.game.deck.pop();
|
||||||
|
table.game.board.push(table.game.deck.pop());
|
||||||
|
}
|
||||||
|
if(table.game.board.length == 4){
|
||||||
|
table.game.deck.pop();
|
||||||
|
table.game.board.push(table.game.deck.pop());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMaxBet(bets){
|
||||||
|
var maxBet, i;
|
||||||
|
maxBet = 0;
|
||||||
|
for(i=0;i< bets.length;i+=1){
|
||||||
|
if(bets[i] > maxBet){
|
||||||
|
maxBet = bets[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return maxBet;
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkForEndOfGame(table){
|
||||||
|
var notFolded = [], allInPlayer = [], actionablePlayer = [], i;
|
||||||
|
for(i = 0; i < table.players.length;i+=1){
|
||||||
|
if(table.players[i].folded === false){
|
||||||
|
notFolded.push(i);
|
||||||
|
}
|
||||||
|
if(table.players[i].allIn === true){
|
||||||
|
allInPlayer.push(i);
|
||||||
|
}
|
||||||
|
if(table.players[i].folded === false && table.players[i].allIn === false){
|
||||||
|
actionablePlayer.push(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(allInPlayer.length > 1){
|
||||||
|
completeBoard(table);
|
||||||
|
}
|
||||||
|
return (notFolded.length === 1 || actionablePlayer.length === 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkForEndOfRound(table){
|
||||||
|
var endOfRound = true;
|
||||||
|
var nextPlayer = getNextAvailablePlayer(table, (table.currentPlayer + 1), table.players.length);
|
||||||
|
if(typeof nextPlayer === 'number'){
|
||||||
|
table.currentPlayer = nextPlayer;
|
||||||
|
endOfRound = false;
|
||||||
|
}
|
||||||
|
return endOfRound;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNextAvailablePlayer(table, playerIndex, len, ctr){
|
||||||
|
ctr = ctr || 0;
|
||||||
|
var maxBet = getMaxBet(table.game.bets);
|
||||||
|
var nextPlayer;
|
||||||
|
if(playerIndex === len){
|
||||||
|
playerIndex = 0;
|
||||||
|
}
|
||||||
|
if(table.players[playerIndex].folded === false && (table.players[playerIndex].talked === false || table.game.bets[playerIndex] !== maxBet) && table.players[playerIndex].allIn === false){
|
||||||
|
nextPlayer = playerIndex;
|
||||||
|
}
|
||||||
|
ctr += 1;
|
||||||
|
playerIndex += 1;
|
||||||
|
if(typeof nextPlayer !== 'number' && ctr !== len){
|
||||||
|
nextPlayer = getNextAvailablePlayer(table, playerIndex, len, ctr);
|
||||||
|
}
|
||||||
|
return nextPlayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkForAllInPlayer(table, winners){
|
||||||
|
var i, allInPlayer;
|
||||||
|
allInPlayer = [];
|
||||||
|
for(i=0;i< winners.length;i+=1){
|
||||||
|
if(table.players[winners[i]].allIn === true){
|
||||||
|
allInPlayer.push(winners[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return allInPlayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkForWinner(table){
|
||||||
|
var i, j, k, l, maxRank, notFolded, winners, part, prize, allInPlayer, minBets, roundEnd;
|
||||||
|
//Identify winner(s)
|
||||||
|
winners = [];
|
||||||
|
notFolded = [];
|
||||||
|
maxRank = 0.000;
|
||||||
|
for(k = 0; k < table.players.length; k += 1){
|
||||||
|
if(table.players[k].folded === false){
|
||||||
|
notFolded.push(k);
|
||||||
|
}
|
||||||
|
if(table.players[k].hand.rank === maxRank && table.players[k].folded === false){
|
||||||
|
winners.push(k);
|
||||||
|
}
|
||||||
|
if(table.players[k].hand.rank > maxRank && table.players[k].folded === false){
|
||||||
|
maxRank = table.players[k].hand.rank;
|
||||||
|
winners.splice(0, winners.length);
|
||||||
|
winners.push(k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// handle mid-round fold
|
||||||
|
if(winners.length === 0 && notFolded.length == 1){
|
||||||
|
console.log('mid round fold');
|
||||||
|
winners.push(notFolded[0]);
|
||||||
|
}
|
||||||
|
part = 0;
|
||||||
|
prize = 0;
|
||||||
|
console.log('roundBets', table.game.roundBets);
|
||||||
|
allInPlayer = checkForAllInPlayer(table, winners);
|
||||||
|
if(allInPlayer.length > 0){
|
||||||
|
minBets = table.game.roundBets[winners[0]];
|
||||||
|
for(j = 1; j < allInPlayer.length; j += 1){
|
||||||
|
if(table.game.roundBets[winners[j]] !== 0 && table.game.roundBets[winners[j]] < minBets){
|
||||||
|
minBets = table.game.roundBets[winners[j]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
part = parseInt(minBets, 10);
|
||||||
|
}else{
|
||||||
|
part = parseInt(table.game.roundBets[winners[0]], 10);
|
||||||
|
}
|
||||||
|
console.log('part', part);
|
||||||
|
for(l = 0; l < table.game.roundBets.length; l += 1){
|
||||||
|
// handle user leave
|
||||||
|
// console.log('more than 1 player', table.players.length > 1 && (table.players.length - table.playersToRemove.length > 1));
|
||||||
|
if(table.game.roundBets[l] > part){
|
||||||
|
prize += part;
|
||||||
|
table.game.roundBets[l] -= part;
|
||||||
|
}else{
|
||||||
|
prize += table.game.roundBets[l];
|
||||||
|
table.game.roundBets[l] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log('prize', prize);
|
||||||
|
console.log('winners', winners);
|
||||||
|
|
||||||
|
if(prize > 0){
|
||||||
|
var remainder = prize % winners.length;
|
||||||
|
var winnerHands = [];
|
||||||
|
var highestIndex;
|
||||||
|
var winnerPrize = Math.floor(prize / winners.length);
|
||||||
|
if(remainder !== 0){
|
||||||
|
console.log('chip remainder of '+remainder);
|
||||||
|
for(i=0;i<winners.length;++i){
|
||||||
|
winnerHands.push(table.players[winners[i]].cards);
|
||||||
|
}
|
||||||
|
highestIndex = getOddChipWinner(winnerHands);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i=0;i<winners.length;++i){
|
||||||
|
var winningPlayer = table.players[winners[i]];
|
||||||
|
if(i === highestIndex){
|
||||||
|
winnerPrize += remainder;
|
||||||
|
console.log('player '+winningPlayer.playerName+' gets the remaining '+remainder+' chips');
|
||||||
|
}
|
||||||
|
winningPlayer.chips += winnerPrize;
|
||||||
|
if(table.game.roundBets[winners[i]] === 0){
|
||||||
|
winningPlayer.folded = true;
|
||||||
|
table.gameWinners.push({
|
||||||
|
playerName: winningPlayer.playerName,
|
||||||
|
id: winningPlayer.id,
|
||||||
|
amount: winnerPrize,
|
||||||
|
hand: winningPlayer.hand,
|
||||||
|
chips: winningPlayer.chips
|
||||||
|
});
|
||||||
|
}
|
||||||
|
logger.debug('player '+table.players[winners[i]].playerName+'('+table.players[winners[i]].id+') wins '+winnerPrize+' chips');
|
||||||
|
if(i === highestIndex)
|
||||||
|
winnerPrize -= remainder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
roundEnd = true;
|
||||||
|
console.log('roundBets', table.game.roundBets);
|
||||||
|
for(l = 0; l < table.game.roundBets.length;l+=1){
|
||||||
|
if(table.game.roundBets[l] !== 0){
|
||||||
|
roundEnd = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(roundEnd === false){
|
||||||
|
checkForWinner(table);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkForBankrupt(table){
|
||||||
|
var i;
|
||||||
|
for(i=0;i<table.players.length;i+= 1){
|
||||||
|
if(table.players[i].chips === 0){
|
||||||
|
table.gameLosers.push(table.players[i]);
|
||||||
|
logger.debug('player '+table.players[i].playerName+'('+table.players[i].id+') went bankrupt');
|
||||||
|
// table.players.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOddChipWinner(hands){
|
||||||
|
var i, highestIndex, highestAmt = 0;
|
||||||
|
console.log('getOddChipWinner', hands);
|
||||||
|
for(i=0;i<hands.length;++i){
|
||||||
|
var hand = hands[i];
|
||||||
|
var rank1 = getCardRank(hand[0]);
|
||||||
|
var rank2 = getCardRank(hand[1]);
|
||||||
|
if(highestAmt < rank1){
|
||||||
|
highestAmt = rank1;
|
||||||
|
highestIndex = i;
|
||||||
|
}
|
||||||
|
if(highestAmt < rank2){
|
||||||
|
highestAmt = rank2;
|
||||||
|
highestIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log('odd chip owner result: '+highestIndex+' '+highestAmt);
|
||||||
|
return highestIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCardRank(card){
|
||||||
|
var rank = 0;
|
||||||
|
switch(card[0]){
|
||||||
|
case 'A': rank += 130; break;
|
||||||
|
case 'K': rank += 120; break;
|
||||||
|
case 'Q': rank += 110; break;
|
||||||
|
case 'J': rank += 100; break;
|
||||||
|
case 'T': rank += 90; break;
|
||||||
|
case '9': rank += 80; break;
|
||||||
|
case '8': rank += 70; break;
|
||||||
|
case '7': rank += 60; break;
|
||||||
|
case '6': rank += 50; break;
|
||||||
|
case '5': rank += 40; break;
|
||||||
|
case '4': rank += 30; break;
|
||||||
|
case '3': rank += 20; break;
|
||||||
|
case '2': rank += 10; break;
|
||||||
|
}
|
||||||
|
switch(card[1]){
|
||||||
|
case 'S': rank += 4; break;
|
||||||
|
case 'H': rank += 3; break;
|
||||||
|
case 'D': rank += 2; break;
|
||||||
|
case 'C': rank += 1; break;
|
||||||
|
}
|
||||||
|
return rank;
|
||||||
|
}
|
203
game-server/app/game/table.js
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
var logger = require('pomelo-logger').getLogger('game-log', __filename);
|
||||||
|
var events = require('events');
|
||||||
|
var Game = require('./game');
|
||||||
|
var Player = require('./player');
|
||||||
|
var GAME_SETTINGS = require('../../config/gameSettings.json');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Table object handles table logic while it is stored in memory.
|
||||||
|
*
|
||||||
|
* @param {number} smallBlind small blind
|
||||||
|
* @param {number} bigBlind big blind
|
||||||
|
* @param {number} minPlayers minimum number of players before game can be started
|
||||||
|
* @param {number} maxPlayers maximum number of players before game can be started
|
||||||
|
* @param {number} minBuyIn minimum buy in
|
||||||
|
* @param {number} maxBuyIn maximum buy in
|
||||||
|
* @param {string} gameMode type of game
|
||||||
|
* @param {object} table instance of a table
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
module.exports = Table = function(smallBlind, bigBlind, minPlayers, maxPlayers, minBuyIn, maxBuyIn, gameMode, table){
|
||||||
|
this.smallBlind = smallBlind;
|
||||||
|
this.bigBlind = bigBlind;
|
||||||
|
this.minPlayers = minPlayers;
|
||||||
|
this.maxPlayers = maxPlayers;
|
||||||
|
this.players = [];
|
||||||
|
this.dealer = 0; //Track the dealer position between games
|
||||||
|
this.minBuyIn = minBuyIn;
|
||||||
|
this.maxBuyIn = maxBuyIn;
|
||||||
|
this.previousPlayers = [];
|
||||||
|
this.playersToRemove = [];
|
||||||
|
this.playersToAdd = [];
|
||||||
|
this.eventEmitter = new events.EventEmitter();
|
||||||
|
this.turnBet = {};
|
||||||
|
this.gameWinners = [];
|
||||||
|
this.gameLosers = [];
|
||||||
|
this.actions = [];
|
||||||
|
this.members = [];
|
||||||
|
this.active = false;
|
||||||
|
this.gameMode = gameMode;
|
||||||
|
this.instance = table;
|
||||||
|
//Validate acceptable value ranges.
|
||||||
|
var err;
|
||||||
|
if(minPlayers < 2){ //require at least two players to start a game.
|
||||||
|
err = new Error(101, 'Parameter [minPlayers] must be a postive integer of a minimum value of 2.');
|
||||||
|
}else if(maxPlayers > 10){ //hard limit of 10 players at a table.
|
||||||
|
err = new Error(102, 'Parameter [maxPlayers] must be a positive integer less than or equal to 10.');
|
||||||
|
}else if(minPlayers > maxPlayers){ //Without this we can never start a game!
|
||||||
|
err = new Error(103, 'Parameter [minPlayers] must be less than or equal to [maxPlayers].');
|
||||||
|
}
|
||||||
|
if(err){
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Table.prototype.initNewGame = function(){
|
||||||
|
var i;
|
||||||
|
this.instance.state = 'JOIN';
|
||||||
|
this.dealer += 1;
|
||||||
|
if(this.dealer >= this.players.length){
|
||||||
|
this.dealer = 0;
|
||||||
|
}
|
||||||
|
delete this.game;
|
||||||
|
this.previousPlayers = [];
|
||||||
|
// add existing players and remove players who left or are bankrupt
|
||||||
|
for(i=0;i<this.players.length;++i){
|
||||||
|
this.previousPlayers.push(this.players[i]);
|
||||||
|
if(this.playersToRemove.indexOf(i) === -1){
|
||||||
|
this.AddPlayer(this.players[i].playerName, this.players[i].chips, this.players[i].id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.players = [];
|
||||||
|
this.playersToRemove = [];
|
||||||
|
this.actions = [];
|
||||||
|
this.eventEmitter.emit('gameInit');
|
||||||
|
};
|
||||||
|
|
||||||
|
Table.prototype.StartGame = function(){
|
||||||
|
//If there is no current game and we have enough players, start a new game.
|
||||||
|
this.instance.state = 'IN_PROGRESS';
|
||||||
|
this.active = true;
|
||||||
|
if(!this.game){
|
||||||
|
this.game = new Game(this.smallBlind, this.bigBlind);
|
||||||
|
this.NewRound();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Table.prototype.AddPlayer = function(playerName, chips, uid){
|
||||||
|
if(chips >= this.minBuyIn && chips <= this.maxBuyIn){
|
||||||
|
var player = new Player(playerName, chips, uid, this);
|
||||||
|
this.playersToAdd.push(player);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Table.prototype.removePlayer = function(pid){
|
||||||
|
for(var i in this.players ){
|
||||||
|
if(this.players[i].id === pid){
|
||||||
|
this.playersToRemove.push( parseInt(i) );
|
||||||
|
this.players[i].Fold();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(var i in this.playersToAdd ){
|
||||||
|
if(this.playersToAdd[i].id === pid){
|
||||||
|
this.playersToAdd.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(var i in this.members ){
|
||||||
|
if(this.members[i].id === pid){
|
||||||
|
this.members.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(var i in this.previousPlayers){
|
||||||
|
if(this.previousPlayers[i].id === pid){
|
||||||
|
this.previousPlayers.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.eventEmitter.emit("playerLeft");
|
||||||
|
}
|
||||||
|
Table.prototype.NewRound = function(){
|
||||||
|
var removeIndex = 0;
|
||||||
|
for(var i in this.playersToAdd){
|
||||||
|
// if(removeIndex < this.playersToRemove.length){
|
||||||
|
// var index = this.playersToRemove[removeIndex];
|
||||||
|
// this.players[index] = this.playersToAdd[i];
|
||||||
|
// removeIndex += 1;
|
||||||
|
// }else{
|
||||||
|
// this.players.push(this.playersToAdd[i]);
|
||||||
|
// }
|
||||||
|
this.players.push(this.playersToAdd[i]);
|
||||||
|
}
|
||||||
|
this.playersToRemove = [];
|
||||||
|
this.playersToAdd = [];
|
||||||
|
this.gameWinners = [];
|
||||||
|
this.gameLosers = [];
|
||||||
|
|
||||||
|
|
||||||
|
var i, smallBlind, bigBlind;
|
||||||
|
//Deal 2 cards to each player
|
||||||
|
for(i=0;i< this.players.length;i+=1){
|
||||||
|
this.players[i].cards.push(this.game.deck.pop());
|
||||||
|
this.players[i].cards.push(this.game.deck.pop());
|
||||||
|
this.game.bets[i] = 0;
|
||||||
|
this.game.roundBets[i] = 0;
|
||||||
|
}
|
||||||
|
//Identify Small and Big Blind player indexes
|
||||||
|
smallBlind = this.dealer + 1;
|
||||||
|
if(smallBlind >= this.players.length){
|
||||||
|
smallBlind = 0;
|
||||||
|
}
|
||||||
|
bigBlind = smallBlind + 1;
|
||||||
|
if(bigBlind >= this.players.length){
|
||||||
|
bigBlind = 0;
|
||||||
|
}
|
||||||
|
this.currentPlayer = bigBlind + 1;
|
||||||
|
if(this.currentPlayer >= this.players.length){
|
||||||
|
this.currentPlayer = 0;
|
||||||
|
}
|
||||||
|
this.startIndex = this.currentPlayer;
|
||||||
|
//Force Blind Bets
|
||||||
|
this.players[smallBlind].chips -= this.smallBlind;
|
||||||
|
this.players[bigBlind].chips -= this.bigBlind;
|
||||||
|
this.game.bets[smallBlind] = this.smallBlind;
|
||||||
|
this.game.bets[bigBlind] = this.bigBlind;
|
||||||
|
this.game.blinds = [smallBlind, bigBlind];
|
||||||
|
|
||||||
|
this.eventEmitter.emit("newRound");
|
||||||
|
};
|
||||||
|
|
||||||
|
Table.prototype.startTimer = function(){
|
||||||
|
var me = this;
|
||||||
|
me.stopTimer();
|
||||||
|
me._countdown = setTimeout(function(){
|
||||||
|
if(!me.active){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log('timer ended. executing move.');
|
||||||
|
if(me.game.bets[me.currentPlayer] < getMaxBet(me.game.bets)){
|
||||||
|
me.players[me.currentPlayer].Fold();
|
||||||
|
}else{
|
||||||
|
me.players[me.currentPlayer].Call();
|
||||||
|
}
|
||||||
|
me.instance.tableService.handleGameState(me.instance.id, function(e){
|
||||||
|
if(e){
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, (GAME_SETTINGS.gameMode[this.gameMode].timeout * 1000));
|
||||||
|
};
|
||||||
|
|
||||||
|
Table.prototype.stopTimer = function(){
|
||||||
|
if(this._countdown){
|
||||||
|
clearTimeout(this._countdown);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function getMaxBet(bets){
|
||||||
|
var maxBet, i;
|
||||||
|
maxBet = 0;
|
||||||
|
for(i=0;i< bets.length;i+=1){
|
||||||
|
if(bets[i] > maxBet){
|
||||||
|
maxBet = bets[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return maxBet;
|
||||||
|
}
|
42
game-server/app/module/timeReport.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
module.exports = function(opts) {
|
||||||
|
return new Module(opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
var testMsg = 'a default message';
|
||||||
|
|
||||||
|
var moduleId = "timeReport";
|
||||||
|
module.exports.moduleId = moduleId;
|
||||||
|
|
||||||
|
var Module = function(opts) {
|
||||||
|
this.app = opts.app;
|
||||||
|
this.type = opts.type || 'pull';
|
||||||
|
this.interval = opts.interval || 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
Module.prototype.monitorHandler = function(agent, msg, cb) {
|
||||||
|
console.log(this.app.getServerId() + '' + msg);
|
||||||
|
var serverId = agent.id;
|
||||||
|
var time = new Date(). toString();
|
||||||
|
|
||||||
|
agent.notify(moduleId, {serverId: serverId, time: time});
|
||||||
|
}
|
||||||
|
|
||||||
|
Module.prototype.masterHandler = function(agent, msg) {
|
||||||
|
if(! msg) {
|
||||||
|
agent.notifyAll(moduleId, testMsg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(msg);
|
||||||
|
var timeData = agent.get(moduleId);
|
||||||
|
if(! timeData) {
|
||||||
|
timeData = {};
|
||||||
|
agent.set(moduleId, timeData);
|
||||||
|
}
|
||||||
|
timeData[msg.serverId] = msg.time;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Module.prototype.clientHandler = function(agent, msg, cb) {
|
||||||
|
cb(null, agent.get(moduleId));
|
||||||
|
}
|
103
game-server/app/persistence/tables.js
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
var uuid = require('node-uuid');
|
||||||
|
var util = require('util');
|
||||||
|
var fs = require('fs');
|
||||||
|
|
||||||
|
var TableStore = module.exports = {
|
||||||
|
store : './localstore/tables.json',
|
||||||
|
entity : 'tables',
|
||||||
|
create : function(obj, cb){
|
||||||
|
var me = this;
|
||||||
|
me.persist({
|
||||||
|
id : obj.id || uuid.v1(),
|
||||||
|
tid : obj.tid,
|
||||||
|
smallBlind : obj.smallBlind,
|
||||||
|
bigBlind : obj.bigBlind,
|
||||||
|
minBuyIn : obj.minBuyIn,
|
||||||
|
maxBuyIn : obj.maxBuyIn,
|
||||||
|
minPlayers : obj.minPlayers,
|
||||||
|
maxPlayers : obj.maxPlayers,
|
||||||
|
gameMode : obj.gameMode,
|
||||||
|
board : obj.board,
|
||||||
|
creator : obj.creator,
|
||||||
|
players : obj.players || [],
|
||||||
|
actions : obj.actions || [],
|
||||||
|
gameWinners : obj.gameWinners || [],
|
||||||
|
created : Date.now()
|
||||||
|
}, function(e, table){
|
||||||
|
if(e){
|
||||||
|
cb(e);
|
||||||
|
}else{
|
||||||
|
// console.log('TableStore: table created - ', table);
|
||||||
|
cb(null, table);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
set : function(obj, cb){
|
||||||
|
var me = this;
|
||||||
|
me.persist(obj, function(e, table){
|
||||||
|
if(e){
|
||||||
|
cb(e);
|
||||||
|
}else{
|
||||||
|
// console.log('TableStore: table modified - ', table);
|
||||||
|
cb(null, table);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getByAttr : function(key, val, cb){
|
||||||
|
var matches = [], matched;
|
||||||
|
this.retrieve(function(e, entities){
|
||||||
|
if(e){
|
||||||
|
return cb(e);
|
||||||
|
}
|
||||||
|
if(key == '*' && val == '*'){
|
||||||
|
cb(null, entities);
|
||||||
|
}else{
|
||||||
|
for(var entity in entities){
|
||||||
|
if(util.isArray(key)){
|
||||||
|
matched = true;
|
||||||
|
for(var i=key.length;i--;){
|
||||||
|
if(entities[entity][key[i]] != val[i]){
|
||||||
|
matched = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(matched){
|
||||||
|
matches.push(entities[entity]);
|
||||||
|
}
|
||||||
|
}else if(entities[entity][key] == val){
|
||||||
|
matches.push(entities[entity]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cb(null, matches.length == 0 ? null : (matches.length == 1 ? matches[0] : matches));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
persist : function(row, cb){
|
||||||
|
var me = this;
|
||||||
|
me.retrieve(function(e, entities){
|
||||||
|
entities[row.id] = entities[row.id] || {};
|
||||||
|
for(var key in row){
|
||||||
|
entities[row.id][key] = row[key];
|
||||||
|
}
|
||||||
|
fs.writeFile(me.store, JSON.stringify(entities, undefined, 4), function(e2){
|
||||||
|
if(e2){
|
||||||
|
return cb('error writing file: ' + e2);
|
||||||
|
}else{
|
||||||
|
cb(null, entities[row.id]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
retrieve : function(cb){
|
||||||
|
fs.readFile(this.store, 'utf8', function(e, content){
|
||||||
|
if(e){
|
||||||
|
return cb('error loading file: ' + e);
|
||||||
|
}
|
||||||
|
if(content.trim().length == 0){
|
||||||
|
content = {};
|
||||||
|
}else{
|
||||||
|
content = JSON.parse(content);
|
||||||
|
}
|
||||||
|
cb(null, content);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
132
game-server/app/persistence/users.js
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
var uuid = require('node-uuid');
|
||||||
|
var util = require('util');
|
||||||
|
var fs = require('fs');
|
||||||
|
|
||||||
|
var UserStore = module.exports = {
|
||||||
|
store : './localstore/users.json',
|
||||||
|
entity : 'users',
|
||||||
|
create : function(obj, callback){
|
||||||
|
var me = this;
|
||||||
|
me.getByAttr('username', obj.username, false, function(e, existingUser){
|
||||||
|
if(existingUser){
|
||||||
|
callback('user-exists', existingUser);
|
||||||
|
}else{
|
||||||
|
var id = uuid.v1();
|
||||||
|
me.persist({
|
||||||
|
id : id,
|
||||||
|
username : obj.username,
|
||||||
|
password : obj.password,
|
||||||
|
email : obj.email,
|
||||||
|
chips : obj.chips,
|
||||||
|
wins : 0,
|
||||||
|
largestWin : 0,
|
||||||
|
friends : [],
|
||||||
|
created : Date.now()
|
||||||
|
}, function(e, user){
|
||||||
|
if(e){
|
||||||
|
callback(e);
|
||||||
|
}else{
|
||||||
|
callback(null, user);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
set : function(obj, cb){
|
||||||
|
var me = this;
|
||||||
|
me.persist(obj, function(e, entity){
|
||||||
|
if(e){
|
||||||
|
cb(e);
|
||||||
|
}else{
|
||||||
|
cb(null, safetyFilter(entity));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getByAttr : function(key, val, opts, cb){
|
||||||
|
var matches = [], matched;
|
||||||
|
opts = opts || {};
|
||||||
|
this.retrieve(function(e, entities){
|
||||||
|
if(e){
|
||||||
|
return cb(e, []);
|
||||||
|
}
|
||||||
|
for(var entity in entities){
|
||||||
|
if(util.isArray(key)){
|
||||||
|
matched = true;
|
||||||
|
for(var i=key.length;i--;){
|
||||||
|
if(entities[entity][key[i]] != val[i]){
|
||||||
|
matched = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(matched){
|
||||||
|
matches.push(opts.getFullEntity ? entities[entity] : safetyFilter(entities[entity]));
|
||||||
|
}
|
||||||
|
}else if(entities[entity][key] == val || (key == '*' && val == '*')){
|
||||||
|
matches.push(opts.getFullEntity ? entities[entity] : safetyFilter(entities[entity]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
matches = opts.getArray ? matches : (matches.length == 0 ? null : (matches.length == 1 ? matches[0] : matches));
|
||||||
|
cb(null, matches);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getByIds : function(ary, cb){
|
||||||
|
var matches = [];
|
||||||
|
this.retrieve(function(e, entities){
|
||||||
|
if(e){
|
||||||
|
return cb(e, []);
|
||||||
|
}
|
||||||
|
for(var entity in entities){
|
||||||
|
if(ary.indexOf(entities[entity].id) != -1){
|
||||||
|
matches.push(safetyFilter(entities[entity]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cb(null, matches);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
persist : function(row, cb){
|
||||||
|
var me = this;
|
||||||
|
me.retrieve(function(e, entities){
|
||||||
|
entities[row.id] = entities[row.id] || {};
|
||||||
|
for(var key in row){
|
||||||
|
entities[row.id][key] = row[key];
|
||||||
|
}
|
||||||
|
fs.writeFile(me.store, JSON.stringify(entities, undefined, 4), function(e2){
|
||||||
|
if(e2){
|
||||||
|
return cb('error writing file: ' + e2);
|
||||||
|
}else{
|
||||||
|
cb(null, entities[row.id]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
retrieve : function(cb){
|
||||||
|
fs.readFile(this.store, 'utf8', function(e, content){
|
||||||
|
if(e){
|
||||||
|
return cb('error loading file: ' + e);
|
||||||
|
}
|
||||||
|
if(content.trim().length == 0){
|
||||||
|
content = {};
|
||||||
|
}else{
|
||||||
|
try{
|
||||||
|
content = JSON.parse(content);
|
||||||
|
}catch(e){
|
||||||
|
console.log('parseerror');
|
||||||
|
content = content.slice(0, - 1);
|
||||||
|
content = JSON.parse(content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cb(null, content);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function safetyFilter(obj){
|
||||||
|
return {
|
||||||
|
id : obj.id,
|
||||||
|
username : obj.username,
|
||||||
|
email : obj.email,
|
||||||
|
chips : obj.chips,
|
||||||
|
wins : obj.wins,
|
||||||
|
largestWin : obj.largestWin,
|
||||||
|
created : obj.created
|
||||||
|
}
|
||||||
|
}
|
90
game-server/app/servers/chat/handler/chatHandler.js
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
var UserStore = require('../../../persistence/users');
|
||||||
|
var dispatcher = require('../../../util/dispatcher');
|
||||||
|
|
||||||
|
module.exports = function(app){
|
||||||
|
return new Handler(app, app.get('chatService'));
|
||||||
|
};
|
||||||
|
var Handler = function(app, chatService){
|
||||||
|
this.app = app;
|
||||||
|
this.chatService = chatService;
|
||||||
|
};
|
||||||
|
var handler = Handler.prototype;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send messages to users in the channel
|
||||||
|
*
|
||||||
|
* @param {Object} msg message from client
|
||||||
|
* @param {Object} session
|
||||||
|
* @param {Function} next next stemp callback
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
handler.sendMessage = function(msg, session, next){
|
||||||
|
var me = this;
|
||||||
|
var tid = session.get('tid');
|
||||||
|
var channelService = this.app.get('channelService');
|
||||||
|
UserStore.getByAttr('id', session.uid, false, function(e, user){
|
||||||
|
if(!user){
|
||||||
|
next(null, {
|
||||||
|
code : 500,
|
||||||
|
error : 'user-not-exist'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// target is all users
|
||||||
|
if(msg.target == 'table'){
|
||||||
|
var channel = channelService.getChannel(tid, true);
|
||||||
|
msg.target = '*';
|
||||||
|
channel.pushMessage({
|
||||||
|
route : 'onChat',
|
||||||
|
msg : msg.content,
|
||||||
|
username : user.username,
|
||||||
|
target : msg.target
|
||||||
|
});
|
||||||
|
next(null, {
|
||||||
|
code : 200,
|
||||||
|
route : msg.route
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
// target is specific user
|
||||||
|
me.chatService.pushByPlayerName(msg.target, {
|
||||||
|
username : user.username,
|
||||||
|
msg : msg.content
|
||||||
|
}, function(e){
|
||||||
|
if(e){
|
||||||
|
return next(null, {
|
||||||
|
code : 500,
|
||||||
|
error : e
|
||||||
|
});
|
||||||
|
}
|
||||||
|
next(null, {
|
||||||
|
code : 200,
|
||||||
|
route : msg.route
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get friend list
|
||||||
|
*
|
||||||
|
* @param {Object} msg game parameters from client
|
||||||
|
* @param {Object} session
|
||||||
|
* @param {Function} next next step callback
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
handler.getFriends = function(msg, session, next){
|
||||||
|
this.chatService.getFriendList(session.uid, function(e, friends){
|
||||||
|
if(e){
|
||||||
|
return next(null, {
|
||||||
|
code : 500,
|
||||||
|
error : e
|
||||||
|
});
|
||||||
|
}
|
||||||
|
next(null, {
|
||||||
|
code : 200,
|
||||||
|
route : msg.route,
|
||||||
|
friends : friends
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
48
game-server/app/servers/chat/remote/chatRemote.js
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
module.exports = function(app){
|
||||||
|
return new ChatRemote(app, app.get('chatService'));
|
||||||
|
};
|
||||||
|
|
||||||
|
var ChatRemote = function(app, chatService){
|
||||||
|
this.app = app;
|
||||||
|
this.chatService = chatService;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add player into channel
|
||||||
|
*/
|
||||||
|
ChatRemote.prototype.addToChannel = function(uid, cid, cb){
|
||||||
|
this.chatService.addToChannel(uid, cid, cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add player record
|
||||||
|
*/
|
||||||
|
ChatRemote.prototype.add = function(uid, cid, cb){
|
||||||
|
this.chatService.add(uid, cid, cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get members in a channel
|
||||||
|
*/
|
||||||
|
ChatRemote.prototype.getMembers = function(cid, cb){
|
||||||
|
this.chatService.getMembers(cid, cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* leave Channel
|
||||||
|
* uid
|
||||||
|
* cid
|
||||||
|
*/
|
||||||
|
ChatRemote.prototype.leave = function(uid, cid, cb){
|
||||||
|
this.chatService.leave(uid, cid);
|
||||||
|
cb();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kick out user
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
ChatRemote.prototype.disconnect = function(uid, cb){
|
||||||
|
this.chatService.disconnect(uid);
|
||||||
|
cb();
|
||||||
|
};
|
101
game-server/app/servers/connector/handler/entryHandler.js
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
var logger = require('pomelo-logger').getLogger('con-log', __filename);
|
||||||
|
|
||||||
|
module.exports = function(app){
|
||||||
|
return new Handler(app);
|
||||||
|
};
|
||||||
|
var Handler = function(app){
|
||||||
|
this.app = app;
|
||||||
|
};
|
||||||
|
var handler = Handler.prototype;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register user.
|
||||||
|
*
|
||||||
|
* @param {Object} msg request message
|
||||||
|
* @param {Object} session current session object
|
||||||
|
* @param {Function} next next step callback
|
||||||
|
*/
|
||||||
|
handler.register = function(msg, session, next){
|
||||||
|
this.app.rpc.game.authRemote.register(session, msg, function(e, user){
|
||||||
|
if(e){
|
||||||
|
next(null, {
|
||||||
|
code : 500,
|
||||||
|
error : e
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
next(null, {
|
||||||
|
code : 201
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connect to the server
|
||||||
|
*
|
||||||
|
* @param {Object} msg request message
|
||||||
|
* @param {Object} session current session object
|
||||||
|
* @param {Function} next next step callback
|
||||||
|
*/
|
||||||
|
handler.connect = function(msg, session, next){
|
||||||
|
var me = this;
|
||||||
|
var sessionService = me.app.get('sessionService');
|
||||||
|
me.app.rpc.game.authRemote.auth(session, msg, function(e, user, token){
|
||||||
|
if(!user){
|
||||||
|
next(null, {
|
||||||
|
code : 401,
|
||||||
|
error : e
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// duplicate log in
|
||||||
|
if(!! sessionService.getByUid(user.id)){
|
||||||
|
return next(null, {
|
||||||
|
code : 500,
|
||||||
|
error : 'duplicate-session'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
session.bind(user.id, function(e){
|
||||||
|
if(e){
|
||||||
|
console.error('error-binding-user', e);
|
||||||
|
}
|
||||||
|
session.set('username', user.username);
|
||||||
|
session.on('closed', onUserLeave.bind(null, me.app));
|
||||||
|
session.pushAll(function(e){
|
||||||
|
if(e){
|
||||||
|
console.error('set username for session service failed! error is : %j', e.stack);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// add user to chat service
|
||||||
|
me.app.rpc.chat.chatRemote.add(session, session.uid, function(e){
|
||||||
|
if(e){
|
||||||
|
return next(null, {
|
||||||
|
code : 500,
|
||||||
|
error : e
|
||||||
|
});
|
||||||
|
}
|
||||||
|
next(null, {
|
||||||
|
code : 200,
|
||||||
|
token : token,
|
||||||
|
user : user
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* User log out handler
|
||||||
|
*
|
||||||
|
* @param {Object} app current application
|
||||||
|
* @param {Object} session current session object
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
var onUserLeave = function(app, session){
|
||||||
|
if(!session || !session.uid || !session.get('tid')){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(session.get('tid')){
|
||||||
|
app.rpc.chat.chatRemote.disconnect(session, session.uid, function(){});
|
||||||
|
app.rpc.game.tableRemote.removeMember(session, session.uid, app.get('serverId'), session.get('tid'), function(){});
|
||||||
|
}
|
||||||
|
};
|
22
game-server/app/servers/game/filter/abuseFilter.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
module.exports = function(){
|
||||||
|
return new Filter();
|
||||||
|
};
|
||||||
|
|
||||||
|
var Filter = function(){};
|
||||||
|
|
||||||
|
Filter.prototype.before = function (msg, session, next){
|
||||||
|
if(msg.content && msg.content.indexOf ('fuck') !== -1){
|
||||||
|
session.__abuse__ = true;
|
||||||
|
msg.content = msg.content.replace ('fuck', '****');
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
|
||||||
|
Filter.prototype.after = function (err, msg, session, resp, next){
|
||||||
|
if(session.__abuse__){
|
||||||
|
var user_info = session.uid.split ('*');
|
||||||
|
console.log ('abuse:' + user_info[0] + "at room" + user_info[1]);
|
||||||
|
}
|
||||||
|
next(err);
|
||||||
|
};
|
245
game-server/app/servers/game/handler/tableHandler.js
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
var logger = require('pomelo-logger').getLogger('game-log', __filename);
|
||||||
|
|
||||||
|
module.exports = function(app){
|
||||||
|
return new Handler(app);
|
||||||
|
};
|
||||||
|
var Handler = function(app){
|
||||||
|
this.app = app;
|
||||||
|
};
|
||||||
|
var handler = Handler.prototype;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get tables
|
||||||
|
*
|
||||||
|
* @param {Object} msg game parameters from client
|
||||||
|
* @param {Object} session
|
||||||
|
* @param {Function} next next step callback
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
handler.getTables = function(msg, session, next){
|
||||||
|
var tableService = this.app.get('tableService');
|
||||||
|
next(null, {
|
||||||
|
code : 200,
|
||||||
|
route : msg.route,
|
||||||
|
tables : tableService.getTables()
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create table
|
||||||
|
*
|
||||||
|
* @param {Object} msg game parameters from client
|
||||||
|
* @param {Object} session
|
||||||
|
* @param {Function} next next step callback
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
handler.createTable = function(msg, session, next){
|
||||||
|
if(session.get('tid')){
|
||||||
|
next(null, {
|
||||||
|
code : 500,
|
||||||
|
error : 'already-in-table'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.app.get('tableService').createTable(session.uid, msg, function(e){
|
||||||
|
if(e){
|
||||||
|
next(null, {
|
||||||
|
code : 500,
|
||||||
|
error : e
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
next(null, {
|
||||||
|
code : 200,
|
||||||
|
route : msg.route
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Join table
|
||||||
|
*
|
||||||
|
* @param {Object} msg table parameters from client
|
||||||
|
* @param {Object} session
|
||||||
|
* @param {Function} next next step callback
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
handler.joinTable = function(msg, session, next){
|
||||||
|
var me = this;
|
||||||
|
var tableService = this.app.get('tableService');
|
||||||
|
var table = tableService.getTable(msg.tid);
|
||||||
|
if(!msg.tid || !table){
|
||||||
|
next(null, {
|
||||||
|
code : 500,
|
||||||
|
error : 'invalid-table'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
session.set('tid', msg.tid);
|
||||||
|
session.pushAll(function(err){
|
||||||
|
if(err){
|
||||||
|
logger.error('set tid for session service failed! error is : %j', err.stack);
|
||||||
|
next(null, {
|
||||||
|
code : 500,
|
||||||
|
error : 'server-error'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var tid = session.get('tid');
|
||||||
|
me.app.rpc.chat.chatRemote.addToChannel(session, session.uid, tid, function(e){
|
||||||
|
if(e){
|
||||||
|
next(null, {
|
||||||
|
code : 500,
|
||||||
|
error : e
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tableService.addMember(tid, session.uid, function(e){
|
||||||
|
if(e){
|
||||||
|
next(null, {
|
||||||
|
code : 500,
|
||||||
|
error : e
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
next(null, {
|
||||||
|
code : 200,
|
||||||
|
route : msg.route
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Leave table
|
||||||
|
*
|
||||||
|
* @param {Object} msg table parameters from client
|
||||||
|
* @param {Object} session
|
||||||
|
* @param {Function} next next step callback
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
handler.leaveTable = function(msg, session, next){
|
||||||
|
var me = this;
|
||||||
|
var tid = session.get('tid');
|
||||||
|
if(!tid){
|
||||||
|
return next(null, {
|
||||||
|
code : 500,
|
||||||
|
error : 'not-table-member'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
me.app.rpc.chat.chatRemote.leave(session, session.uid, tid, function(e){});
|
||||||
|
session.set('tid', undefined);
|
||||||
|
session.pushAll(function(e){
|
||||||
|
if(e){
|
||||||
|
logger.error('unset tid for session service failed! error is : %j', e.stack);
|
||||||
|
return next(null, {
|
||||||
|
code : 500,
|
||||||
|
error : 'server-error'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
me.app.get('tableService').removeMember(tid, session.uid, function(e){
|
||||||
|
if(e){
|
||||||
|
next(null, {
|
||||||
|
code : 500,
|
||||||
|
error : e
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
next(null, {
|
||||||
|
code : 200,
|
||||||
|
route : msg.route
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Join game
|
||||||
|
*
|
||||||
|
* @param {Object} msg game parameters from client
|
||||||
|
* @param {Object} session
|
||||||
|
* @param {Function} next next step callback
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
handler.joinGame = function(msg, session, next){
|
||||||
|
this.app.get('tableService').addPlayer(session.get('tid'), session.uid, msg.buyIn, function(e){
|
||||||
|
if(e){
|
||||||
|
next(null, {
|
||||||
|
code : 500,
|
||||||
|
error : e
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
next(null, {
|
||||||
|
code : 200,
|
||||||
|
route : msg.route
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start game
|
||||||
|
*
|
||||||
|
* @param {Object} msg game parameters from client
|
||||||
|
* @param {Object} session
|
||||||
|
* @param {Function} next next step callback
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
handler.startGame = function(msg, session, next){
|
||||||
|
this.app.get('tableService').startGame(session.get('tid'), function(e){
|
||||||
|
if(e){
|
||||||
|
next(null, {
|
||||||
|
code : 500,
|
||||||
|
error : e
|
||||||
|
});
|
||||||
|
}
|
||||||
|
next(null, {
|
||||||
|
code : 200,
|
||||||
|
route : msg.route
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform an action on your turn
|
||||||
|
*
|
||||||
|
* @param {Object} msg game parameters from client
|
||||||
|
* @param {Object} session
|
||||||
|
* @param {Function} next next step callback
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
handler.execute = function(msg, session, next){
|
||||||
|
this.app.get('tableService').performAction(session.get('tid'), session.uid, msg, function(e){
|
||||||
|
if(e){
|
||||||
|
next(null, {
|
||||||
|
code : 500,
|
||||||
|
error : e
|
||||||
|
});
|
||||||
|
}
|
||||||
|
next(null, {
|
||||||
|
code : 200,
|
||||||
|
route : msg.route
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform an action on your turn
|
||||||
|
*
|
||||||
|
* @param {Object} msg game parameters from client
|
||||||
|
* @param {Object} session
|
||||||
|
* @param {Function} next next step callback
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
handler.removeBots = function(msg, session, next){
|
||||||
|
var botService = this.app.get('botService');
|
||||||
|
botService.removeAllBots(session.get('tid'), true);
|
||||||
|
next(null, {
|
||||||
|
code : 200,
|
||||||
|
route : msg.route
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
188
game-server/app/servers/game/handler/userHandler.js
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
var logger = require('pomelo-logger').getLogger('game-log', __filename);
|
||||||
|
var UserStore = require('../../../persistence/users');
|
||||||
|
|
||||||
|
module.exports = function(app){
|
||||||
|
return new Handler(app);
|
||||||
|
};
|
||||||
|
var Handler = function(app){
|
||||||
|
this.app = app;
|
||||||
|
};
|
||||||
|
var handler = Handler.prototype;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get users matching the criteria
|
||||||
|
*
|
||||||
|
* @param {Object} msg game parameters from client
|
||||||
|
* @param {Object} session
|
||||||
|
* @param {Function} next next step callback
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
handler.getUsers = function(msg, session, next){
|
||||||
|
if(!msg.name && !msg.val && !session.uid){
|
||||||
|
return next(null, {
|
||||||
|
code : 500,
|
||||||
|
error : 'invalid-input'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
var searchId = (msg.name == 'id' || msg.name == 'username' || msg.name == 'email') ? msg.name : 'id';
|
||||||
|
var searchVal = typeof msg.val === 'string' ? msg.val : session.uid;
|
||||||
|
UserStore.getByAttr(searchId, searchVal, {
|
||||||
|
getArray : true
|
||||||
|
}, function(e, matches){
|
||||||
|
if(e){
|
||||||
|
return next(null, {
|
||||||
|
code : 500,
|
||||||
|
error : e
|
||||||
|
});
|
||||||
|
}
|
||||||
|
next(null, {
|
||||||
|
code : 200,
|
||||||
|
route : msg.route,
|
||||||
|
matches : matches
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update user profile
|
||||||
|
*
|
||||||
|
* @param {Object} msg game parameters from client
|
||||||
|
* @param {Object} session
|
||||||
|
* @param {Function} next next step callback
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
handler.setProfile = function(msg, session, next){
|
||||||
|
if(!session.uid){
|
||||||
|
return next(null, {
|
||||||
|
code : 500,
|
||||||
|
error : 'invalid-session'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
UserStore.getByAttr('id', session.uid, false, function(e, user){
|
||||||
|
if(e){
|
||||||
|
return next(null, {
|
||||||
|
code : 500,
|
||||||
|
error : e
|
||||||
|
});
|
||||||
|
}
|
||||||
|
var userObj = {
|
||||||
|
id : user.id
|
||||||
|
};
|
||||||
|
if(msg.email){
|
||||||
|
userObj.email = msg.email.trim();
|
||||||
|
}
|
||||||
|
UserStore.set(userObj, function(e, updatedUser){
|
||||||
|
if(e){
|
||||||
|
return next(null, {
|
||||||
|
code : 500,
|
||||||
|
error : e
|
||||||
|
});
|
||||||
|
}
|
||||||
|
next(null, {
|
||||||
|
code : 200,
|
||||||
|
route : msg.route
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update user password
|
||||||
|
*
|
||||||
|
* @param {Object} msg game parameters from client
|
||||||
|
* @param {Object} session
|
||||||
|
* @param {Function} next next step callback
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
handler.setPassword = function(msg, session, next){
|
||||||
|
if(!session.uid || !msg.oldpassword || !msg.password){
|
||||||
|
return next(null, {
|
||||||
|
code : 500,
|
||||||
|
error : 'invalid-input'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
UserStore.getByAttr(['id', 'password'], [session.uid, msg.oldpassword], false, function(e, user){
|
||||||
|
if(e){
|
||||||
|
return next(null, {
|
||||||
|
code : 500,
|
||||||
|
error : e
|
||||||
|
});
|
||||||
|
}
|
||||||
|
var userObj = {
|
||||||
|
id : user.id,
|
||||||
|
password : msg.password.trim()
|
||||||
|
};
|
||||||
|
UserStore.set(userObj, function(e, updatedUser){
|
||||||
|
if(e){
|
||||||
|
return next(null, {
|
||||||
|
code : 500,
|
||||||
|
error : e
|
||||||
|
});
|
||||||
|
}
|
||||||
|
next(null, {
|
||||||
|
code : 200,
|
||||||
|
route : msg.route
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a friend to friend list
|
||||||
|
*
|
||||||
|
* @param {Object} msg game parameters from client
|
||||||
|
* @param {Object} session
|
||||||
|
* @param {Function} next next step callback
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
handler.addFriend = function(msg, session, next){
|
||||||
|
if(!session.uid){
|
||||||
|
return next(null, {
|
||||||
|
code : 500,
|
||||||
|
error : 'invalid-session'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if(!msg.friend){
|
||||||
|
return next(null, {
|
||||||
|
code : 200,
|
||||||
|
route : msg.route
|
||||||
|
});
|
||||||
|
}
|
||||||
|
UserStore.getByAttr('id', session.uid, {
|
||||||
|
getFullEntity : true
|
||||||
|
}, function(e, user){
|
||||||
|
if(e){
|
||||||
|
return next(null, {
|
||||||
|
code : 500,
|
||||||
|
error : e
|
||||||
|
});
|
||||||
|
}
|
||||||
|
UserStore.getByAttr('id', msg.friend, false, function(e, friend){
|
||||||
|
if(e){
|
||||||
|
return next(null, {
|
||||||
|
code : 500,
|
||||||
|
error : e
|
||||||
|
});
|
||||||
|
}
|
||||||
|
user.friends.push({
|
||||||
|
id : friend.id,
|
||||||
|
username : friend.username
|
||||||
|
});
|
||||||
|
UserStore.set({
|
||||||
|
id : user.id,
|
||||||
|
friends : user.friends
|
||||||
|
}, function(e, updatedUser){
|
||||||
|
if(e){
|
||||||
|
return next(null, {
|
||||||
|
code : 500,
|
||||||
|
error : e
|
||||||
|
});
|
||||||
|
}
|
||||||
|
next(null, {
|
||||||
|
code : 200,
|
||||||
|
route : msg.route
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
82
game-server/app/servers/game/remote/authRemote.js
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
var UserStore = require('../../../persistence/users');
|
||||||
|
var tokenService = require('../../../../../shared/token');
|
||||||
|
var SESSION_CONFIG = require('../../../../../shared/config/session.json');
|
||||||
|
|
||||||
|
module.exports = function(app){
|
||||||
|
return new Remote(app);
|
||||||
|
};
|
||||||
|
var Remote = function(app){
|
||||||
|
this.app = app;
|
||||||
|
};
|
||||||
|
var remote = Remote.prototype;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register user.
|
||||||
|
*
|
||||||
|
* @param {object} userObj object containing userObj.user and userObj.pass
|
||||||
|
* @param {Function} cb
|
||||||
|
* @return {Void}
|
||||||
|
*/
|
||||||
|
remote.register = function(userObj, cb){
|
||||||
|
UserStore.create({
|
||||||
|
username : userObj.username,
|
||||||
|
password : userObj.password,
|
||||||
|
email : userObj.email,
|
||||||
|
chips : 100000
|
||||||
|
}, function(e, user){
|
||||||
|
if(e){
|
||||||
|
cb(e);
|
||||||
|
}else{
|
||||||
|
cb(null, user);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Auth via user/pass or token, and check for expiry.
|
||||||
|
*
|
||||||
|
* @param {object|string} input token or object containing username and password
|
||||||
|
* @param {Function} cb
|
||||||
|
* @return {Void}
|
||||||
|
*/
|
||||||
|
remote.auth = function(input, cb){
|
||||||
|
if(typeof input === 'string'){
|
||||||
|
var res = tokenService.parse(input, SESSION_CONFIG.secret);
|
||||||
|
if(!res){
|
||||||
|
cb('invalid-token');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(!checkExpire(res, SESSION_CONFIG.expire)){
|
||||||
|
cb('token-expired');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
UserStore.getByAttr('id', res.uid, false, function(e, user){
|
||||||
|
if(e){
|
||||||
|
cb('invalid-user');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cb(null, user);
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
UserStore.getByAttr(['username', 'password'], [input.username, input.password], false, function(e, user){
|
||||||
|
if(!user){
|
||||||
|
cb('invalid-user');
|
||||||
|
}else{
|
||||||
|
cb(null, user, tokenService.create(user.id, Date.now(), SESSION_CONFIG.secret));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Check the token whether expire.
|
||||||
|
*
|
||||||
|
* @param {Object} token token info
|
||||||
|
* @param {Number} expire expire time
|
||||||
|
* @return {Boolean} true for not expire and false for expire
|
||||||
|
*/
|
||||||
|
var checkExpire = function(token, expire){
|
||||||
|
if(expire < 0){
|
||||||
|
// negative expire means never expire
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return (Date.now() - token.timestamp) < expire;
|
||||||
|
};
|
22
game-server/app/servers/game/remote/tableRemote.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
module.exports = function(app){
|
||||||
|
return new Remote(app);
|
||||||
|
};
|
||||||
|
var Remote = function(app){
|
||||||
|
this.app = app;
|
||||||
|
this.tableService = app.get('tableService');
|
||||||
|
};
|
||||||
|
var remote = Remote.prototype;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove member/player from table
|
||||||
|
*
|
||||||
|
* @param {string} uid user id
|
||||||
|
* @param {string} sid server id
|
||||||
|
* @param {string} tid channel id
|
||||||
|
* @param {function} cb callback
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
remote.removeMember = function(uid, sid, tid, cb){
|
||||||
|
this.tableService.removeMember(tid, uid, cb);
|
||||||
|
};
|
||||||
|
|
37
game-server/app/servers/gate/handler/gateHandler.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
var dispatcher = require('../../../util/dispatcher');
|
||||||
|
|
||||||
|
module.exports = function(app){
|
||||||
|
return new Handler(app);
|
||||||
|
};
|
||||||
|
|
||||||
|
var Handler = function(app){
|
||||||
|
this.app = app;
|
||||||
|
};
|
||||||
|
|
||||||
|
var handler = Handler.prototype;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gate handler that dispatch user to connectors.
|
||||||
|
*
|
||||||
|
* @param {Object} msg message from client
|
||||||
|
* @param {Object} session
|
||||||
|
* @param {Function} next next stemp callback
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
handler.queryEntry = function(msg, session, next){
|
||||||
|
// get all connectors
|
||||||
|
var connectors = this.app.getServersByType('connector');
|
||||||
|
if(!connectors || connectors.length === 0){
|
||||||
|
next(null, {
|
||||||
|
code : 500
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// select connector
|
||||||
|
var res = dispatcher.dispatch(1, connectors);
|
||||||
|
next(null, {
|
||||||
|
code : 200,
|
||||||
|
host : res.host,
|
||||||
|
port : res.clientPort
|
||||||
|
});
|
||||||
|
};
|
388
game-server/app/services/botService.js
Normal file
@ -0,0 +1,388 @@
|
|||||||
|
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();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
240
game-server/app/services/chatService.js
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
var UserStore = require('../persistence/users');
|
||||||
|
var dispatcher = require('../util/dispatcher');
|
||||||
|
|
||||||
|
var ChatService = function(app) {
|
||||||
|
this.app = app;
|
||||||
|
this.uidMap = {};
|
||||||
|
this.nameMap = {};
|
||||||
|
this.channelMap = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = ChatService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add player into a channel
|
||||||
|
*
|
||||||
|
* @param {String} uid user id
|
||||||
|
* @param {String} cid channel id
|
||||||
|
* @return {Number} see code.js
|
||||||
|
*/
|
||||||
|
ChatService.prototype.addToChannel = function(uid, cid, cb){
|
||||||
|
var me = this;
|
||||||
|
UserStore.getByAttr('id', uid, false, function(e, user){
|
||||||
|
if(e){
|
||||||
|
return cb(e);
|
||||||
|
}
|
||||||
|
var sid = getSidByUid(uid, me.app);
|
||||||
|
if(!sid){
|
||||||
|
return cb('invalid-connector-server');
|
||||||
|
}
|
||||||
|
if(checkDuplicate(me, uid, cid)){
|
||||||
|
return cb();
|
||||||
|
}
|
||||||
|
var channel = me.app.get('channelService').getChannel(cid, true);
|
||||||
|
if(!channel){
|
||||||
|
return cb('invalid-channel');
|
||||||
|
}
|
||||||
|
channel.add(uid, sid);
|
||||||
|
addRecord(me, uid, user.username, sid, cid);
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add player record
|
||||||
|
*
|
||||||
|
* @param {String} uid user id
|
||||||
|
*/
|
||||||
|
ChatService.prototype.add = function(uid, cb){
|
||||||
|
var me = this;
|
||||||
|
UserStore.getByAttr('id', uid, false, function(e, user){
|
||||||
|
if(e){
|
||||||
|
return cb(e);
|
||||||
|
}
|
||||||
|
var sid = getSidByUid(uid, me.app);
|
||||||
|
if(!sid){
|
||||||
|
return cb('invalid-connector-server');
|
||||||
|
}
|
||||||
|
addRecord(me, uid, user.username, sid);
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User leaves the channel
|
||||||
|
*
|
||||||
|
* @param {String} uid user id
|
||||||
|
* @param {String} cid channel id
|
||||||
|
*/
|
||||||
|
ChatService.prototype.leave = function(uid, cid) {
|
||||||
|
var record = this.uidMap[uid];
|
||||||
|
var channel = this.app.get('channelService').getChannel(cid, true);
|
||||||
|
if(channel && record) {
|
||||||
|
channel.leave(uid, record.sid);
|
||||||
|
}
|
||||||
|
removeRecord(this, uid, cid);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnect user from all channels in chat service.
|
||||||
|
* This operation would remove the user from all channels and
|
||||||
|
* clear all the records of the user.
|
||||||
|
*
|
||||||
|
* @param {String} uid user id
|
||||||
|
*/
|
||||||
|
ChatService.prototype.disconnect = function(uid){
|
||||||
|
var cids = this.channelMap[uid];
|
||||||
|
var record = this.uidMap[uid];
|
||||||
|
if(cids && record){
|
||||||
|
// remove user from channels
|
||||||
|
var channel;
|
||||||
|
for(var name in cids){
|
||||||
|
channel = this.app.get('channelService').getChannel(name);
|
||||||
|
if(channel){
|
||||||
|
channel.leave(uid, record.sid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clearRecords(this, uid);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get friend list
|
||||||
|
* @param {string} uid user id
|
||||||
|
* @param {Function} cb callback function
|
||||||
|
*/
|
||||||
|
ChatService.prototype.getFriendList = function(uid, cb){
|
||||||
|
var me = this;
|
||||||
|
UserStore.getByAttr('id', uid, {
|
||||||
|
getFullEntity : true
|
||||||
|
}, function(e, user){
|
||||||
|
if(e){
|
||||||
|
return cb(e);
|
||||||
|
}
|
||||||
|
user.friends = user.friends || [];
|
||||||
|
for(var i=0;i<user.friends.length;++i){
|
||||||
|
user.friends[i].online = me.uidMap[user.friends[i].id] ? true : false;
|
||||||
|
}
|
||||||
|
cb(null, user.friends);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Push message by the specified channel
|
||||||
|
*
|
||||||
|
* @param {String} cid channel id
|
||||||
|
* @param {Object} msg message json object
|
||||||
|
* @param {Function} cb callback function
|
||||||
|
*/
|
||||||
|
ChatService.prototype.pushByChannel = function(cid, msg, cb){
|
||||||
|
var channel = this.app.get('channelService').getChannel(cid);
|
||||||
|
if(!channel){
|
||||||
|
cb(new Error('channel ' + cid + ' doses not exist'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
channel.pushMessage('onChat', msg, cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Push message to the specified player
|
||||||
|
*
|
||||||
|
* @param {String} username player's role name
|
||||||
|
* @param {Object} msg message json object
|
||||||
|
* @param {Function} cb callback
|
||||||
|
*/
|
||||||
|
ChatService.prototype.pushByPlayerName = function(username, msg, cb){
|
||||||
|
var record = this.nameMap[username];
|
||||||
|
if(!record){
|
||||||
|
cb('user-not-online');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.app.get('channelService').pushMessageByUids('onUserChat', msg, [{uid: record.uid, sid: record.sid}], cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Push message to the specified player
|
||||||
|
*
|
||||||
|
* @param {String} uid player's user id
|
||||||
|
* @param {Object} msg message json object
|
||||||
|
* @param {Function} cb callback
|
||||||
|
*/
|
||||||
|
ChatService.prototype.pushByPlayerId = function(uid, msg, cb){
|
||||||
|
var record = this.uidMap[uid];
|
||||||
|
if(!record){
|
||||||
|
cb('user-not-online');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.app.get('channelService').pushMessageByUids('onUserChat', msg, [{uid: record.uid, sid: record.sid}], cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the user is already in the channel
|
||||||
|
*/
|
||||||
|
var checkDuplicate = function(service, uid, cid) {
|
||||||
|
return !!service.channelMap[uid] && !!service.channelMap[uid][cid];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add records for the specified user
|
||||||
|
*/
|
||||||
|
var addRecord = function(service, uid, name, sid, cid){
|
||||||
|
var record = {uid: uid, name: name, sid: sid};
|
||||||
|
service.uidMap[uid] = record;
|
||||||
|
service.nameMap[name] = record;
|
||||||
|
var item = service.channelMap[uid];
|
||||||
|
if(!item){
|
||||||
|
item = service.channelMap[uid] = {};
|
||||||
|
}
|
||||||
|
if(cid){
|
||||||
|
item[cid] = 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove records for the specified user and channel pair
|
||||||
|
*/
|
||||||
|
var removeRecord = function(service, uid, cid) {
|
||||||
|
delete service.channelMap[uid][cid];
|
||||||
|
// if(objLen(service.channelMap[uid])){
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// // if user not in any channel then clear his records
|
||||||
|
// clearRecords(service, uid);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear all records of the user
|
||||||
|
*/
|
||||||
|
var clearRecords = function(service, uid) {
|
||||||
|
delete service.channelMap[uid];
|
||||||
|
var record = service.uidMap[uid];
|
||||||
|
if(!record) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
delete service.uidMap[uid];
|
||||||
|
delete service.nameMap[record.name];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the connector server id associated with the uid
|
||||||
|
*/
|
||||||
|
var getSidByUid = function(uid, app) {
|
||||||
|
var connector = dispatcher.dispatch(uid, app.getServersByType('connector'));
|
||||||
|
if(connector) {
|
||||||
|
return connector.id;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
function objLen(obj){
|
||||||
|
if(!obj) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
var size = 0;
|
||||||
|
for(var f in obj) {
|
||||||
|
if(obj.hasOwnProperty(f)) {
|
||||||
|
size++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
64
game-server/app/services/stateService.js
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
var logger = require('pomelo-logger').getLogger('game-log', __filename);
|
||||||
|
var REDIS_CONFIG = require('../../config/redis.json');
|
||||||
|
var redis = require('redis');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maintain a persistent store .
|
||||||
|
*
|
||||||
|
* StateService is created by stateComponent.
|
||||||
|
*
|
||||||
|
* @class
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
module.exports = StateService = function(){
|
||||||
|
this.prefix = 'POKER:';
|
||||||
|
};
|
||||||
|
|
||||||
|
StateService.prototype.start = function(cb){
|
||||||
|
this.redis.createClient(REDIS_CONFIG.port, REDIS_CONFIG.host, REDIS_CONFIG.opts);
|
||||||
|
this.redis.on('error', function(e){
|
||||||
|
logger.error('redis error', e.stack);
|
||||||
|
cb('connection-error');
|
||||||
|
});
|
||||||
|
this.redis.once('ready', function(){
|
||||||
|
logger.info('redis initialized!');
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
StateService.prototype.stop = function(cb){
|
||||||
|
if(this.redis){
|
||||||
|
logger.info('redis stopped');
|
||||||
|
this.redis.end();
|
||||||
|
this.redis = null;
|
||||||
|
}
|
||||||
|
cb();
|
||||||
|
};
|
||||||
|
|
||||||
|
StateService.prototype.push = function(uid, sid ,cb){
|
||||||
|
this.redis.sadd(genKey(this, uid), sid, function(err){
|
||||||
|
invokeCallback(cb, err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
StateService.prototype.remove = function(uid, sid, cb){
|
||||||
|
this.redis.srem(genKey(this, uid), sid, function(err){
|
||||||
|
invokeCallback(cb, err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
StateService.prototype.getSidsByUid = function(uid, cb){
|
||||||
|
this.redis.smembers(genKey(this, uid), function(err, list){
|
||||||
|
invokeCallback(cb, err, list);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var genKey = function(me, uid){
|
||||||
|
return me.prefix + ':' + uid;
|
||||||
|
};
|
||||||
|
|
||||||
|
var invokeCallback = function(cb){
|
||||||
|
if(!!cb && typeof cb === 'function'){
|
||||||
|
cb.apply(null, Array.prototype.slice.call(arguments, 1));
|
||||||
|
}
|
||||||
|
};
|
677
game-server/app/services/tableService.js
Normal file
@ -0,0 +1,677 @@
|
|||||||
|
var events = require('events');
|
||||||
|
var uuid = require('node-uuid');
|
||||||
|
var logger = require('pomelo-logger').getLogger('game-log', __filename);
|
||||||
|
var TableStore = require('../../app/persistence/tables');
|
||||||
|
var UserStore = require('../../app/persistence/users');
|
||||||
|
var dispatcher = require('../util/dispatcher');
|
||||||
|
var Table = require('../game/table');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and maintain table tables.
|
||||||
|
*
|
||||||
|
* TableService is created by tableComponent.
|
||||||
|
*
|
||||||
|
* @class
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
var TableService = function(app, opts){
|
||||||
|
opts = opts || {};
|
||||||
|
this.app = app;
|
||||||
|
this.tables = {};
|
||||||
|
this.prefix = opts.prefix;
|
||||||
|
this.store = opts.store;
|
||||||
|
this.stateService = this.app.get('stateService');
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = TableService;
|
||||||
|
|
||||||
|
TableService.prototype.start = function(cb){
|
||||||
|
cb();
|
||||||
|
};
|
||||||
|
|
||||||
|
TableService.prototype.stop = function(force, cb){
|
||||||
|
cb();
|
||||||
|
};
|
||||||
|
|
||||||
|
TableService.prototype.getTable = function(tid){
|
||||||
|
return this.tables[tid];
|
||||||
|
};
|
||||||
|
|
||||||
|
TableService.prototype.getTables = function(){
|
||||||
|
var tables = {
|
||||||
|
tables : [],
|
||||||
|
totalMembers : 0,
|
||||||
|
totalPlayers : 0
|
||||||
|
};
|
||||||
|
for(var i in this.tables){
|
||||||
|
var table = this.tables[i];
|
||||||
|
var members = table.table.members.length;
|
||||||
|
var players = (table.table.players.length - table.table.playersToRemove.length);
|
||||||
|
tables.totalMembers += members;
|
||||||
|
tables.totalPlayers += players;
|
||||||
|
tables.tables.push({
|
||||||
|
id : table.id,
|
||||||
|
smallBlind : table.table.smallBlind,
|
||||||
|
bigBlind : table.table.bigBlind,
|
||||||
|
minBuyIn : table.table.minBuyIn,
|
||||||
|
maxBuyIn : table.table.maxBuyIn,
|
||||||
|
minPlayers : table.table.minPlayers,
|
||||||
|
maxPlayers : table.table.maxPlayers,
|
||||||
|
gameMode : table.table.gameMode,
|
||||||
|
players : players,
|
||||||
|
members : members
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return tables;
|
||||||
|
};
|
||||||
|
|
||||||
|
TableService.prototype.createTable = function(uid, obj, cb){
|
||||||
|
if(!obj || (obj && (
|
||||||
|
isNaN(obj.smallBlind) ||
|
||||||
|
isNaN(obj.bigBlind) ||
|
||||||
|
isNaN(obj.minBuyIn) ||
|
||||||
|
isNaN(obj.maxBuyIn) ||
|
||||||
|
isNaN(obj.minPlayers) ||
|
||||||
|
isNaN(obj.maxPlayers) ||
|
||||||
|
obj.minPlayers < 2 ||
|
||||||
|
obj.minPlayers > 10 ||
|
||||||
|
obj.maxPlayers < 2 ||
|
||||||
|
obj.maxPlayers > 10
|
||||||
|
))){
|
||||||
|
return cb('invalid-table-rules');
|
||||||
|
}
|
||||||
|
var tid = uuid.v1();
|
||||||
|
this.tables[tid] = {};
|
||||||
|
this.tables[tid].id = tid;
|
||||||
|
this.tables[tid].creator = uid;
|
||||||
|
this.tables[tid].state = 'JOIN';
|
||||||
|
this.tables[tid].tableService = this;
|
||||||
|
obj.smallBlind = Math.round(parseInt(obj.smallBlind));
|
||||||
|
obj.bigBlind = Math.round(parseInt(obj.bigBlind));
|
||||||
|
obj.minBuyIn = Math.round(parseInt(obj.minBuyIn));
|
||||||
|
obj.maxBuyIn = Math.round(parseInt(obj.maxBuyIn));
|
||||||
|
obj.minPlayers = Math.round(parseInt(obj.minPlayers));
|
||||||
|
obj.maxPlayers = Math.round(parseInt(obj.maxPlayers));
|
||||||
|
obj.gameMode = (obj.gameMode == 'normal' || obj.gameMode == 'fast') ? obj.gameMode : 'normal';
|
||||||
|
this.tables[tid].table = new Table(obj.smallBlind, obj.bigBlind, obj.minPlayers, obj.maxPlayers, obj.minBuyIn, obj.maxBuyIn, obj.gameMode, this.tables[tid]);
|
||||||
|
// automatically join created table
|
||||||
|
// session.set('tid', table.id);
|
||||||
|
// var tid = session.get('tid');
|
||||||
|
// me.app.rpc.chat.chatRemote.add(session, session.uid, tid, function(e, users){
|
||||||
|
// if(e){
|
||||||
|
// next(500, {
|
||||||
|
// code : 200,
|
||||||
|
// error : e
|
||||||
|
// });
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// var channelService = me.app.get('channelService');
|
||||||
|
// var channel = channelService.getChannel(tid, true);
|
||||||
|
// channel.pushMessage({
|
||||||
|
// route : 'onTableEvent',
|
||||||
|
// msg : tableService.getTableJSON(tid, session.uid)
|
||||||
|
// });
|
||||||
|
// channel.pushMessage({
|
||||||
|
// route : 'onUpdateUsers',
|
||||||
|
// users : users
|
||||||
|
// });
|
||||||
|
// tableService.broadcastGameState(tid);
|
||||||
|
// next(null, {
|
||||||
|
// code : 200,
|
||||||
|
// route : msg.route
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
cb(null, this.tables[tid]);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add member to the table
|
||||||
|
*
|
||||||
|
* @param {Object} tid id of an existing table
|
||||||
|
* @param {function} cb callback
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
TableService.prototype.addMember = function(tid, uid, cb){
|
||||||
|
var me = this;
|
||||||
|
var channelService = me.app.get('channelService');
|
||||||
|
var table = this.tables[tid];
|
||||||
|
if(!table){
|
||||||
|
cb('table-not-found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
UserStore.getByAttr('id', uid, false, function(e, user){
|
||||||
|
if(!user){
|
||||||
|
cb(e);
|
||||||
|
}
|
||||||
|
var sid = getSidByUid(uid, me.app);
|
||||||
|
if(!sid){
|
||||||
|
return cb('invalid-connector-server');
|
||||||
|
}
|
||||||
|
// TODO: reduce payload by handling based on game state
|
||||||
|
var channel = channelService.getChannel(tid, true);
|
||||||
|
channel.add(uid, sid);
|
||||||
|
channelService.pushMessageByUids({
|
||||||
|
route : 'onTableEvent',
|
||||||
|
msg : me.getTableJSON(tid, uid)
|
||||||
|
}, [{
|
||||||
|
uid : uid,
|
||||||
|
sid : channel.getMember(uid)['sid']
|
||||||
|
}], function(){
|
||||||
|
logger.debug('initiated player '+uid+' into table '+tid+' with state '+table.state);
|
||||||
|
table.table.members.push(user);
|
||||||
|
channel.pushMessage({
|
||||||
|
route : 'onUpdateUsers',
|
||||||
|
members : table.table.members
|
||||||
|
});
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the connector server id associated with the uid
|
||||||
|
*/
|
||||||
|
var getSidByUid = function(uid, app){
|
||||||
|
var connector = dispatcher.dispatch(uid, app.getServersByType('connector'));
|
||||||
|
if(connector){
|
||||||
|
return connector.id;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove member from the table
|
||||||
|
*
|
||||||
|
* @param {Object} tid id of an existing table
|
||||||
|
* @param {string} uid userId to remove from the table
|
||||||
|
* @param {function} cb callback
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
TableService.prototype.removeMember = function(tid, uid, cb){
|
||||||
|
var me = this;
|
||||||
|
if(!me.tables[tid]){
|
||||||
|
var e = 'table-not-found';
|
||||||
|
logger.error('error removing player '+uid+' from table '+tid, e);
|
||||||
|
cb(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var channelService = me.app.get('channelService');
|
||||||
|
var channel = channelService.getChannel(tid, false);
|
||||||
|
if(channel && channel.getMember(uid)){
|
||||||
|
channel.leave(uid, channel.getMember(uid)['sid']);
|
||||||
|
}
|
||||||
|
var user = me.getPlayerJSON(tid, uid, 'players') || me.getPlayerJSON(tid, uid, 'playersToAdd') || me.getPlayerJSON(tid, uid, 'previousPlayers');
|
||||||
|
if(user){
|
||||||
|
console.log('adding '+user.chips+' to player '+user.id);
|
||||||
|
me.updatePlayerInfo(uid, {
|
||||||
|
chips : user.chips
|
||||||
|
}, function(e, updatedUser){
|
||||||
|
if(e){
|
||||||
|
logger.error('error removing player '+uid+' from table ', e);
|
||||||
|
}else{
|
||||||
|
logger.debug('removed player '+uid+' from table '+tid);
|
||||||
|
}
|
||||||
|
me.tables[tid].table.removePlayer(uid);
|
||||||
|
me.pushPlayerInfo(tid, uid, updatedUser);
|
||||||
|
me.handleGameState(tid, cb);
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
me.tables[tid].table.removePlayer(uid);
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update player information
|
||||||
|
*
|
||||||
|
* @param {string} uid id of a user to update
|
||||||
|
* @param {object} obj updated player information
|
||||||
|
* @param {function} cb callback
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
TableService.prototype.updatePlayerInfo = function(uid, obj, cb){
|
||||||
|
UserStore.getByAttr('id', uid, false, function(e, user){
|
||||||
|
if(e){
|
||||||
|
return cb(e);
|
||||||
|
}
|
||||||
|
if(!user){
|
||||||
|
return cb('user-not-found');
|
||||||
|
}
|
||||||
|
var userObj = {
|
||||||
|
id : user.id
|
||||||
|
};
|
||||||
|
if(obj.chips && typeof obj.chips === 'number' && obj.chips != 0){
|
||||||
|
userObj.chips = Math.round(user.chips + Math.round(obj.chips))
|
||||||
|
}
|
||||||
|
if(obj.wins){
|
||||||
|
userObj.wins = Math.round(user.wins + Math.round(obj.wins))
|
||||||
|
}
|
||||||
|
if(obj.wonAmount && obj.wonAmount > user.largestWin){
|
||||||
|
userObj.largestWin = obj.wonAmount;
|
||||||
|
}
|
||||||
|
UserStore.set(userObj, function(e, updatedUser){
|
||||||
|
if(e){
|
||||||
|
cb(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cb(null, updatedUser);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
TableService.prototype.getTableJSON = function(tid, uid){
|
||||||
|
if(!this.tables[tid]){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var table = this.tables[tid];
|
||||||
|
return {
|
||||||
|
state : table.state,
|
||||||
|
id : (table.table && table.table.game && table.table.game.id ? table.table.game.id : undefined),
|
||||||
|
tid : tid,
|
||||||
|
creator : table.creator,
|
||||||
|
smallBlind : table.table.smallBlind,
|
||||||
|
bigBlind : table.table.bigBlind,
|
||||||
|
minPlayers : table.table.minPlayers,
|
||||||
|
maxPlayers : table.table.maxPlayers,
|
||||||
|
minBuyIn : table.table.minBuyIn,
|
||||||
|
maxBuyIn : table.table.maxBuyIn,
|
||||||
|
gameMode : table.table.gameMode,
|
||||||
|
players : this.getPlayersJSON(tid, 'players', uid),
|
||||||
|
playersToRemove : this.getPlayersJSON(tid, 'playersToRemove', uid),
|
||||||
|
playersToAdd : this.getPlayersJSON(tid, 'playersToAdd', uid),
|
||||||
|
gameWinners : this.getPlayersJSON(tid, 'gameWinners', uid),
|
||||||
|
actions : table.table.actions,
|
||||||
|
game : stripDeck(table.table.game, ['deck', 'id']),
|
||||||
|
board : (table.table.game && table.table.game.board) ? table.table.game.board : [],
|
||||||
|
currentPlayer : table.table.currentPlayer
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
function stripDeck(obj, props){
|
||||||
|
var out = {};
|
||||||
|
for(var key in obj){
|
||||||
|
if(props.indexOf(key) == -1){
|
||||||
|
out[key] = obj[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
TableService.prototype.getPlayerIndex = function(tid, uid, type){
|
||||||
|
var match;
|
||||||
|
if(!this.tables[tid]){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for(var i=0;i<this.tables[tid].table[type ? type : 'players'].length;++i){
|
||||||
|
if(uid == this.tables[tid].table[type ? type : 'players'][i].id){
|
||||||
|
match = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return match;
|
||||||
|
};
|
||||||
|
|
||||||
|
TableService.prototype.getPlayerJSON = function(tid, uid, type, requestUid){
|
||||||
|
if(!this.tables[tid]){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var playerIndex = this.getPlayerIndex(tid, uid, type);
|
||||||
|
var player = this.tables[tid].table[type ? type : 'players'][playerIndex];
|
||||||
|
return player ? {
|
||||||
|
playerName : player.playerName,
|
||||||
|
id : player.id,
|
||||||
|
chips : player.chips,
|
||||||
|
folded : player.folded,
|
||||||
|
allIn : player.allIn,
|
||||||
|
talked : player.talked,
|
||||||
|
amount : player.amount,
|
||||||
|
cards : (typeof requestUid === 'undefined' || player.id == requestUid) ? player.cards : undefined,
|
||||||
|
} : undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
TableService.prototype.getPlayersJSON = function(tid, type, requestUid){
|
||||||
|
var players = [];
|
||||||
|
if(!this.tables[tid]){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for(var i=0;i<this.tables[tid].table[type ? type : 'players'].length;++i){
|
||||||
|
players.push(this.getPlayerJSON(tid, this.tables[tid].table[type ? type : 'players'][i].id, type, requestUid));
|
||||||
|
}
|
||||||
|
return players;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a player to the game
|
||||||
|
*
|
||||||
|
* @param {Object} tid id of an existing table
|
||||||
|
* @param {string} uid userId to add to the table
|
||||||
|
* @param {number} buyIn amount to buy in
|
||||||
|
* @param {function} cb callback
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
TableService.prototype.addPlayer = function(tid, uid, buyIn, cb){
|
||||||
|
var me = this;
|
||||||
|
if(!this.tables[tid]){
|
||||||
|
return cb('table-not-found');
|
||||||
|
}
|
||||||
|
var table = this.tables[tid].table;
|
||||||
|
if(me.getPlayerIndex(tid, uid, 'playersToAdd')){
|
||||||
|
return cb('already-joined');
|
||||||
|
}
|
||||||
|
buyIn = parseInt(buyIn);
|
||||||
|
if(isNaN(buyIn) || buyIn < table.minBuyIn || buyIn > table.maxBuyIn){
|
||||||
|
cb('invalid-buyin');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
buyIn = Math.round(buyIn);
|
||||||
|
UserStore.getByAttr('id', uid, false, function(e, user){
|
||||||
|
if(e){
|
||||||
|
cb(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(Math.round(user.chips) < table.minBuyIn){
|
||||||
|
cb('below-minimum-buyin');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(Math.round(user.chips) < buyIn){
|
||||||
|
cb('not-enough-chips');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var chips = Math.round(user.chips - buyIn);
|
||||||
|
UserStore.set({
|
||||||
|
id : user.id,
|
||||||
|
chips : chips
|
||||||
|
}, function(e, updatedUser){
|
||||||
|
if(e){
|
||||||
|
cb(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
table.eventEmitter.emit('playerJoined');
|
||||||
|
var mIndex = me.getPlayerIndex(tid, updatedUser.id, 'members');
|
||||||
|
if(typeof mIndex === 'number'){
|
||||||
|
table.members[mIndex].chips = chips;
|
||||||
|
}
|
||||||
|
table.AddPlayer(updatedUser.username, buyIn, uid);
|
||||||
|
me.pushPlayerInfo(tid, user.id, updatedUser);
|
||||||
|
me.app.get('channelService').getChannel(tid, true).pushMessage({
|
||||||
|
route : 'onUpdateUsers',
|
||||||
|
members : table.members
|
||||||
|
});
|
||||||
|
me.app.get('channelService').getChannel(tid, true).pushMessage({
|
||||||
|
route : 'onTableJoin',
|
||||||
|
msg : me.getPlayerJSON(tid, uid, 'playersToAdd') || me.getPlayerJSON(tid, uid)
|
||||||
|
});
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Push detailed user information to a user
|
||||||
|
*
|
||||||
|
* @param {Object} tid id of an existing table
|
||||||
|
* @param {string} uid userId to add to the table
|
||||||
|
* @param {object} info player information
|
||||||
|
* @param {function} cb callback
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
TableService.prototype.pushPlayerInfo = function(tid, uid, info){
|
||||||
|
var channelService = this.app.get('channelService');
|
||||||
|
var channel = channelService.getChannel(tid, false);
|
||||||
|
if(!channel || !channel.getMember(uid)) return;
|
||||||
|
channelService.pushMessageByUids({
|
||||||
|
route : 'onUpdateMyself',
|
||||||
|
user : info
|
||||||
|
}, [{
|
||||||
|
uid : uid,
|
||||||
|
sid : channel.getMember(uid)['sid']
|
||||||
|
}], function(e){
|
||||||
|
if(e){
|
||||||
|
logger.error('unable to push player info ', e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the game
|
||||||
|
*
|
||||||
|
* @param {Object} tid id of an existing table
|
||||||
|
* @param {function} cb callback
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
TableService.prototype.startGame = function(tid, cb){
|
||||||
|
var table = this.tables[tid];
|
||||||
|
if(!table){
|
||||||
|
return cb('table-not-found');
|
||||||
|
}
|
||||||
|
if(table.state != 'JOIN'){
|
||||||
|
return cb('table-not-ready');
|
||||||
|
}
|
||||||
|
if(table.table.active){
|
||||||
|
return cb('table-still-active');
|
||||||
|
}
|
||||||
|
if(table.table.playersToAdd.length < table.table.minPlayers){
|
||||||
|
return cb('not-enough-players');
|
||||||
|
}
|
||||||
|
if(table.table.playersToAdd.length > table.table.maxPlayers){
|
||||||
|
return cb('too-many-players');
|
||||||
|
}
|
||||||
|
// remove chips from user for buy in
|
||||||
|
table.table.StartGame();
|
||||||
|
this.app.get('channelService').getChannel(tid, true).pushMessage({
|
||||||
|
route : 'onUpdateUsers',
|
||||||
|
members : table.table.members
|
||||||
|
});
|
||||||
|
this.broadcastGameState(tid);
|
||||||
|
cb();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform a game action
|
||||||
|
*
|
||||||
|
* @param {string} tid table id
|
||||||
|
* @param {string} uid userId to add to the table
|
||||||
|
* @param {object} action an object containing the action type and optionally the amount of chips
|
||||||
|
* @param {function} cb callback
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
TableService.prototype.performAction = function(tid, uid, action, cb){
|
||||||
|
var me = this;
|
||||||
|
var table = this.tables[tid];
|
||||||
|
if(!table){
|
||||||
|
return cb('table-not-found');
|
||||||
|
}
|
||||||
|
if(table.state != 'IN_PROGRESS'){
|
||||||
|
return cb('game-not-ready');
|
||||||
|
}
|
||||||
|
if(me.getPlayerIndex(tid, uid) != table.table.currentPlayer){
|
||||||
|
return cb('not-your-turn');
|
||||||
|
}
|
||||||
|
if(me.getPlayerJSON(tid, uid).folded == true){
|
||||||
|
return cb('already-folded');
|
||||||
|
}
|
||||||
|
if(action.action == 'bet' && isNaN(action.amt)){
|
||||||
|
return cb('invalid-bet-amt');
|
||||||
|
}
|
||||||
|
// perform action
|
||||||
|
if(action.action == 'call'){
|
||||||
|
table.table.players[table.table.currentPlayer].Call();
|
||||||
|
}else if(action.action == 'bet'){
|
||||||
|
table.table.players[table.table.currentPlayer].Bet(parseInt(action.amt));
|
||||||
|
}else if(action.action == 'check'){
|
||||||
|
table.table.players[table.table.currentPlayer].Check();
|
||||||
|
}else if(action.action == 'allin'){
|
||||||
|
table.table.players[table.table.currentPlayer].AllIn();
|
||||||
|
}else if(action.action == 'fold'){
|
||||||
|
table.table.players[table.table.currentPlayer].Fold();
|
||||||
|
}else{
|
||||||
|
return cb('invalid-action');
|
||||||
|
}
|
||||||
|
table.table.stopTimer();
|
||||||
|
logger.debug('player '+uid+' executed action '+action.action+' on table '+tid+' with state '+table.state);
|
||||||
|
me.handleGameState(tid, function(e){
|
||||||
|
if(e){
|
||||||
|
return cb(e);
|
||||||
|
}
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* End game and broadcast result to clients
|
||||||
|
*
|
||||||
|
* @param {string} tid table id
|
||||||
|
* @param {function} cb callback
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
TableService.prototype.endGame = function(tid, cb){
|
||||||
|
var me = this;
|
||||||
|
if(!me.tables[tid]){
|
||||||
|
cb('table-not-found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var table = me.tables[tid];
|
||||||
|
if(table.table.game.roundName != 'GameEnd'){
|
||||||
|
cb('not-game-end');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
table.table.active = false;
|
||||||
|
table.table.stopTimer();
|
||||||
|
me.saveResults(tid, function(e){
|
||||||
|
if(e){
|
||||||
|
cb(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var channelService = me.app.get('channelService');
|
||||||
|
channelService.getChannel(tid, false).pushMessage({
|
||||||
|
route : 'onUpdateUsers',
|
||||||
|
members : table.table.members
|
||||||
|
});
|
||||||
|
table.table.initNewGame();
|
||||||
|
me.broadcastGameState(tid);
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store table results to persistence
|
||||||
|
*
|
||||||
|
* @param {string} tid id of the table
|
||||||
|
* @param {string} cb callback
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
TableService.prototype.saveResults = function(tid, cb){
|
||||||
|
var me = this;
|
||||||
|
if(!this.tables[tid]){
|
||||||
|
cb('table-not-found');
|
||||||
|
}
|
||||||
|
var table = this.tables[tid];
|
||||||
|
TableStore.getByAttr('id', table.table.game.id, function(e, foundTable){
|
||||||
|
if(foundTable){
|
||||||
|
cb('game-already-exists');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
TableStore.create(me.getTableJSON(tid), function(e, newTable){
|
||||||
|
if(e){
|
||||||
|
cb(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var i = 0;
|
||||||
|
function saveWinner(){
|
||||||
|
me.updatePlayerInfo(table.table.gameWinners[i].id, {
|
||||||
|
wins : 1,
|
||||||
|
wonAmount : table.table.gameWinners[i].amount
|
||||||
|
}, function(){
|
||||||
|
if(++i === table.table.gameWinners.length){
|
||||||
|
cb();
|
||||||
|
}else{
|
||||||
|
saveWinner();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if(table.table.gameWinners.length){
|
||||||
|
saveWinner();
|
||||||
|
}else{
|
||||||
|
return cb();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle end of game or broadcast game state to users
|
||||||
|
*
|
||||||
|
* @param {string} tid id of the table
|
||||||
|
* @param {function} cb callback
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
TableService.prototype.handleGameState = function(tid, cb){
|
||||||
|
var me = this;
|
||||||
|
var table = me.tables[tid];
|
||||||
|
if(table.table && table.table.game && table.table.game.roundName == 'GameEnd' && table.state == 'IN_PROGRESS' && table.table.active){
|
||||||
|
me.endGame(tid, cb);
|
||||||
|
}else{
|
||||||
|
me.app.get('channelService').getChannel(tid, true).pushMessage({
|
||||||
|
route : 'onUpdateUsers',
|
||||||
|
members : table.table.members
|
||||||
|
});
|
||||||
|
me.broadcastGameState(tid);
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Broadcast game state by iteratively pushing game details to clients
|
||||||
|
*
|
||||||
|
* @param {string} tid id
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
TableService.prototype.broadcastGameState = function(tid){
|
||||||
|
var i = 0;
|
||||||
|
var me = this;
|
||||||
|
var channelService = me.app.get('channelService');
|
||||||
|
var channel = channelService.getChannel(tid, false);
|
||||||
|
function broadcast(){
|
||||||
|
if(i == me.tables[tid].table.members.length){
|
||||||
|
if(me.tables[tid].state == 'IN_PROGRESS' && me.tables[tid].table.active){
|
||||||
|
me.tables[tid].table.startTimer();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var uid = me.tables[tid].table.members[i].id;
|
||||||
|
if(channel.getMember(uid)){
|
||||||
|
channelService.pushMessageByUids({
|
||||||
|
route : 'onTableEvent',
|
||||||
|
msg : me.getTableJSON(tid, uid)
|
||||||
|
}, [{
|
||||||
|
uid : uid,
|
||||||
|
sid : channel.getMember(uid)['sid']
|
||||||
|
}], function(){
|
||||||
|
++i;
|
||||||
|
broadcast();
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
++i;
|
||||||
|
broadcast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
broadcast();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shuffles an array
|
||||||
|
*
|
||||||
|
* @param {array} ary an array
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
TableService.prototype.shuffle = function(ary){
|
||||||
|
var currentIndex = ary.length, temporaryValue, randomIndex;
|
||||||
|
while(0 !== currentIndex){
|
||||||
|
randomIndex = Math.floor(Math.random() * currentIndex);
|
||||||
|
currentIndex -= 1;
|
||||||
|
temporaryValue = ary[currentIndex];
|
||||||
|
ary[currentIndex] = ary[randomIndex];
|
||||||
|
ary[randomIndex] = temporaryValue;
|
||||||
|
}
|
||||||
|
return ary;
|
||||||
|
};
|
||||||
|
|
6
game-server/app/util/dispatcher.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
var crc = require('crc');
|
||||||
|
|
||||||
|
module.exports.dispatch = function(uid, connectors){
|
||||||
|
var index = Math.abs(crc.crc32(uid)) % connectors.length;
|
||||||
|
return connectors[index];
|
||||||
|
};
|
12
game-server/app/util/routeUtil.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
var exp = module.exports;
|
||||||
|
var dispatcher = require('./dispatcher');
|
||||||
|
|
||||||
|
exp.game = function(session, msg, app, cb){
|
||||||
|
var gameServers = app.getServersByType('game');
|
||||||
|
if(!gameServers || gameServers.length === 0){
|
||||||
|
cb(new Error('can not find game servers.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var res = dispatcher.dispatch(1, gameServers);
|
||||||
|
cb(null, res.id);
|
||||||
|
};
|
13
game-server/config/adminServer.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
[{
|
||||||
|
"type": "connector",
|
||||||
|
"token": "agarxhqb98rpajloaxn34ga8xrunpagkjwlaw3ruxnpaagl29w4rxn"
|
||||||
|
}, {
|
||||||
|
"type": "chat",
|
||||||
|
"token": "agarxhqb98rpajloaxn34ga8xrunpagkjwlaw3ruxnpaagl29w4rxn"
|
||||||
|
},{
|
||||||
|
"type": "game",
|
||||||
|
"token": "agarxhqb98rpajloaxn34ga8xrunpagkjwlaw3ruxnpaagl29w4rxn"
|
||||||
|
},{
|
||||||
|
"type": "gate",
|
||||||
|
"token": "agarxhqb98rpajloaxn34ga8xrunpagkjwlaw3ruxnpaagl29w4rxn"
|
||||||
|
}]
|
16
game-server/config/adminUser.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
[{
|
||||||
|
"id": "user-1",
|
||||||
|
"username": "admin",
|
||||||
|
"password": "admin",
|
||||||
|
"level": 1
|
||||||
|
}, {
|
||||||
|
"id": "user-2",
|
||||||
|
"username": "monitor",
|
||||||
|
"password": "monitor",
|
||||||
|
"level": 2
|
||||||
|
},{
|
||||||
|
"id": "user-3",
|
||||||
|
"username": "test",
|
||||||
|
"password": "test",
|
||||||
|
"level": 2
|
||||||
|
}]
|
63
game-server/config/bots.json
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
{
|
||||||
|
"config" : {
|
||||||
|
"enabled" : true,
|
||||||
|
"joinInterval" : {
|
||||||
|
"min" : 3,
|
||||||
|
"max" : 3
|
||||||
|
},
|
||||||
|
"actionInterval" : {
|
||||||
|
"min" : 1,
|
||||||
|
"max" : 3
|
||||||
|
},
|
||||||
|
"gamesToPlay" : {
|
||||||
|
"min" : 1,
|
||||||
|
"max" : 1
|
||||||
|
},
|
||||||
|
"buyIn" : 1000,
|
||||||
|
"minBots" : 7,
|
||||||
|
"minPlayers" : 3,
|
||||||
|
"validateChips" : false
|
||||||
|
},
|
||||||
|
"bots" : [
|
||||||
|
{
|
||||||
|
"username": "bot1",
|
||||||
|
"password": "bot1",
|
||||||
|
"email": "bot1@poker-stack.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"username": "bot2",
|
||||||
|
"password": "bot2",
|
||||||
|
"email": "bot2@poker-stack.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"username": "bot3",
|
||||||
|
"password": "bot3",
|
||||||
|
"email": "bot3@poker-stack.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"username": "bot4",
|
||||||
|
"password": "bot4",
|
||||||
|
"email": "bot4@poker-stack.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"username": "bot5",
|
||||||
|
"password": "bot5",
|
||||||
|
"email": "bot5@poker-stack.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"username": "bot6",
|
||||||
|
"password": "bot6",
|
||||||
|
"email": "bot6@poker-stack.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"username": "bot7",
|
||||||
|
"password": "bot7",
|
||||||
|
"email": "bot7@poker-stack.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"username": "bot8",
|
||||||
|
"password": "bot8",
|
||||||
|
"email": "bot8@poker-stack.com"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
10
game-server/config/gameSettings.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"gameMode" : {
|
||||||
|
"normal" : {
|
||||||
|
"timeout" : 30
|
||||||
|
},
|
||||||
|
"fast" : {
|
||||||
|
"timeout" : 15
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
125
game-server/config/log4js.json
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
{
|
||||||
|
"appenders": [
|
||||||
|
{
|
||||||
|
"type": "console"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "file",
|
||||||
|
"filename": "${opts:base}/logs/con-log-${opts:serverId}.log",
|
||||||
|
"pattern": "connector",
|
||||||
|
"maxLogSize": 1048576,
|
||||||
|
"layout": {
|
||||||
|
"type": "basic"
|
||||||
|
},
|
||||||
|
"backups": 5,
|
||||||
|
"category": "con-log"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "file",
|
||||||
|
"filename": "${opts:base}/logs/rpc-log-${opts:serverId}.log",
|
||||||
|
"maxLogSize": 1048576,
|
||||||
|
"layout": {
|
||||||
|
"type": "basic"
|
||||||
|
},
|
||||||
|
"backups": 5,
|
||||||
|
"category": "rpc-log"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "file",
|
||||||
|
"filename": "${opts:base}/logs/forward-log-${opts:serverId}.log",
|
||||||
|
"maxLogSize": 1048576,
|
||||||
|
"layout": {
|
||||||
|
"type": "basic"
|
||||||
|
},
|
||||||
|
"backups": 5,
|
||||||
|
"category": "forward-log"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "file",
|
||||||
|
"filename": "${opts:base}/logs/rpc-debug-${opts:serverId}.log",
|
||||||
|
"maxLogSize": 1048576,
|
||||||
|
"layout": {
|
||||||
|
"type": "basic"
|
||||||
|
},
|
||||||
|
"backups": 5,
|
||||||
|
"category": "rpc-debug"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "file",
|
||||||
|
"level": "DEBUG",
|
||||||
|
"filename": "${opts:base}/logs/game-log-${opts:serverId}.log",
|
||||||
|
"maxLogSize": 1048576,
|
||||||
|
"layout": {
|
||||||
|
"type": "basic"
|
||||||
|
},
|
||||||
|
"backups": 5,
|
||||||
|
"category": "game-log"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "file",
|
||||||
|
"filename": "${opts:base}/logs/crash.log",
|
||||||
|
"maxLogSize": 1048576,
|
||||||
|
"layout": {
|
||||||
|
"type": "basic"
|
||||||
|
},
|
||||||
|
"backups": 5,
|
||||||
|
"category":"crash-log"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "file",
|
||||||
|
"filename": "${opts:base}/logs/admin.log",
|
||||||
|
"maxLogSize": 1048576,
|
||||||
|
"layout": {
|
||||||
|
"type": "basic"
|
||||||
|
}
|
||||||
|
,"backups": 5,
|
||||||
|
"category":"admin-log"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "file",
|
||||||
|
"filename": "${opts:base}/logs/pomelo.log",
|
||||||
|
"maxLogSize": 1048576,
|
||||||
|
"layout": {
|
||||||
|
"type": "basic"
|
||||||
|
}
|
||||||
|
,"backups": 5,
|
||||||
|
"category":"pomelo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "file",
|
||||||
|
"filename": "${opts:base}/logs/pomelo-admin.log",
|
||||||
|
"maxLogSize": 1048576,
|
||||||
|
"layout": {
|
||||||
|
"type": "basic"
|
||||||
|
}
|
||||||
|
,"backups": 5,
|
||||||
|
"category":"pomelo-admin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "file",
|
||||||
|
"filename": "${opts:base}/logs/pomelo-rpc.log",
|
||||||
|
"maxLogSize": 1048576,
|
||||||
|
"layout": {
|
||||||
|
"type": "basic"
|
||||||
|
}
|
||||||
|
,"backups": 5,
|
||||||
|
"category":"pomelo-rpc"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"levels": {
|
||||||
|
"rpc-log" : "ERROR",
|
||||||
|
"forward-log": "ERROR",
|
||||||
|
"con-log" : "ERROR",
|
||||||
|
"rpc-debug": "ERROR",
|
||||||
|
"crash-log" : "ERROR",
|
||||||
|
"admin-log": "ERROR",
|
||||||
|
"pomelo" : "ERROR",
|
||||||
|
"pomelo-admin": "ERROR",
|
||||||
|
"pomelo-rpc": "ERROR"
|
||||||
|
},
|
||||||
|
|
||||||
|
"replaceConsole": true,
|
||||||
|
|
||||||
|
"lineDebug": false
|
||||||
|
}
|
14
game-server/config/master.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"development":{
|
||||||
|
"id":"master-server-1",
|
||||||
|
"host":"127.0.0.1",
|
||||||
|
"port":3005
|
||||||
|
},
|
||||||
|
|
||||||
|
"production":{
|
||||||
|
"id":"master-server-1",
|
||||||
|
"host":"127.0.0.1",
|
||||||
|
"port":3005
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
7
game-server/config/redis.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"host" : "localhost",
|
||||||
|
"port" : 6379,
|
||||||
|
"opts" : {
|
||||||
|
"auth_pass" : "secretredispass"
|
||||||
|
}
|
||||||
|
}
|
30
game-server/config/servers.json
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"development":{
|
||||||
|
"connector":[
|
||||||
|
{"id":"connector-server-1", "host":"127.0.0.1", "port":4050, "clientPort": 3050, "frontend": true}
|
||||||
|
],
|
||||||
|
"game":[
|
||||||
|
{"id":"game-server-1", "host":"127.0.0.1", "port":6050}
|
||||||
|
],
|
||||||
|
"chat": [
|
||||||
|
{"id":"chat-server-1","host":"127.0.0.1","port":3450}
|
||||||
|
],
|
||||||
|
"gate":[
|
||||||
|
{"id": "gate-server-1", "host": "127.0.0.1", "clientPort": 3014, "frontend": true}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"production":{
|
||||||
|
"connector":[
|
||||||
|
{"id":"connector-server-1", "host":"127.0.0.1", "port":4050, "clientPort": 3050, "frontend": true}
|
||||||
|
],
|
||||||
|
"game":[
|
||||||
|
{"id":"game-server-1", "host":"127.0.0.1", "port":6050}
|
||||||
|
],
|
||||||
|
"chat": [
|
||||||
|
{"id":"chat-server-1","host":"127.0.0.1","port":3450}
|
||||||
|
],
|
||||||
|
"gate":[
|
||||||
|
{"id": "gate-server-1", "host": "127.0.0.1", "clientPort": 3014, "frontend": true}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
0
game-server/localstore/tables.json
Normal file
0
game-server/localstore/users.json
Normal file
4
game-server/logs/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# Ignore everything in this directory
|
||||||
|
*
|
||||||
|
# Except this file
|
||||||
|
!.gitignore
|
15
game-server/package.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"author": "Edward Yang",
|
||||||
|
"name": "poker-game-stack-server",
|
||||||
|
"description": "Texas Holdem poker game server",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": false,
|
||||||
|
"dependencies": {
|
||||||
|
"pomelo": "~1.0.0",
|
||||||
|
"crc": "0.2.0",
|
||||||
|
"hoyle": "^0.2.2",
|
||||||
|
"node-uuid": "^1.4.1",
|
||||||
|
"pomelo-logger": "^0.1.6",
|
||||||
|
"redis": "^0.12.1"
|
||||||
|
}
|
||||||
|
}
|
4
npm-install.bat
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
::npm-install.bat
|
||||||
|
@echo off
|
||||||
|
::install web server dependencies && game server dependencies
|
||||||
|
cd web-server && npm install -d && cd .. && cd game-server && npm install -d
|
5
npm-install.sh
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
cd ./game-server && npm install -d
|
||||||
|
echo '============ game-server npm installed ============'
|
||||||
|
cd ..
|
||||||
|
cd ./web-server && npm install -d
|
||||||
|
echo '============ web-server npm installed ============'
|
23
shared/config/authCodes.json
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"OK" : 200,
|
||||||
|
"FAILED" : 500,
|
||||||
|
"BAD_REQUEST" : 400,
|
||||||
|
"UNAUTHORIZED" : 401,
|
||||||
|
"FORBIDDEN" : 403,
|
||||||
|
"ENTRY" : {
|
||||||
|
"TOKEN_INVALID" : 1001,
|
||||||
|
"TOKEN_EXPIRE" : 1002,
|
||||||
|
"USER_NOT_EXIST" : 1003
|
||||||
|
},
|
||||||
|
|
||||||
|
"GATE" : {
|
||||||
|
"NO_SERVER_AVAILABLE" : 2001
|
||||||
|
},
|
||||||
|
|
||||||
|
"CHAT" : {
|
||||||
|
"CHANNEL_CREATE" : 3001,
|
||||||
|
"CHANNEL_NOT_EXIST" : 3002,
|
||||||
|
"UNKNOWN_CONNECTOR" : 3003,
|
||||||
|
"USER_NOT_ONLINE" : 3004
|
||||||
|
}
|
||||||
|
}
|
4
shared/config/session.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"secret": "poker-game-session-secret",
|
||||||
|
"expire": 21600000
|
||||||
|
}
|
15
shared/server.crt
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICSzCCAbQCCQCQVN8rD6MylDANBgkqhkiG9w0BAQUFADBqMQswCQYDVQQGEwJD
|
||||||
|
TjERMA8GA1UECAwIemhlamlhbmcxETAPBgNVBAcMCGhhbmd6aG91MRAwDgYDVQQK
|
||||||
|
DAdOZXRFYXNlMQ8wDQYDVQQLDAZwb21lbG8xEjAQBgNVBAMMCWxvY2FsaG9zdDAe
|
||||||
|
Fw0xNDA0MjIwNjEwMDJaFw0xNDA1MjIwNjEwMDJaMGoxCzAJBgNVBAYTAkNOMREw
|
||||||
|
DwYDVQQIDAh6aGVqaWFuZzERMA8GA1UEBwwIaGFuZ3pob3UxEDAOBgNVBAoMB05l
|
||||||
|
dEVhc2UxDzANBgNVBAsMBnBvbWVsbzESMBAGA1UEAwwJbG9jYWxob3N0MIGfMA0G
|
||||||
|
CSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMPe8oscKpTlQFZRrpbWmSO1UE+H65nq50
|
||||||
|
l5+ptOVPMK3wgEj+YRyGWhBjugj9teVmLXY9ImWdZkBlvdAiQj7/S/1MxRbRtwEF
|
||||||
|
GRE5ul/X1M6I+F0UyTGYA1Mo0jIlQaBDXAAyDujCWi+qlyZ28efNDUlO2KBY1H4r
|
||||||
|
Xobm9hoEFQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAMIuL8KqEEtjbfL/tR2+5dQ5
|
||||||
|
958gtDtA62L7bMosl4hmuzdyWADu3IcKSaXAESLhIuIClt2Pwc14iFf9qRyB/cjY
|
||||||
|
4kLgwDGhK5EJw1kQS+Hs9NNSGxJTXUkoms3kEdRGy4hrZpTheJJNaKuv3oXrdvYQ
|
||||||
|
85yoc/P5OnJapB3huYL9
|
||||||
|
-----END CERTIFICATE-----
|
15
shared/server.key
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIICWwIBAAKBgQDMPe8oscKpTlQFZRrpbWmSO1UE+H65nq50l5+ptOVPMK3wgEj+
|
||||||
|
YRyGWhBjugj9teVmLXY9ImWdZkBlvdAiQj7/S/1MxRbRtwEFGRE5ul/X1M6I+F0U
|
||||||
|
yTGYA1Mo0jIlQaBDXAAyDujCWi+qlyZ28efNDUlO2KBY1H4rXobm9hoEFQIDAQAB
|
||||||
|
AoGAXhaeCUIyqeoynLWh+yzzOHFqzjpnrr0iIwYCgJycEqobRzLh7YXxLRdqe3al
|
||||||
|
U7Oq9TI2SR2CcEs9mWEi89VOzVvfu+4zRlvJLMzNjG8ncdvzmzWR288ORq6qmYVU
|
||||||
|
3KAEz/tbNaQMLrD43hkIb9BrSIb/cnwekl3pANo9dwytU5UCQQD4V6vTyzs/ob21
|
||||||
|
+fO98tFkPtoHbt43S/1kDBSUyh6WWbS1KIQgtUSr2P5Ddtl6/vD3DW+XHCAhxyfV
|
||||||
|
vuDvaP/fAkEA0oomFfmlpvzYejYNKPOz2PR+M0oRFVwn7lYyNwbRtUK1JYOMHwJ/
|
||||||
|
3gwQEgAcYEkvgRlsxX0T5vHNmoR3U3OqiwJAIWkiG9devDvVWxMqoKZ3V0ZBbPiU
|
||||||
|
etoFWB1r82yR2uZssmamCAR7HaeO5aKqtapw3rv3BFxrUkAJ8u7AMlVs/wJAVnpm
|
||||||
|
MGqNjyyWIoSnHSYUvk2WtKx8neBvimcfUxja9HAFBfaljGszaFpeE3a2MRp+h7GQ
|
||||||
|
ywGYNikmAYzdkoqVBwJAcOm/6u863pD2xA1mSFnmm3TulAMBfCULLdcY40w9m38b
|
||||||
|
D89R1ISEy//N1fWa4KTsM0GpVOowEyluc53XNRUghw==
|
||||||
|
-----END RSA PRIVATE KEY-----
|
42
shared/token.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
var crypto = require('crypto');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create token by uid. Encrypt uid and timestamp to get a token.
|
||||||
|
*
|
||||||
|
* @param {String} uid user id
|
||||||
|
* @param {String|Number} timestamp
|
||||||
|
* @param {String} pwd encrypt password
|
||||||
|
* @return {String} token string
|
||||||
|
*/
|
||||||
|
module.exports.create = function(uid, timestamp, pwd){
|
||||||
|
var msg = uid + '|' + timestamp;
|
||||||
|
var cipher = crypto.createCipher('aes256', pwd);
|
||||||
|
var enc = cipher.update(msg, 'utf8', 'hex');
|
||||||
|
enc += cipher.final('hex');
|
||||||
|
return enc;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse token to validate it and get the uid and timestamp.
|
||||||
|
*
|
||||||
|
* @param {String} token token string
|
||||||
|
* @param {String} pwd decrypt password
|
||||||
|
* @return {Object} uid and timestamp that exported from token. null for illegal token.
|
||||||
|
*/
|
||||||
|
module.exports.parse = function(token, pwd){
|
||||||
|
var decipher = crypto.createDecipher('aes256', pwd);
|
||||||
|
var dec;
|
||||||
|
try{
|
||||||
|
dec = decipher.update(token, 'hex', 'utf8');
|
||||||
|
dec += decipher.final('utf8');
|
||||||
|
} catch(err){
|
||||||
|
console.error('[token] fail to decrypt token. %j', token);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var ts = dec.split('|');
|
||||||
|
if(ts.length !== 2){
|
||||||
|
// illegal token
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return {uid: ts[0], timestamp: Number(ts[1])};
|
||||||
|
};
|
26
web-server/app.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
var express = require('express');
|
||||||
|
var app = express();
|
||||||
|
|
||||||
|
app.configure(function(){
|
||||||
|
app.use(express.methodOverride());
|
||||||
|
app.use(express.bodyParser());
|
||||||
|
app.use(app.router);
|
||||||
|
app.set('view engine', 'jade');
|
||||||
|
app.set('views', __dirname + '/public');
|
||||||
|
app.set('view options', {layout: false});
|
||||||
|
app.set('basepath',__dirname + '/public');
|
||||||
|
});
|
||||||
|
|
||||||
|
app.configure('development', function(){
|
||||||
|
app.use(express.static(__dirname + '/public'));
|
||||||
|
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
|
||||||
|
});
|
||||||
|
|
||||||
|
app.configure('production', function(){
|
||||||
|
var oneYear = 31557600000;
|
||||||
|
app.use(express.static(__dirname + '/public', { maxAge: oneYear }));
|
||||||
|
app.use(express.errorHandler());
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("Web server has started.\nPlease log on http://127.0.0.1:3002/index.html");
|
||||||
|
app.listen(3002);
|
1
web-server/bin/component.bat
Normal file
@ -0,0 +1 @@
|
|||||||
|
cd public/js/lib && component install -f && component build -v
|
1
web-server/bin/component.sh
Normal file
@ -0,0 +1 @@
|
|||||||
|
cd public/js/lib && component install -f && component build -v
|
10
web-server/package.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"author": "Edward Yang",
|
||||||
|
"name": "poker-game-stack-webclient",
|
||||||
|
"description": "Texas Holdem poker game web client",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": false,
|
||||||
|
"dependencies": {
|
||||||
|
"express": "3.4.0"
|
||||||
|
}
|
||||||
|
}
|
5
web-server/public/css/bootstrap.min.css
vendored
Normal file
31
web-server/public/css/styles.css
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
.navbar-default .btn{margin-right:5px}
|
||||||
|
.panel-heading .btn-default.pull-right{margin:-5px;margin-left:15px}
|
||||||
|
.msg-text.error{color:#FF0000}
|
||||||
|
|
||||||
|
#user-list-container .list-group{margin:0}
|
||||||
|
#user-list-container .panel-body{padding:0}
|
||||||
|
#user-list-container .list-group-item.is-me{background:#d9edf7;border-radius:0}
|
||||||
|
|
||||||
|
#chat-container .panel-body{padding:0 2px}
|
||||||
|
#chat-container textarea{border:none;background:none;width:100%;padding:3px;height:58px;margin-top:10px;border-top:solid 1px #ccc}
|
||||||
|
#chat-area{height:130px;overflow-y:auto}
|
||||||
|
#chat-area .nick{padding:0 4px;font-weight:bold}
|
||||||
|
#chat-area .msg-text{margin-left:5px}
|
||||||
|
|
||||||
|
#history-container .panel-body{padding:0 2px}
|
||||||
|
#history-area{min-height:100px;max-height:160px;overflow-y:auto}
|
||||||
|
|
||||||
|
#poker-container h4{font-size:24px;color:#888;text-align:center;margin:20px auto}
|
||||||
|
#poker-container .panel-body .panel img{height:100px}
|
||||||
|
#poker-container .panel-body .panel .btn-primary{margin:10px 0}
|
||||||
|
#poker-container .panel-body .panel .btn-primary.input-group-addon{background-color:#428bca;border-color:#357ebd;padding:6px 12px}
|
||||||
|
#poker-container .panel-body .panel .btn-primary.input-group-addon, .btn-primary.input-group-addon:hover{color:#fff}
|
||||||
|
|
||||||
|
#friend-list-container .list-group{margin:0}
|
||||||
|
#friend-list li{padding:4px 15px}
|
||||||
|
#friend-results-list{margin:15px 0 0 0;padding:0}
|
||||||
|
|
||||||
|
#messenger-area{height:200px;overflow-y:auto;border: 1px solid #ccc}
|
||||||
|
#messenger-area .nick{padding:0 4px;font-weight:bold}
|
||||||
|
#messenger-area .msg-text{margin-left:5px}
|
||||||
|
#messenger-chatbox{background:none;width:100%;padding:10px;margin-top:10px;border:solid 1px #ccc}
|
BIN
web-server/public/fonts/glyphicons-halflings-regular.eot
Normal file
229
web-server/public/fonts/glyphicons-halflings-regular.svg
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<metadata></metadata>
|
||||||
|
<defs>
|
||||||
|
<font id="glyphicons_halflingsregular" horiz-adv-x="1200" >
|
||||||
|
<font-face units-per-em="1200" ascent="960" descent="-240" />
|
||||||
|
<missing-glyph horiz-adv-x="500" />
|
||||||
|
<glyph />
|
||||||
|
<glyph />
|
||||||
|
<glyph unicode="
" />
|
||||||
|
<glyph unicode=" " />
|
||||||
|
<glyph unicode="*" d="M100 500v200h259l-183 183l141 141l183 -183v259h200v-259l183 183l141 -141l-183 -183h259v-200h-259l183 -183l-141 -141l-183 183v-259h-200v259l-183 -183l-141 141l183 183h-259z" />
|
||||||
|
<glyph unicode="+" d="M0 400v300h400v400h300v-400h400v-300h-400v-400h-300v400h-400z" />
|
||||||
|
<glyph unicode=" " />
|
||||||
|
<glyph unicode=" " horiz-adv-x="652" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="1304" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="652" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="1304" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="434" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="326" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="217" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="217" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="163" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="260" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="72" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="260" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="326" />
|
||||||
|
<glyph unicode="€" d="M100 500l100 100h113q0 47 5 100h-218l100 100h135q37 167 112 257q117 141 297 141q242 0 354 -189q60 -103 66 -209h-181q0 55 -25.5 99t-63.5 68t-75 36.5t-67 12.5q-24 0 -52.5 -10t-62.5 -32t-65.5 -67t-50.5 -107h379l-100 -100h-300q-6 -46 -6 -100h406l-100 -100 h-300q9 -74 33 -132t52.5 -91t62 -54.5t59 -29t46.5 -7.5q29 0 66 13t75 37t63.5 67.5t25.5 96.5h174q-31 -172 -128 -278q-107 -117 -274 -117q-205 0 -324 158q-36 46 -69 131.5t-45 205.5h-217z" />
|
||||||
|
<glyph unicode="−" d="M200 400h900v300h-900v-300z" />
|
||||||
|
<glyph unicode="◼" horiz-adv-x="500" d="M0 0z" />
|
||||||
|
<glyph unicode="☁" d="M-14 494q0 -80 56.5 -137t135.5 -57h750q120 0 205 86.5t85 207.5t-85 207t-205 86q-46 0 -90 -14q-44 97 -134.5 156.5t-200.5 59.5q-152 0 -260 -107.5t-108 -260.5q0 -25 2 -37q-66 -14 -108.5 -67.5t-42.5 -122.5z" />
|
||||||
|
<glyph unicode="✉" d="M0 100l400 400l200 -200l200 200l400 -400h-1200zM0 300v600l300 -300zM0 1100l600 -603l600 603h-1200zM900 600l300 300v-600z" />
|
||||||
|
<glyph unicode="✏" d="M-13 -13l333 112l-223 223zM187 403l214 -214l614 614l-214 214zM887 1103l214 -214l99 92q13 13 13 32.5t-13 33.5l-153 153q-15 13 -33 13t-33 -13z" />
|
||||||
|
<glyph unicode="" d="M0 1200h1200l-500 -550v-550h300v-100h-800v100h300v550z" />
|
||||||
|
<glyph unicode="" d="M14 84q18 -55 86 -75.5t147 5.5q65 21 109 69t44 90v606l600 155v-521q-64 16 -138 -7q-79 -26 -122.5 -83t-25.5 -111q18 -55 86 -75.5t147 4.5q70 23 111.5 63.5t41.5 95.5v881q0 10 -7 15.5t-17 2.5l-752 -193q-10 -3 -17 -12.5t-7 -19.5v-689q-64 17 -138 -7 q-79 -25 -122.5 -82t-25.5 -112z" />
|
||||||
|
<glyph unicode="" d="M23 693q0 200 142 342t342 142t342 -142t142 -342q0 -142 -78 -261l300 -300q7 -8 7 -18t-7 -18l-109 -109q-8 -7 -18 -7t-18 7l-300 300q-119 -78 -261 -78q-200 0 -342 142t-142 342zM176 693q0 -136 97 -233t234 -97t233.5 96.5t96.5 233.5t-96.5 233.5t-233.5 96.5 t-234 -97t-97 -233z" />
|
||||||
|
<glyph unicode="" d="M100 784q0 64 28 123t73 100.5t104.5 64t119 20.5t120 -38.5t104.5 -104.5q48 69 109.5 105t121.5 38t118.5 -20.5t102.5 -64t71 -100.5t27 -123q0 -57 -33.5 -117.5t-94 -124.5t-126.5 -127.5t-150 -152.5t-146 -174q-62 85 -145.5 174t-149.5 152.5t-126.5 127.5 t-94 124.5t-33.5 117.5z" />
|
||||||
|
<glyph unicode="" d="M-72 800h479l146 400h2l146 -400h472l-382 -278l145 -449l-384 275l-382 -275l146 447zM168 71l2 1z" />
|
||||||
|
<glyph unicode="" d="M-72 800h479l146 400h2l146 -400h472l-382 -278l145 -449l-384 275l-382 -275l146 447zM168 71l2 1zM237 700l196 -142l-73 -226l192 140l195 -141l-74 229l193 140h-235l-77 211l-78 -211h-239z" />
|
||||||
|
<glyph unicode="" d="M0 0v143l400 257v100q-37 0 -68.5 74.5t-31.5 125.5v200q0 124 88 212t212 88t212 -88t88 -212v-200q0 -51 -31.5 -125.5t-68.5 -74.5v-100l400 -257v-143h-1200z" />
|
||||||
|
<glyph unicode="" d="M0 0v1100h1200v-1100h-1200zM100 100h100v100h-100v-100zM100 300h100v100h-100v-100zM100 500h100v100h-100v-100zM100 700h100v100h-100v-100zM100 900h100v100h-100v-100zM300 100h600v400h-600v-400zM300 600h600v400h-600v-400zM1000 100h100v100h-100v-100z M1000 300h100v100h-100v-100zM1000 500h100v100h-100v-100zM1000 700h100v100h-100v-100zM1000 900h100v100h-100v-100z" />
|
||||||
|
<glyph unicode="" d="M0 50v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5zM0 650v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5zM600 50v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5zM600 650v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400 q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5z" />
|
||||||
|
<glyph unicode="" d="M0 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM0 450v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200 q-21 0 -35.5 14.5t-14.5 35.5zM0 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5 t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 450v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5 v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM800 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM800 450v200q0 21 14.5 35.5t35.5 14.5h200 q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM800 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5z" />
|
||||||
|
<glyph unicode="" d="M0 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM0 450q0 -21 14.5 -35.5t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v200q0 21 -14.5 35.5t-35.5 14.5h-200q-21 0 -35.5 -14.5 t-14.5 -35.5v-200zM0 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 50v200q0 21 14.5 35.5t35.5 14.5h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5 t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5zM400 450v200q0 21 14.5 35.5t35.5 14.5h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5zM400 850v200q0 21 14.5 35.5t35.5 14.5h700q21 0 35.5 -14.5t14.5 -35.5 v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5z" />
|
||||||
|
<glyph unicode="" d="M29 454l419 -420l818 820l-212 212l-607 -607l-206 207z" />
|
||||||
|
<glyph unicode="" d="M106 318l282 282l-282 282l212 212l282 -282l282 282l212 -212l-282 -282l282 -282l-212 -212l-282 282l-282 -282z" />
|
||||||
|
<glyph unicode="" d="M23 693q0 200 142 342t342 142t342 -142t142 -342q0 -142 -78 -261l300 -300q7 -8 7 -18t-7 -18l-109 -109q-8 -7 -18 -7t-18 7l-300 300q-119 -78 -261 -78q-200 0 -342 142t-142 342zM176 693q0 -136 97 -233t234 -97t233.5 96.5t96.5 233.5t-96.5 233.5t-233.5 96.5 t-234 -97t-97 -233zM300 600v200h100v100h200v-100h100v-200h-100v-100h-200v100h-100z" />
|
||||||
|
<glyph unicode="" d="M23 694q0 200 142 342t342 142t342 -142t142 -342q0 -141 -78 -262l300 -299q7 -7 7 -18t-7 -18l-109 -109q-8 -8 -18 -8t-18 8l-300 300q-119 -78 -261 -78q-200 0 -342 142t-142 342zM176 694q0 -136 97 -233t234 -97t233.5 97t96.5 233t-96.5 233t-233.5 97t-234 -97 t-97 -233zM300 601h400v200h-400v-200z" />
|
||||||
|
<glyph unicode="" d="M23 600q0 183 105 331t272 210v-166q-103 -55 -165 -155t-62 -220q0 -177 125 -302t302 -125t302 125t125 302q0 120 -62 220t-165 155v166q167 -62 272 -210t105 -331q0 -118 -45.5 -224.5t-123 -184t-184 -123t-224.5 -45.5t-224.5 45.5t-184 123t-123 184t-45.5 224.5 zM500 750q0 -21 14.5 -35.5t35.5 -14.5h100q21 0 35.5 14.5t14.5 35.5v400q0 21 -14.5 35.5t-35.5 14.5h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-400z" />
|
||||||
|
<glyph unicode="" d="M100 1h200v300h-200v-300zM400 1v500h200v-500h-200zM700 1v800h200v-800h-200zM1000 1v1200h200v-1200h-200z" />
|
||||||
|
<glyph unicode="" d="M26 601q0 -33 6 -74l151 -38l2 -6q14 -49 38 -93l3 -5l-80 -134q45 -59 105 -105l133 81l5 -3q45 -26 94 -39l5 -2l38 -151q40 -5 74 -5q27 0 74 5l38 151l6 2q46 13 93 39l5 3l134 -81q56 44 104 105l-80 134l3 5q24 44 39 93l1 6l152 38q5 40 5 74q0 28 -5 73l-152 38 l-1 6q-16 51 -39 93l-3 5l80 134q-44 58 -104 105l-134 -81l-5 3q-45 25 -93 39l-6 1l-38 152q-40 5 -74 5q-27 0 -74 -5l-38 -152l-5 -1q-50 -14 -94 -39l-5 -3l-133 81q-59 -47 -105 -105l80 -134l-3 -5q-25 -47 -38 -93l-2 -6l-151 -38q-6 -48 -6 -73zM385 601 q0 88 63 151t152 63t152 -63t63 -151q0 -89 -63 -152t-152 -63t-152 63t-63 152z" />
|
||||||
|
<glyph unicode="" d="M100 1025v50q0 10 7.5 17.5t17.5 7.5h275v100q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5v-100h275q10 0 17.5 -7.5t7.5 -17.5v-50q0 -11 -7 -18t-18 -7h-1050q-11 0 -18 7t-7 18zM200 100v800h900v-800q0 -41 -29.5 -71t-70.5 -30h-700q-41 0 -70.5 30 t-29.5 71zM300 100h100v700h-100v-700zM500 100h100v700h-100v-700zM500 1100h300v100h-300v-100zM700 100h100v700h-100v-700zM900 100h100v700h-100v-700z" />
|
||||||
|
<glyph unicode="" d="M1 601l656 644l644 -644h-200v-600h-300v400h-300v-400h-300v600h-200z" />
|
||||||
|
<glyph unicode="" d="M100 25v1150q0 11 7 18t18 7h475v-500h400v-675q0 -11 -7 -18t-18 -7h-850q-11 0 -18 7t-7 18zM700 800v300l300 -300h-300z" />
|
||||||
|
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM500 500v400h100 v-300h200v-100h-300z" />
|
||||||
|
<glyph unicode="" d="M-100 0l431 1200h209l-21 -300h162l-20 300h208l431 -1200h-538l-41 400h-242l-40 -400h-539zM488 500h224l-27 300h-170z" />
|
||||||
|
<glyph unicode="" d="M0 0v400h490l-290 300h200v500h300v-500h200l-290 -300h490v-400h-1100zM813 200h175v100h-175v-100z" />
|
||||||
|
<glyph unicode="" d="M1 600q0 122 47.5 233t127.5 191t191 127.5t233 47.5t233 -47.5t191 -127.5t127.5 -191t47.5 -233t-47.5 -233t-127.5 -191t-191 -127.5t-233 -47.5t-233 47.5t-191 127.5t-127.5 191t-47.5 233zM188 600q0 -170 121 -291t291 -121t291 121t121 291t-121 291t-291 121 t-291 -121t-121 -291zM350 600h150v300h200v-300h150l-250 -300z" />
|
||||||
|
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM350 600l250 300 l250 -300h-150v-300h-200v300h-150z" />
|
||||||
|
<glyph unicode="" d="M0 25v475l200 700h800l199 -700l1 -475q0 -11 -7 -18t-18 -7h-1150q-11 0 -18 7t-7 18zM200 500h200l50 -200h300l50 200h200l-97 500h-606z" />
|
||||||
|
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -172 121.5 -293t292.5 -121t292.5 121t121.5 293q0 171 -121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM500 397v401 l297 -200z" />
|
||||||
|
<glyph unicode="" d="M23 600q0 -118 45.5 -224.5t123 -184t184 -123t224.5 -45.5t224.5 45.5t184 123t123 184t45.5 224.5h-150q0 -177 -125 -302t-302 -125t-302 125t-125 302t125 302t302 125q136 0 246 -81l-146 -146h400v400l-145 -145q-157 122 -355 122q-118 0 -224.5 -45.5t-184 -123 t-123 -184t-45.5 -224.5z" />
|
||||||
|
<glyph unicode="" d="M23 600q0 118 45.5 224.5t123 184t184 123t224.5 45.5q198 0 355 -122l145 145v-400h-400l147 147q-112 80 -247 80q-177 0 -302 -125t-125 -302h-150zM100 0v400h400l-147 -147q112 -80 247 -80q177 0 302 125t125 302h150q0 -118 -45.5 -224.5t-123 -184t-184 -123 t-224.5 -45.5q-198 0 -355 122z" />
|
||||||
|
<glyph unicode="" d="M100 0h1100v1200h-1100v-1200zM200 100v900h900v-900h-900zM300 200v100h100v-100h-100zM300 400v100h100v-100h-100zM300 600v100h100v-100h-100zM300 800v100h100v-100h-100zM500 200h500v100h-500v-100zM500 400v100h500v-100h-500zM500 600v100h500v-100h-500z M500 800v100h500v-100h-500z" />
|
||||||
|
<glyph unicode="" d="M0 100v600q0 41 29.5 70.5t70.5 29.5h100v200q0 82 59 141t141 59h300q82 0 141 -59t59 -141v-200h100q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-900q-41 0 -70.5 29.5t-29.5 70.5zM400 800h300v150q0 21 -14.5 35.5t-35.5 14.5h-200 q-21 0 -35.5 -14.5t-14.5 -35.5v-150z" />
|
||||||
|
<glyph unicode="" d="M100 0v1100h100v-1100h-100zM300 400q60 60 127.5 84t127.5 17.5t122 -23t119 -30t110 -11t103 42t91 120.5v500q-40 -81 -101.5 -115.5t-127.5 -29.5t-138 25t-139.5 40t-125.5 25t-103 -29.5t-65 -115.5v-500z" />
|
||||||
|
<glyph unicode="" d="M0 275q0 -11 7 -18t18 -7h50q11 0 18 7t7 18v300q0 127 70.5 231.5t184.5 161.5t245 57t245 -57t184.5 -161.5t70.5 -231.5v-300q0 -11 7 -18t18 -7h50q11 0 18 7t7 18v300q0 116 -49.5 227t-131 192.5t-192.5 131t-227 49.5t-227 -49.5t-192.5 -131t-131 -192.5 t-49.5 -227v-300zM200 20v460q0 8 6 14t14 6h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14zM800 20v460q0 8 6 14t14 6h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14z" />
|
||||||
|
<glyph unicode="" d="M0 400h300l300 -200v800l-300 -200h-300v-400zM688 459l141 141l-141 141l71 71l141 -141l141 141l71 -71l-141 -141l141 -141l-71 -71l-141 141l-141 -141z" />
|
||||||
|
<glyph unicode="" d="M0 400h300l300 -200v800l-300 -200h-300v-400zM700 857l69 53q111 -135 111 -310q0 -169 -106 -302l-67 54q86 110 86 248q0 146 -93 257z" />
|
||||||
|
<glyph unicode="" d="M0 401v400h300l300 200v-800l-300 200h-300zM702 858l69 53q111 -135 111 -310q0 -170 -106 -303l-67 55q86 110 86 248q0 145 -93 257zM889 951l7 -8q123 -151 123 -344q0 -189 -119 -339l-7 -8l81 -66l6 8q142 178 142 405q0 230 -144 408l-6 8z" />
|
||||||
|
<glyph unicode="" d="M0 0h500v500h-200v100h-100v-100h-200v-500zM0 600h100v100h400v100h100v100h-100v300h-500v-600zM100 100v300h300v-300h-300zM100 800v300h300v-300h-300zM200 200v100h100v-100h-100zM200 900h100v100h-100v-100zM500 500v100h300v-300h200v-100h-100v-100h-200v100 h-100v100h100v200h-200zM600 0v100h100v-100h-100zM600 1000h100v-300h200v-300h300v200h-200v100h200v500h-600v-200zM800 800v300h300v-300h-300zM900 0v100h300v-100h-300zM900 900v100h100v-100h-100zM1100 200v100h100v-100h-100z" />
|
||||||
|
<glyph unicode="" d="M0 200h100v1000h-100v-1000zM100 0v100h300v-100h-300zM200 200v1000h100v-1000h-100zM500 0v91h100v-91h-100zM500 200v1000h200v-1000h-200zM700 0v91h100v-91h-100zM800 200v1000h100v-1000h-100zM900 0v91h200v-91h-200zM1000 200v1000h200v-1000h-200z" />
|
||||||
|
<glyph unicode="" d="M0 700l1 475q0 10 7.5 17.5t17.5 7.5h474l700 -700l-500 -500zM148 953q0 -42 29 -71q30 -30 71.5 -30t71.5 30q29 29 29 71t-29 71q-30 30 -71.5 30t-71.5 -30q-29 -29 -29 -71z" />
|
||||||
|
<glyph unicode="" d="M1 700l1 475q0 11 7 18t18 7h474l700 -700l-500 -500zM148 953q0 -42 30 -71q29 -30 71 -30t71 30q30 29 30 71t-30 71q-29 30 -71 30t-71 -30q-30 -29 -30 -71zM701 1200h100l700 -700l-500 -500l-50 50l450 450z" />
|
||||||
|
<glyph unicode="" d="M100 0v1025l175 175h925v-1000l-100 -100v1000h-750l-100 -100h750v-1000h-900z" />
|
||||||
|
<glyph unicode="" d="M200 0l450 444l450 -443v1150q0 20 -14.5 35t-35.5 15h-800q-21 0 -35.5 -15t-14.5 -35v-1151z" />
|
||||||
|
<glyph unicode="" d="M0 100v700h200l100 -200h600l100 200h200v-700h-200v200h-800v-200h-200zM253 829l40 -124h592l62 124l-94 346q-2 11 -10 18t-18 7h-450q-10 0 -18 -7t-10 -18zM281 24l38 152q2 10 11.5 17t19.5 7h500q10 0 19.5 -7t11.5 -17l38 -152q2 -10 -3.5 -17t-15.5 -7h-600 q-10 0 -15.5 7t-3.5 17z" />
|
||||||
|
<glyph unicode="" d="M0 200q0 -41 29.5 -70.5t70.5 -29.5h1000q41 0 70.5 29.5t29.5 70.5v600q0 41 -29.5 70.5t-70.5 29.5h-150q-4 8 -11.5 21.5t-33 48t-53 61t-69 48t-83.5 21.5h-200q-41 0 -82 -20.5t-70 -50t-52 -59t-34 -50.5l-12 -20h-150q-41 0 -70.5 -29.5t-29.5 -70.5v-600z M356 500q0 100 72 172t172 72t172 -72t72 -172t-72 -172t-172 -72t-172 72t-72 172zM494 500q0 -44 31 -75t75 -31t75 31t31 75t-31 75t-75 31t-75 -31t-31 -75zM900 700v100h100v-100h-100z" />
|
||||||
|
<glyph unicode="" d="M53 0h365v66q-41 0 -72 11t-49 38t1 71l92 234h391l82 -222q16 -45 -5.5 -88.5t-74.5 -43.5v-66h417v66q-34 1 -74 43q-18 19 -33 42t-21 37l-6 13l-385 998h-93l-399 -1006q-24 -48 -52 -75q-12 -12 -33 -25t-36 -20l-15 -7v-66zM416 521l178 457l46 -140l116 -317h-340 z" />
|
||||||
|
<glyph unicode="" d="M100 0v89q41 7 70.5 32.5t29.5 65.5v827q0 28 -1 39.5t-5.5 26t-15.5 21t-29 14t-49 14.5v71l471 -1q120 0 213 -88t93 -228q0 -55 -11.5 -101.5t-28 -74t-33.5 -47.5t-28 -28l-12 -7q8 -3 21.5 -9t48 -31.5t60.5 -58t47.5 -91.5t21.5 -129q0 -84 -59 -156.5t-142 -111 t-162 -38.5h-500zM400 200h161q89 0 153 48.5t64 132.5q0 90 -62.5 154.5t-156.5 64.5h-159v-400zM400 700h139q76 0 130 61.5t54 138.5q0 82 -84 130.5t-239 48.5v-379z" />
|
||||||
|
<glyph unicode="" d="M200 0v57q77 7 134.5 40.5t65.5 80.5l173 849q10 56 -10 74t-91 37q-6 1 -10.5 2.5t-9.5 2.5v57h425l2 -57q-33 -8 -62 -25.5t-46 -37t-29.5 -38t-17.5 -30.5l-5 -12l-128 -825q-10 -52 14 -82t95 -36v-57h-500z" />
|
||||||
|
<glyph unicode="" d="M-75 200h75v800h-75l125 167l125 -167h-75v-800h75l-125 -167zM300 900v300h150h700h150v-300h-50q0 29 -8 48.5t-18.5 30t-33.5 15t-39.5 5.5t-50.5 1h-200v-850l100 -50v-100h-400v100l100 50v850h-200q-34 0 -50.5 -1t-40 -5.5t-33.5 -15t-18.5 -30t-8.5 -48.5h-49z " />
|
||||||
|
<glyph unicode="" d="M33 51l167 125v-75h800v75l167 -125l-167 -125v75h-800v-75zM100 901v300h150h700h150v-300h-50q0 29 -8 48.5t-18 30t-33.5 15t-40 5.5t-50.5 1h-200v-650l100 -50v-100h-400v100l100 50v650h-200q-34 0 -50.5 -1t-39.5 -5.5t-33.5 -15t-18.5 -30t-8 -48.5h-50z" />
|
||||||
|
<glyph unicode="" d="M0 50q0 -20 14.5 -35t35.5 -15h1100q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM0 350q0 -20 14.5 -35t35.5 -15h800q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-800q-21 0 -35.5 -14.5t-14.5 -35.5 v-100zM0 650q0 -20 14.5 -35t35.5 -15h1000q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1000q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM0 950q0 -20 14.5 -35t35.5 -15h600q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-600q-21 0 -35.5 -14.5 t-14.5 -35.5v-100z" />
|
||||||
|
<glyph unicode="" d="M0 50q0 -20 14.5 -35t35.5 -15h1100q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM0 650q0 -20 14.5 -35t35.5 -15h1100q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5 v-100zM200 350q0 -20 14.5 -35t35.5 -15h700q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-700q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM200 950q0 -20 14.5 -35t35.5 -15h700q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-700q-21 0 -35.5 -14.5 t-14.5 -35.5v-100z" />
|
||||||
|
<glyph unicode="" d="M0 50v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15t-14.5 35zM100 650v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1000q-21 0 -35.5 15 t-14.5 35zM300 350v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800q-21 0 -35.5 15t-14.5 35zM500 950v100q0 21 14.5 35.5t35.5 14.5h600q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-600 q-21 0 -35.5 15t-14.5 35z" />
|
||||||
|
<glyph unicode="" d="M0 50v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15t-14.5 35zM0 350v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15 t-14.5 35zM0 650v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15t-14.5 35zM0 950v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100 q-21 0 -35.5 15t-14.5 35z" />
|
||||||
|
<glyph unicode="" d="M0 50v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15t-14.5 35zM0 350v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15 t-14.5 35zM0 650v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15t-14.5 35zM0 950v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15 t-14.5 35zM300 50v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800q-21 0 -35.5 15t-14.5 35zM300 350v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800 q-21 0 -35.5 15t-14.5 35zM300 650v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800q-21 0 -35.5 15t-14.5 35zM300 950v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15 h-800q-21 0 -35.5 15t-14.5 35z" />
|
||||||
|
<glyph unicode="" d="M-101 500v100h201v75l166 -125l-166 -125v75h-201zM300 0h100v1100h-100v-1100zM500 50q0 -20 14.5 -35t35.5 -15h600q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-600q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM500 350q0 -20 14.5 -35t35.5 -15h300q20 0 35 15t15 35 v100q0 21 -15 35.5t-35 14.5h-300q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM500 650q0 -20 14.5 -35t35.5 -15h500q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM500 950q0 -20 14.5 -35t35.5 -15h100q20 0 35 15t15 35v100 q0 21 -15 35.5t-35 14.5h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-100z" />
|
||||||
|
<glyph unicode="" d="M1 50q0 -20 14.5 -35t35.5 -15h600q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-600q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM1 350q0 -20 14.5 -35t35.5 -15h300q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-300q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM1 650 q0 -20 14.5 -35t35.5 -15h500q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM1 950q0 -20 14.5 -35t35.5 -15h100q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM801 0v1100h100v-1100 h-100zM934 550l167 -125v75h200v100h-200v75z" />
|
||||||
|
<glyph unicode="" d="M0 275v650q0 31 22 53t53 22h750q31 0 53 -22t22 -53v-650q0 -31 -22 -53t-53 -22h-750q-31 0 -53 22t-22 53zM900 600l300 300v-600z" />
|
||||||
|
<glyph unicode="" d="M0 44v1012q0 18 13 31t31 13h1112q19 0 31.5 -13t12.5 -31v-1012q0 -18 -12.5 -31t-31.5 -13h-1112q-18 0 -31 13t-13 31zM100 263l247 182l298 -131l-74 156l293 318l236 -288v500h-1000v-737zM208 750q0 56 39 95t95 39t95 -39t39 -95t-39 -95t-95 -39t-95 39t-39 95z " />
|
||||||
|
<glyph unicode="" d="M148 745q0 124 60.5 231.5t165 172t226.5 64.5q123 0 227 -63t164.5 -169.5t60.5 -229.5t-73 -272q-73 -114 -166.5 -237t-150.5 -189l-57 -66q-10 9 -27 26t-66.5 70.5t-96 109t-104 135.5t-100.5 155q-63 139 -63 262zM342 772q0 -107 75.5 -182.5t181.5 -75.5 q107 0 182.5 75.5t75.5 182.5t-75.5 182t-182.5 75t-182 -75.5t-75 -181.5z" />
|
||||||
|
<glyph unicode="" d="M1 600q0 122 47.5 233t127.5 191t191 127.5t233 47.5t233 -47.5t191 -127.5t127.5 -191t47.5 -233t-47.5 -233t-127.5 -191t-191 -127.5t-233 -47.5t-233 47.5t-191 127.5t-127.5 191t-47.5 233zM173 600q0 -177 125.5 -302t301.5 -125v854q-176 0 -301.5 -125 t-125.5 -302z" />
|
||||||
|
<glyph unicode="" d="M117 406q0 94 34 186t88.5 172.5t112 159t115 177t87.5 194.5q21 -71 57.5 -142.5t76 -130.5t83 -118.5t82 -117t70 -116t50 -125.5t18.5 -136q0 -89 -39 -165.5t-102 -126.5t-140 -79.5t-156 -33.5q-114 6 -211.5 53t-161.5 139t-64 210zM243 414q14 -82 59.5 -136 t136.5 -80l16 98q-7 6 -18 17t-34 48t-33 77q-15 73 -14 143.5t10 122.5l9 51q-92 -110 -119.5 -185t-12.5 -156z" />
|
||||||
|
<glyph unicode="" d="M0 400v300q0 165 117.5 282.5t282.5 117.5q366 -6 397 -14l-186 -186h-311q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v125l200 200v-225q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5 t-117.5 282.5zM436 341l161 50l412 412l-114 113l-405 -405zM995 1015l113 -113l113 113l-21 85l-92 28z" />
|
||||||
|
<glyph unicode="" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h261l2 -80q-133 -32 -218 -120h-145q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5l200 153v-53q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5 zM423 524q30 38 81.5 64t103 35.5t99 14t77.5 3.5l29 -1v-209l360 324l-359 318v-216q-7 0 -19 -1t-48 -8t-69.5 -18.5t-76.5 -37t-76.5 -59t-62 -88t-39.5 -121.5z" />
|
||||||
|
<glyph unicode="" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h300q61 0 127 -23l-178 -177h-349q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v69l200 200v-169q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5 t-117.5 282.5zM342 632l283 -284l567 567l-137 137l-430 -431l-146 147z" />
|
||||||
|
<glyph unicode="" d="M0 603l300 296v-198h200v200h-200l300 300l295 -300h-195v-200h200v198l300 -296l-300 -300v198h-200v-200h195l-295 -300l-300 300h200v200h-200v-198z" />
|
||||||
|
<glyph unicode="" d="M200 50v1000q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-437l500 487v-1100l-500 488v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5z" />
|
||||||
|
<glyph unicode="" d="M0 50v1000q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-437l500 487v-487l500 487v-1100l-500 488v-488l-500 488v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5z" />
|
||||||
|
<glyph unicode="" d="M136 550l564 550v-487l500 487v-1100l-500 488v-488z" />
|
||||||
|
<glyph unicode="" d="M200 0l900 550l-900 550v-1100z" />
|
||||||
|
<glyph unicode="" d="M200 150q0 -21 14.5 -35.5t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v800q0 21 -14.5 35.5t-35.5 14.5h-200q-21 0 -35.5 -14.5t-14.5 -35.5v-800zM600 150q0 -21 14.5 -35.5t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v800q0 21 -14.5 35.5t-35.5 14.5h-200 q-21 0 -35.5 -14.5t-14.5 -35.5v-800z" />
|
||||||
|
<glyph unicode="" d="M200 150q0 -20 14.5 -35t35.5 -15h800q21 0 35.5 15t14.5 35v800q0 21 -14.5 35.5t-35.5 14.5h-800q-21 0 -35.5 -14.5t-14.5 -35.5v-800z" />
|
||||||
|
<glyph unicode="" d="M0 0v1100l500 -487v487l564 -550l-564 -550v488z" />
|
||||||
|
<glyph unicode="" d="M0 0v1100l500 -487v487l500 -487v437q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-1000q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v438l-500 -488v488z" />
|
||||||
|
<glyph unicode="" d="M300 0v1100l500 -487v437q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-1000q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v438z" />
|
||||||
|
<glyph unicode="" d="M100 250v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5zM100 500h1100l-550 564z" />
|
||||||
|
<glyph unicode="" d="M185 599l592 -592l240 240l-353 353l353 353l-240 240z" />
|
||||||
|
<glyph unicode="" d="M272 194l353 353l-353 353l241 240l572 -571l21 -22l-1 -1v-1l-592 -591z" />
|
||||||
|
<glyph unicode="" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM300 500h200v-200h200v200h200v200h-200v200h-200v-200h-200v-200z" />
|
||||||
|
<glyph unicode="" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM300 500h600v200h-600v-200z" />
|
||||||
|
<glyph unicode="" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM246 459l213 -213l141 142l141 -142l213 213l-142 141l142 141l-213 212l-141 -141l-141 142l-212 -213l141 -141 z" />
|
||||||
|
<glyph unicode="" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM270 551l276 -277l411 411l-175 174l-236 -236l-102 102z" />
|
||||||
|
<glyph unicode="" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM364 700h143q4 0 11.5 -1t11 -1t6.5 3t3 9t1 11t3.5 8.5t3.5 6t5.5 4t6.5 2.5t9 1.5t9 0.5h11.5h12.5 q19 0 30 -10t11 -26q0 -22 -4 -28t-27 -22q-5 -1 -12.5 -3t-27 -13.5t-34 -27t-26.5 -46t-11 -68.5h200q5 3 14 8t31.5 25.5t39.5 45.5t31 69t14 94q0 51 -17.5 89t-42 58t-58.5 32t-58.5 15t-51.5 3q-50 0 -90.5 -12t-75 -38.5t-53.5 -74.5t-19 -114zM500 300h200v100h-200 v-100z" />
|
||||||
|
<glyph unicode="" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM400 300h400v100h-100v300h-300v-100h100v-200h-100v-100zM500 800h200v100h-200v-100z" />
|
||||||
|
<glyph unicode="" d="M0 500v200h195q31 125 98.5 199.5t206.5 100.5v200h200v-200q54 -20 113 -60t112.5 -105.5t71.5 -134.5h203v-200h-203q-25 -102 -116.5 -186t-180.5 -117v-197h-200v197q-140 27 -208 102.5t-98 200.5h-194zM290 500q24 -73 79.5 -127.5t130.5 -78.5v206h200v-206 q149 48 201 206h-201v200h200q-25 74 -75.5 127t-124.5 77v-204h-200v203q-75 -23 -130 -77t-79 -126h209v-200h-210z" />
|
||||||
|
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM356 465l135 135 l-135 135l109 109l135 -135l135 135l109 -109l-135 -135l135 -135l-109 -109l-135 135l-135 -135z" />
|
||||||
|
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM322 537l141 141 l87 -87l204 205l142 -142l-346 -345z" />
|
||||||
|
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -115 62 -215l568 567q-100 62 -216 62q-171 0 -292.5 -121.5t-121.5 -292.5zM391 245q97 -59 209 -59q171 0 292.5 121.5t121.5 292.5 q0 112 -59 209z" />
|
||||||
|
<glyph unicode="" d="M0 547l600 453v-300h600v-300h-600v-301z" />
|
||||||
|
<glyph unicode="" d="M0 400v300h600v300l600 -453l-600 -448v301h-600z" />
|
||||||
|
<glyph unicode="" d="M204 600l450 600l444 -600h-298v-600h-300v600h-296z" />
|
||||||
|
<glyph unicode="" d="M104 600h296v600h300v-600h298l-449 -600z" />
|
||||||
|
<glyph unicode="" d="M0 200q6 132 41 238.5t103.5 193t184 138t271.5 59.5v271l600 -453l-600 -448v301q-95 -2 -183 -20t-170 -52t-147 -92.5t-100 -135.5z" />
|
||||||
|
<glyph unicode="" d="M0 0v400l129 -129l294 294l142 -142l-294 -294l129 -129h-400zM635 777l142 -142l294 294l129 -129v400h-400l129 -129z" />
|
||||||
|
<glyph unicode="" d="M34 176l295 295l-129 129h400v-400l-129 130l-295 -295zM600 600v400l129 -129l295 295l142 -141l-295 -295l129 -130h-400z" />
|
||||||
|
<glyph unicode="" d="M23 600q0 118 45.5 224.5t123 184t184 123t224.5 45.5t224.5 -45.5t184 -123t123 -184t45.5 -224.5t-45.5 -224.5t-123 -184t-184 -123t-224.5 -45.5t-224.5 45.5t-184 123t-123 184t-45.5 224.5zM456 851l58 -302q4 -20 21.5 -34.5t37.5 -14.5h54q20 0 37.5 14.5 t21.5 34.5l58 302q4 20 -8 34.5t-32 14.5h-207q-21 0 -33 -14.5t-8 -34.5zM500 300h200v100h-200v-100z" />
|
||||||
|
<glyph unicode="" d="M0 800h100v-200h400v300h200v-300h400v200h100v100h-111q1 1 1 6.5t-1.5 15t-3.5 17.5l-34 172q-11 39 -41.5 63t-69.5 24q-32 0 -61 -17l-239 -144q-22 -13 -40 -35q-19 24 -40 36l-238 144q-33 18 -62 18q-39 0 -69.5 -23t-40.5 -61l-35 -177q-2 -8 -3 -18t-1 -15v-6 h-111v-100zM100 0h400v400h-400v-400zM200 900q-3 0 14 48t36 96l18 47l213 -191h-281zM700 0v400h400v-400h-400zM731 900l202 197q5 -12 12 -32.5t23 -64t25 -72t7 -28.5h-269z" />
|
||||||
|
<glyph unicode="" d="M0 -22v143l216 193q-9 53 -13 83t-5.5 94t9 113t38.5 114t74 124q47 60 99.5 102.5t103 68t127.5 48t145.5 37.5t184.5 43.5t220 58.5q0 -189 -22 -343t-59 -258t-89 -181.5t-108.5 -120t-122 -68t-125.5 -30t-121.5 -1.5t-107.5 12.5t-87.5 17t-56.5 7.5l-99 -55z M238.5 300.5q19.5 -6.5 86.5 76.5q55 66 367 234q70 38 118.5 69.5t102 79t99 111.5t86.5 148q22 50 24 60t-6 19q-7 5 -17 5t-26.5 -14.5t-33.5 -39.5q-35 -51 -113.5 -108.5t-139.5 -89.5l-61 -32q-369 -197 -458 -401q-48 -111 -28.5 -117.5z" />
|
||||||
|
<glyph unicode="" d="M111 408q0 -33 5 -63q9 -56 44 -119.5t105 -108.5q31 -21 64 -16t62 23.5t57 49.5t48 61.5t35 60.5q32 66 39 184.5t-13 157.5q79 -80 122 -164t26 -184q-5 -33 -20.5 -69.5t-37.5 -80.5q-10 -19 -14.5 -29t-12 -26t-9 -23.5t-3 -19t2.5 -15.5t11 -9.5t19.5 -5t30.5 2.5 t42 8q57 20 91 34t87.5 44.5t87 64t65.5 88.5t47 122q38 172 -44.5 341.5t-246.5 278.5q22 -44 43 -129q39 -159 -32 -154q-15 2 -33 9q-79 33 -120.5 100t-44 175.5t48.5 257.5q-13 -8 -34 -23.5t-72.5 -66.5t-88.5 -105.5t-60 -138t-8 -166.5q2 -12 8 -41.5t8 -43t6 -39.5 t3.5 -39.5t-1 -33.5t-6 -31.5t-13.5 -24t-21 -20.5t-31 -12q-38 -10 -67 13t-40.5 61.5t-15 81.5t10.5 75q-52 -46 -83.5 -101t-39 -107t-7.5 -85z" />
|
||||||
|
<glyph unicode="" d="M-61 600l26 40q6 10 20 30t49 63.5t74.5 85.5t97 90t116.5 83.5t132.5 59t145.5 23.5t145.5 -23.5t132.5 -59t116.5 -83.5t97 -90t74.5 -85.5t49 -63.5t20 -30l26 -40l-26 -40q-6 -10 -20 -30t-49 -63.5t-74.5 -85.5t-97 -90t-116.5 -83.5t-132.5 -59t-145.5 -23.5 t-145.5 23.5t-132.5 59t-116.5 83.5t-97 90t-74.5 85.5t-49 63.5t-20 30zM120 600q7 -10 40.5 -58t56 -78.5t68 -77.5t87.5 -75t103 -49.5t125 -21.5t123.5 20t100.5 45.5t85.5 71.5t66.5 75.5t58 81.5t47 66q-1 1 -28.5 37.5t-42 55t-43.5 53t-57.5 63.5t-58.5 54 q49 -74 49 -163q0 -124 -88 -212t-212 -88t-212 88t-88 212q0 85 46 158q-102 -87 -226 -258zM377 656q49 -124 154 -191l105 105q-37 24 -75 72t-57 84l-20 36z" />
|
||||||
|
<glyph unicode="" d="M-61 600l26 40q6 10 20 30t49 63.5t74.5 85.5t97 90t116.5 83.5t132.5 59t145.5 23.5q61 0 121 -17l37 142h148l-314 -1200h-148l37 143q-82 21 -165 71.5t-140 102t-109.5 112t-72 88.5t-29.5 43zM120 600q210 -282 393 -336l37 141q-107 18 -178.5 101.5t-71.5 193.5 q0 85 46 158q-102 -87 -226 -258zM377 656q49 -124 154 -191l47 47l23 87q-30 28 -59 69t-44 68l-14 26zM780 161l38 145q22 15 44.5 34t46 44t40.5 44t41 50.5t33.5 43.5t33 44t24.5 34q-97 127 -140 175l39 146q67 -54 131.5 -125.5t87.5 -103.5t36 -52l26 -40l-26 -40 q-7 -12 -25.5 -38t-63.5 -79.5t-95.5 -102.5t-124 -100t-146.5 -79z" />
|
||||||
|
<glyph unicode="" d="M-97.5 34q13.5 -34 50.5 -34h1294q37 0 50.5 35.5t-7.5 67.5l-642 1056q-20 34 -48 36.5t-48 -29.5l-642 -1066q-21 -32 -7.5 -66zM155 200l445 723l445 -723h-345v100h-200v-100h-345zM500 600l100 -300l100 300v100h-200v-100z" />
|
||||||
|
<glyph unicode="" d="M100 262v41q0 20 11 44.5t26 38.5l363 325v339q0 62 44 106t106 44t106 -44t44 -106v-339l363 -325q15 -14 26 -38.5t11 -44.5v-41q0 -20 -12 -26.5t-29 5.5l-359 249v-263q100 -91 100 -113v-64q0 -20 -13 -28.5t-32 0.5l-94 78h-222l-94 -78q-19 -9 -32 -0.5t-13 28.5 v64q0 22 100 113v263l-359 -249q-17 -12 -29 -5.5t-12 26.5z" />
|
||||||
|
<glyph unicode="" d="M0 50q0 -20 14.5 -35t35.5 -15h1000q21 0 35.5 15t14.5 35v750h-1100v-750zM0 900h1100v150q0 21 -14.5 35.5t-35.5 14.5h-150v100h-100v-100h-500v100h-100v-100h-150q-21 0 -35.5 -14.5t-14.5 -35.5v-150zM100 100v100h100v-100h-100zM100 300v100h100v-100h-100z M100 500v100h100v-100h-100zM300 100v100h100v-100h-100zM300 300v100h100v-100h-100zM300 500v100h100v-100h-100zM500 100v100h100v-100h-100zM500 300v100h100v-100h-100zM500 500v100h100v-100h-100zM700 100v100h100v-100h-100zM700 300v100h100v-100h-100zM700 500 v100h100v-100h-100zM900 100v100h100v-100h-100zM900 300v100h100v-100h-100zM900 500v100h100v-100h-100z" />
|
||||||
|
<glyph unicode="" d="M0 200v200h259l600 600h241v198l300 -295l-300 -300v197h-159l-600 -600h-341zM0 800h259l122 -122l141 142l-181 180h-341v-200zM678 381l141 142l122 -123h159v198l300 -295l-300 -300v197h-241z" />
|
||||||
|
<glyph unicode="" d="M0 400v600q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-596l-304 -300v300h-100q-41 0 -70.5 29.5t-29.5 70.5z" />
|
||||||
|
<glyph unicode="" d="M100 600v200h300v-250q0 -113 6 -145q17 -92 102 -117q39 -11 92 -11q37 0 66.5 5.5t50 15.5t36 24t24 31.5t14 37.5t7 42t2.5 45t0 47v25v250h300v-200q0 -42 -3 -83t-15 -104t-31.5 -116t-58 -109.5t-89 -96.5t-129 -65.5t-174.5 -25.5t-174.5 25.5t-129 65.5t-89 96.5 t-58 109.5t-31.5 116t-15 104t-3 83zM100 900v300h300v-300h-300zM800 900v300h300v-300h-300z" />
|
||||||
|
<glyph unicode="" d="M-30 411l227 -227l352 353l353 -353l226 227l-578 579z" />
|
||||||
|
<glyph unicode="" d="M70 797l580 -579l578 579l-226 227l-353 -353l-352 353z" />
|
||||||
|
<glyph unicode="" d="M-198 700l299 283l300 -283h-203v-400h385l215 -200h-800v600h-196zM402 1000l215 -200h381v-400h-198l299 -283l299 283h-200v600h-796z" />
|
||||||
|
<glyph unicode="" d="M18 939q-5 24 10 42q14 19 39 19h896l38 162q5 17 18.5 27.5t30.5 10.5h94q20 0 35 -14.5t15 -35.5t-15 -35.5t-35 -14.5h-54l-201 -961q-2 -4 -6 -10.5t-19 -17.5t-33 -11h-31v-50q0 -20 -14.5 -35t-35.5 -15t-35.5 15t-14.5 35v50h-300v-50q0 -20 -14.5 -35t-35.5 -15 t-35.5 15t-14.5 35v50h-50q-21 0 -35.5 15t-14.5 35q0 21 14.5 35.5t35.5 14.5h535l48 200h-633q-32 0 -54.5 21t-27.5 43z" />
|
||||||
|
<glyph unicode="" d="M0 0v800h1200v-800h-1200zM0 900v100h200q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5h500v-100h-1200z" />
|
||||||
|
<glyph unicode="" d="M1 0l300 700h1200l-300 -700h-1200zM1 400v600h200q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5h500v-200h-1000z" />
|
||||||
|
<glyph unicode="" d="M302 300h198v600h-198l298 300l298 -300h-198v-600h198l-298 -300z" />
|
||||||
|
<glyph unicode="" d="M0 600l300 298v-198h600v198l300 -298l-300 -297v197h-600v-197z" />
|
||||||
|
<glyph unicode="" d="M0 100v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM31 400l172 739q5 22 23 41.5t38 19.5h672q19 0 37.5 -22.5t23.5 -45.5l172 -732h-1138zM800 100h100v100h-100v-100z M1000 100h100v100h-100v-100z" />
|
||||||
|
<glyph unicode="" d="M-101 600v50q0 24 25 49t50 38l25 13v-250l-11 5.5t-24 14t-30 21.5t-24 27.5t-11 31.5zM100 500v250v8v8v7t0.5 7t1.5 5.5t2 5t3 4t4.5 3.5t6 1.5t7.5 0.5h200l675 250v-850l-675 200h-38l47 -276q2 -12 -3 -17.5t-11 -6t-21 -0.5h-8h-83q-20 0 -34.5 14t-18.5 35 q-55 337 -55 351zM1100 200v850q0 21 14.5 35.5t35.5 14.5q20 0 35 -14.5t15 -35.5v-850q0 -20 -15 -35t-35 -15q-21 0 -35.5 15t-14.5 35z" />
|
||||||
|
<glyph unicode="" d="M74 350q0 21 13.5 35.5t33.5 14.5h18l117 173l63 327q15 77 76 140t144 83l-18 32q-6 19 3 32t29 13h94q20 0 29 -10.5t3 -29.5q-18 -36 -18 -37q83 -19 144 -82.5t76 -140.5l63 -327l118 -173h17q20 0 33.5 -14.5t13.5 -35.5q0 -20 -13 -40t-31 -27q-8 -3 -23 -8.5 t-65 -20t-103 -25t-132.5 -19.5t-158.5 -9q-125 0 -245.5 20.5t-178.5 40.5l-58 20q-18 7 -31 27.5t-13 40.5zM497 110q12 -49 40 -79.5t63 -30.5t63 30.5t39 79.5q-48 -6 -102 -6t-103 6z" />
|
||||||
|
<glyph unicode="" d="M21 445l233 -45l-78 -224l224 78l45 -233l155 179l155 -179l45 233l224 -78l-78 224l234 45l-180 155l180 156l-234 44l78 225l-224 -78l-45 233l-155 -180l-155 180l-45 -233l-224 78l78 -225l-233 -44l179 -156z" />
|
||||||
|
<glyph unicode="" d="M0 200h200v600h-200v-600zM300 275q0 -75 100 -75h61q124 -100 139 -100h250q46 0 83 57l238 344q29 31 29 74v100q0 44 -30.5 84.5t-69.5 40.5h-328q28 118 28 125v150q0 44 -30.5 84.5t-69.5 40.5h-50q-27 0 -51 -20t-38 -48l-96 -198l-145 -196q-20 -26 -20 -63v-400z M400 300v375l150 213l100 212h50v-175l-50 -225h450v-125l-250 -375h-214l-136 100h-100z" />
|
||||||
|
<glyph unicode="" d="M0 400v600h200v-600h-200zM300 525v400q0 75 100 75h61q124 100 139 100h250q46 0 83 -57l238 -344q29 -31 29 -74v-100q0 -44 -30.5 -84.5t-69.5 -40.5h-328q28 -118 28 -125v-150q0 -44 -30.5 -84.5t-69.5 -40.5h-50q-27 0 -51 20t-38 48l-96 198l-145 196 q-20 26 -20 63zM400 525l150 -212l100 -213h50v175l-50 225h450v125l-250 375h-214l-136 -100h-100v-375z" />
|
||||||
|
<glyph unicode="" d="M8 200v600h200v-600h-200zM308 275v525q0 17 14 35.5t28 28.5l14 9l362 230q14 6 25 6q17 0 29 -12l109 -112q14 -14 14 -34q0 -18 -11 -32l-85 -121h302q85 0 138.5 -38t53.5 -110t-54.5 -111t-138.5 -39h-107l-130 -339q-7 -22 -20.5 -41.5t-28.5 -19.5h-341 q-7 0 -90 81t-83 94zM408 289l100 -89h293l131 339q6 21 19.5 41t28.5 20h203q16 0 25 15t9 36q0 20 -9 34.5t-25 14.5h-457h-6.5h-7.5t-6.5 0.5t-6 1t-5 1.5t-5.5 2.5t-4 4t-4 5.5q-5 12 -5 20q0 14 10 27l147 183l-86 83l-339 -236v-503z" />
|
||||||
|
<glyph unicode="" d="M-101 651q0 72 54 110t139 38l302 -1l-85 121q-11 16 -11 32q0 21 14 34l109 113q13 12 29 12q11 0 25 -6l365 -230q7 -4 17 -10.5t26.5 -26t16.5 -36.5v-526q0 -13 -86 -93.5t-94 -80.5h-341q-16 0 -29.5 20t-19.5 41l-130 339h-107q-84 0 -139 39t-55 111zM-1 601h222 q15 0 28.5 -20.5t19.5 -40.5l131 -339h293l107 89v502l-343 237l-87 -83l145 -184q10 -11 10 -26q0 -11 -5 -20q-1 -3 -3.5 -5.5l-4 -4t-5 -2.5t-5.5 -1.5t-6.5 -1t-6.5 -0.5h-7.5h-6.5h-476v-100zM1000 201v600h200v-600h-200z" />
|
||||||
|
<glyph unicode="" d="M97 719l230 -363q4 -6 10.5 -15.5t26 -25t36.5 -15.5h525q13 0 94 83t81 90v342q0 15 -20 28.5t-41 19.5l-339 131v106q0 84 -39 139t-111 55t-110 -53.5t-38 -138.5v-302l-121 84q-15 12 -33.5 11.5t-32.5 -13.5l-112 -110q-22 -22 -6 -53zM172 739l83 86l183 -146 q22 -18 47 -5q3 1 5.5 3.5l4 4t2.5 5t1.5 5.5t1 6.5t0.5 6.5v7.5v6.5v456q0 22 25 31t50 -0.5t25 -30.5v-202q0 -16 20 -29.5t41 -19.5l339 -130v-294l-89 -100h-503zM400 0v200h600v-200h-600z" />
|
||||||
|
<glyph unicode="" d="M2 585q-16 -31 6 -53l112 -110q13 -13 32 -13.5t34 10.5l121 85q0 -51 -0.5 -153.5t-0.5 -148.5q0 -84 38.5 -138t110.5 -54t111 55t39 139v106l339 131q20 6 40.5 19.5t20.5 28.5v342q0 7 -81 90t-94 83h-525q-17 0 -35.5 -14t-28.5 -28l-10 -15zM77 565l236 339h503 l89 -100v-294l-340 -130q-20 -6 -40 -20t-20 -29v-202q0 -22 -25 -31t-50 0t-25 31v456v14.5t-1.5 11.5t-5 12t-9.5 7q-24 13 -46 -5l-184 -146zM305 1104v200h600v-200h-600z" />
|
||||||
|
<glyph unicode="" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q162 0 299.5 -80t217.5 -218t80 -300t-80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM298 701l2 -201h300l-2 -194l402 294l-402 298v-197h-300z" />
|
||||||
|
<glyph unicode="" d="M0 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t231.5 47.5q122 0 232.5 -47.5t190.5 -127.5t127.5 -190.5t47.5 -232.5q0 -162 -80 -299.5t-218 -217.5t-300 -80t-299.5 80t-217.5 217.5t-80 299.5zM200 600l402 -294l-2 194h300l2 201h-300v197z" />
|
||||||
|
<glyph unicode="" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q162 0 299.5 -80t217.5 -218t80 -300t-80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM300 600h200v-300h200v300h200l-300 400z" />
|
||||||
|
<glyph unicode="" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q162 0 299.5 -80t217.5 -218t80 -300t-80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM300 600l300 -400l300 400h-200v300h-200v-300h-200z" />
|
||||||
|
<glyph unicode="" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q121 0 231.5 -47.5t190.5 -127.5t127.5 -190.5t47.5 -232.5q0 -162 -80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM254 780q-8 -33 5.5 -92.5t7.5 -87.5q0 -9 17 -44t16 -60 q12 0 23 -5.5t23 -15t20 -13.5q24 -12 108 -42q22 -8 53 -31.5t59.5 -38.5t57.5 -11q8 -18 -15 -55t-20 -57q42 -71 87 -80q0 -6 -3 -15.5t-3.5 -14.5t4.5 -17q104 -3 221 112q30 29 47 47t34.5 49t20.5 62q-14 9 -37 9.5t-36 7.5q-14 7 -49 15t-52 19q-9 0 -39.5 -0.5 t-46.5 -1.5t-39 -6.5t-39 -16.5q-50 -35 -66 -12q-4 2 -3.5 25.5t0.5 25.5q-6 13 -26.5 17t-24.5 7q2 22 -2 41t-16.5 28t-38.5 -20q-23 -25 -42 4q-19 28 -8 58q6 16 22 22q6 -1 26 -1.5t33.5 -4t19.5 -13.5q12 -19 32 -37.5t34 -27.5l14 -8q0 3 9.5 39.5t5.5 57.5 q-4 23 14.5 44.5t22.5 31.5q5 14 10 35t8.5 31t15.5 22.5t34 21.5q-6 18 10 37q8 0 23.5 -1.5t24.5 -1.5t20.5 4.5t20.5 15.5q-10 23 -30.5 42.5t-38 30t-49 26.5t-43.5 23q11 39 2 44q31 -13 58 -14.5t39 3.5l11 4q7 36 -16.5 53.5t-64.5 28.5t-56 23q-19 -3 -37 0 q-15 -12 -36.5 -21t-34.5 -12t-44 -8t-39 -6q-15 -3 -45.5 0.5t-45.5 -2.5q-21 -7 -52 -26.5t-34 -34.5q-3 -11 6.5 -22.5t8.5 -18.5q-3 -34 -27.5 -90.5t-29.5 -79.5zM518 916q3 12 16 30t16 25q10 -10 18.5 -10t14 6t14.5 14.5t16 12.5q0 -24 17 -66.5t17 -43.5 q-9 2 -31 5t-36 5t-32 8t-30 14zM692 1003h1h-1z" />
|
||||||
|
<glyph unicode="" d="M0 164.5q0 21.5 15 37.5l600 599q-33 101 6 201.5t135 154.5q164 92 306 -9l-259 -138l145 -232l251 126q13 -175 -151 -267q-123 -70 -253 -23l-596 -596q-15 -16 -36.5 -16t-36.5 16l-111 110q-15 15 -15 36.5z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="1220" d="M0 196v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM0 596v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000 q-41 0 -70.5 29.5t-29.5 70.5zM0 996v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM600 596h500v100h-500v-100zM800 196h300v100h-300v-100zM900 996h200v100h-200v-100z" />
|
||||||
|
<glyph unicode="" d="M100 1100v100h1000v-100h-1000zM150 1000h900l-350 -500v-300l-200 -200v500z" />
|
||||||
|
<glyph unicode="" d="M0 200v200h1200v-200q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM0 500v400q0 41 29.5 70.5t70.5 29.5h300v100q0 41 29.5 70.5t70.5 29.5h200q41 0 70.5 -29.5t29.5 -70.5v-100h300q41 0 70.5 -29.5t29.5 -70.5v-400h-500v100h-200v-100h-500z M500 1000h200v100h-200v-100z" />
|
||||||
|
<glyph unicode="" d="M0 0v400l129 -129l200 200l142 -142l-200 -200l129 -129h-400zM0 800l129 129l200 -200l142 142l-200 200l129 129h-400v-400zM729 329l142 142l200 -200l129 129v-400h-400l129 129zM729 871l200 200l-129 129h400v-400l-129 129l-200 -200z" />
|
||||||
|
<glyph unicode="" d="M0 596q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM182 596q0 -172 121.5 -293t292.5 -121t292.5 121t121.5 293q0 171 -121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM291 655 q0 23 15.5 38.5t38.5 15.5t39 -16t16 -38q0 -23 -16 -39t-39 -16q-22 0 -38 16t-16 39zM400 850q0 22 16 38.5t39 16.5q22 0 38 -16t16 -39t-16 -39t-38 -16q-23 0 -39 16.5t-16 38.5zM514 609q0 32 20.5 56.5t51.5 29.5l122 126l1 1q-9 14 -9 28q0 22 16 38.5t39 16.5 q22 0 38 -16t16 -39t-16 -39t-38 -16q-14 0 -29 10l-55 -145q17 -22 17 -51q0 -36 -25.5 -61.5t-61.5 -25.5t-61.5 25.5t-25.5 61.5zM800 655q0 22 16 38t39 16t38.5 -15.5t15.5 -38.5t-16 -39t-38 -16q-23 0 -39 16t-16 39z" />
|
||||||
|
<glyph unicode="" d="M-40 375q-13 -95 35 -173q35 -57 94 -89t129 -32q63 0 119 28q33 16 65 40.5t52.5 45.5t59.5 64q40 44 57 61l394 394q35 35 47 84t-3 96q-27 87 -117 104q-20 2 -29 2q-46 0 -78.5 -16.5t-67.5 -51.5l-389 -396l-7 -7l69 -67l377 373q20 22 39 38q23 23 50 23 q38 0 53 -36q16 -39 -20 -75l-547 -547q-52 -52 -125 -52q-55 0 -100 33t-54 96q-5 35 2.5 66t31.5 63t42 50t56 54q24 21 44 41l348 348q52 52 82.5 79.5t84 54t107.5 26.5q25 0 48 -4q95 -17 154 -94.5t51 -175.5q-7 -101 -98 -192l-252 -249l-253 -256l7 -7l69 -60 l517 511q67 67 95 157t11 183q-16 87 -67 154t-130 103q-69 33 -152 33q-107 0 -197 -55q-40 -24 -111 -95l-512 -512q-68 -68 -81 -163z" />
|
||||||
|
<glyph unicode="" d="M80 784q0 131 98.5 229.5t230.5 98.5q143 0 241 -129q103 129 246 129q129 0 226 -98.5t97 -229.5q0 -46 -17.5 -91t-61 -99t-77 -89.5t-104.5 -105.5q-197 -191 -293 -322l-17 -23l-16 23q-43 58 -100 122.5t-92 99.5t-101 100q-71 70 -104.5 105.5t-77 89.5t-61 99 t-17.5 91zM250 784q0 -27 30.5 -70t61.5 -75.5t95 -94.5l22 -22q93 -90 190 -201q82 92 195 203l12 12q64 62 97.5 97t64.5 79t31 72q0 71 -48 119.5t-105 48.5q-74 0 -132 -83l-118 -171l-114 174q-51 80 -123 80q-60 0 -109.5 -49.5t-49.5 -118.5z" />
|
||||||
|
<glyph unicode="" d="M57 353q0 -95 66 -159l141 -142q68 -66 159 -66q93 0 159 66l283 283q66 66 66 159t-66 159l-141 141q-8 9 -19 17l-105 -105l212 -212l-389 -389l-247 248l95 95l-18 18q-46 45 -75 101l-55 -55q-66 -66 -66 -159zM269 706q0 -93 66 -159l141 -141q7 -7 19 -17l105 105 l-212 212l389 389l247 -247l-95 -96l18 -17q47 -49 77 -100l29 29q35 35 62.5 88t27.5 96q0 93 -66 159l-141 141q-66 66 -159 66q-95 0 -159 -66l-283 -283q-66 -64 -66 -159z" />
|
||||||
|
<glyph unicode="" d="M200 100v953q0 21 30 46t81 48t129 38t163 15t162 -15t127 -38t79 -48t29 -46v-953q0 -41 -29.5 -70.5t-70.5 -29.5h-600q-41 0 -70.5 29.5t-29.5 70.5zM300 300h600v700h-600v-700zM496 150q0 -43 30.5 -73.5t73.5 -30.5t73.5 30.5t30.5 73.5t-30.5 73.5t-73.5 30.5 t-73.5 -30.5t-30.5 -73.5z" />
|
||||||
|
<glyph unicode="" d="M0 0l303 380l207 208l-210 212h300l267 279l-35 36q-15 14 -15 35t15 35q14 15 35 15t35 -15l283 -282q15 -15 15 -36t-15 -35q-14 -15 -35 -15t-35 15l-36 35l-279 -267v-300l-212 210l-208 -207z" />
|
||||||
|
<glyph unicode="" d="M295 433h139q5 -77 48.5 -126.5t117.5 -64.5v335q-6 1 -15.5 4t-11.5 3q-46 14 -79 26.5t-72 36t-62.5 52t-40 72.5t-16.5 99q0 92 44 159.5t109 101t144 40.5v78h100v-79q38 -4 72.5 -13.5t75.5 -31.5t71 -53.5t51.5 -84t24.5 -118.5h-159q-8 72 -35 109.5t-101 50.5 v-307l64 -14q34 -7 64 -16.5t70 -31.5t67.5 -52t47.5 -80.5t20 -112.5q0 -139 -89 -224t-244 -96v-77h-100v78q-152 17 -237 104q-40 40 -52.5 93.5t-15.5 139.5zM466 889q0 -29 8 -51t16.5 -34t29.5 -22.5t31 -13.5t38 -10q7 -2 11 -3v274q-61 -8 -97.5 -37.5t-36.5 -102.5 zM700 237q170 18 170 151q0 64 -44 99.5t-126 60.5v-311z" />
|
||||||
|
<glyph unicode="" d="M100 600v100h166q-24 49 -44 104q-10 26 -14.5 55.5t-3 72.5t25 90t68.5 87q97 88 263 88q129 0 230 -89t101 -208h-153q0 52 -34 89.5t-74 51.5t-76 14q-37 0 -79 -14.5t-62 -35.5q-41 -44 -41 -101q0 -28 16.5 -69.5t28 -62.5t41.5 -72h241v-100h-197q8 -50 -2.5 -115 t-31.5 -94q-41 -59 -99 -113q35 11 84 18t70 7q33 1 103 -16t103 -17q76 0 136 30l50 -147q-41 -25 -80.5 -36.5t-59 -13t-61.5 -1.5q-23 0 -128 33t-155 29q-39 -4 -82 -17t-66 -25l-24 -11l-55 145l16.5 11t15.5 10t13.5 9.5t14.5 12t14.5 14t17.5 18.5q48 55 54 126.5 t-30 142.5h-221z" />
|
||||||
|
<glyph unicode="" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM602 900l298 300l298 -300h-198v-900h-200v900h-198z" />
|
||||||
|
<glyph unicode="" d="M2 300h198v900h200v-900h198l-298 -300zM700 0v200h100v-100h200v-100h-300zM700 400v100h300v-200h-99v-100h-100v100h99v100h-200zM700 700v500h300v-500h-100v100h-100v-100h-100zM801 900h100v200h-100v-200z" />
|
||||||
|
<glyph unicode="" d="M2 300h198v900h200v-900h198l-298 -300zM700 0v500h300v-500h-100v100h-100v-100h-100zM700 700v200h100v-100h200v-100h-300zM700 1100v100h300v-200h-99v-100h-100v100h99v100h-200zM801 200h100v200h-100v-200z" />
|
||||||
|
<glyph unicode="" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM800 100v400h300v-500h-100v100h-200zM800 1100v100h200v-500h-100v400h-100zM901 200h100v200h-100v-200z" />
|
||||||
|
<glyph unicode="" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM800 400v100h200v-500h-100v400h-100zM800 800v400h300v-500h-100v100h-200zM901 900h100v200h-100v-200z" />
|
||||||
|
<glyph unicode="" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM700 100v200h500v-200h-500zM700 400v200h400v-200h-400zM700 700v200h300v-200h-300zM700 1000v200h200v-200h-200z" />
|
||||||
|
<glyph unicode="" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM700 100v200h200v-200h-200zM700 400v200h300v-200h-300zM700 700v200h400v-200h-400zM700 1000v200h500v-200h-500z" />
|
||||||
|
<glyph unicode="" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h300q162 0 281 -118.5t119 -281.5v-300q0 -165 -118.5 -282.5t-281.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500z" />
|
||||||
|
<glyph unicode="" d="M0 400v300q0 163 119 281.5t281 118.5h300q165 0 282.5 -117.5t117.5 -282.5v-300q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-163 0 -281.5 117.5t-118.5 282.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM400 300l333 250l-333 250v-500z" />
|
||||||
|
<glyph unicode="" d="M0 400v300q0 163 117.5 281.5t282.5 118.5h300q163 0 281.5 -119t118.5 -281v-300q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM300 700l250 -333l250 333h-500z" />
|
||||||
|
<glyph unicode="" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h300q165 0 282.5 -117.5t117.5 -282.5v-300q0 -162 -118.5 -281t-281.5 -119h-300q-165 0 -282.5 118.5t-117.5 281.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM300 400h500l-250 333z" />
|
||||||
|
<glyph unicode="" d="M0 400v300h300v200l400 -350l-400 -350v200h-300zM500 0v200h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5h-500v200h400q165 0 282.5 -117.5t117.5 -282.5v-300q0 -165 -117.5 -282.5t-282.5 -117.5h-400z" />
|
||||||
|
<glyph unicode="" d="M217 519q8 -19 31 -19h302q-155 -438 -160 -458q-5 -21 4 -32l9 -8h9q14 0 26 15q11 13 274.5 321.5t264.5 308.5q14 19 5 36q-8 17 -31 17l-301 -1q1 4 78 219.5t79 227.5q2 15 -5 27l-9 9h-9q-15 0 -25 -16q-4 -6 -98 -111.5t-228.5 -257t-209.5 -237.5q-16 -19 -6 -41 z" />
|
||||||
|
<glyph unicode="" d="M0 400q0 -165 117.5 -282.5t282.5 -117.5h300q47 0 100 15v185h-500q-41 0 -70.5 29.5t-29.5 70.5v500q0 41 29.5 70.5t70.5 29.5h500v185q-14 4 -114 7.5t-193 5.5l-93 2q-165 0 -282.5 -117.5t-117.5 -282.5v-300zM600 400v300h300v200l400 -350l-400 -350v200h-300z " />
|
||||||
|
<glyph unicode="" d="M0 400q0 -165 117.5 -282.5t282.5 -117.5h300q163 0 281.5 117.5t118.5 282.5v98l-78 73l-122 -123v-148q0 -41 -29.5 -70.5t-70.5 -29.5h-500q-41 0 -70.5 29.5t-29.5 70.5v500q0 41 29.5 70.5t70.5 29.5h156l118 122l-74 78h-100q-165 0 -282.5 -117.5t-117.5 -282.5 v-300zM496 709l353 342l-149 149h500v-500l-149 149l-342 -353z" />
|
||||||
|
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM406 600 q0 80 57 137t137 57t137 -57t57 -137t-57 -137t-137 -57t-137 57t-57 137z" />
|
||||||
|
<glyph unicode="" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 800l445 -500l450 500h-295v400h-300v-400h-300zM900 150h100v50h-100v-50z" />
|
||||||
|
<glyph unicode="" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 700h300v-300h300v300h295l-445 500zM900 150h100v50h-100v-50z" />
|
||||||
|
<glyph unicode="" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 705l305 -305l596 596l-154 155l-442 -442l-150 151zM900 150h100v50h-100v-50z" />
|
||||||
|
<glyph unicode="" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 988l97 -98l212 213l-97 97zM200 400l697 1l3 699l-250 -239l-149 149l-212 -212l149 -149zM900 150h100v50h-100v-50z" />
|
||||||
|
<glyph unicode="" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM200 612l212 -212l98 97l-213 212zM300 1200l239 -250l-149 -149l212 -212l149 148l249 -237l-1 697zM900 150h100v50h-100v-50z" />
|
||||||
|
<glyph unicode="" d="M23 415l1177 784v-1079l-475 272l-310 -393v416h-392zM494 210l672 938l-672 -712v-226z" />
|
||||||
|
<glyph unicode="" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-850q0 -21 -15 -35.5t-35 -14.5h-150v400h-700v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 1000h100v200h-100v-200z" />
|
||||||
|
<glyph unicode="" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-218l-276 -275l-120 120l-126 -127h-378v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM581 306l123 123l120 -120l353 352l123 -123l-475 -476zM600 1000h100v200h-100v-200z" />
|
||||||
|
<glyph unicode="" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-269l-103 -103l-170 170l-298 -298h-329v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 1000h100v200h-100v-200zM700 133l170 170l-170 170l127 127l170 -170l170 170l127 -128l-170 -169l170 -170 l-127 -127l-170 170l-170 -170z" />
|
||||||
|
<glyph unicode="" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-300h-400v-200h-500v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 300l300 -300l300 300h-200v300h-200v-300h-200zM600 1000v200h100v-200h-100z" />
|
||||||
|
<glyph unicode="" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-402l-200 200l-298 -298h-402v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 300h200v-300h200v300h200l-300 300zM600 1000v200h100v-200h-100z" />
|
||||||
|
<glyph unicode="" d="M0 250q0 -21 14.5 -35.5t35.5 -14.5h1100q21 0 35.5 14.5t14.5 35.5v550h-1200v-550zM0 900h1200v150q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5v-150zM100 300v200h400v-200h-400z" />
|
||||||
|
<glyph unicode="" d="M0 400l300 298v-198h400v-200h-400v-198zM100 800v200h100v-200h-100zM300 800v200h100v-200h-100zM500 800v200h400v198l300 -298l-300 -298v198h-400zM800 300v200h100v-200h-100zM1000 300h100v200h-100v-200z" />
|
||||||
|
<glyph unicode="" d="M100 700v400l50 100l50 -100v-300h100v300l50 100l50 -100v-300h100v300l50 100l50 -100v-400l-100 -203v-447q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v447zM800 597q0 -29 10.5 -55.5t25 -43t29 -28.5t25.5 -18l10 -5v-397q0 -21 14.5 -35.5 t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v1106q0 31 -18 40.5t-44 -7.5l-276 -116q-25 -17 -43.5 -51.5t-18.5 -65.5v-359z" />
|
||||||
|
<glyph unicode="" d="M100 0h400v56q-75 0 -87.5 6t-12.5 44v394h500v-394q0 -38 -12.5 -44t-87.5 -6v-56h400v56q-4 0 -11 0.5t-24 3t-30 7t-24 15t-11 24.5v888q0 22 25 34.5t50 13.5l25 2v56h-400v-56q75 0 87.5 -6t12.5 -44v-394h-500v394q0 38 12.5 44t87.5 6v56h-400v-56q4 0 11 -0.5 t24 -3t30 -7t24 -15t11 -24.5v-888q0 -22 -25 -34.5t-50 -13.5l-25 -2v-56z" />
|
||||||
|
<glyph unicode="" d="M0 300q0 -41 29.5 -70.5t70.5 -29.5h300q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5h-300q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM100 100h400l200 200h105l295 98v-298h-425l-100 -100h-375zM100 300v200h300v-200h-300zM100 600v200h300v-200h-300z M100 1000h400l200 -200v-98l295 98h105v200h-425l-100 100h-375zM700 402v163l400 133v-163z" />
|
||||||
|
<glyph unicode="" d="M16.5 974.5q0.5 -21.5 16 -90t46.5 -140t104 -177.5t175 -208q103 -103 207.5 -176t180 -103.5t137 -47t92.5 -16.5l31 1l163 162q17 18 13.5 41t-22.5 37l-192 136q-19 14 -45 12t-42 -19l-118 -118q-142 101 -268 227t-227 268l118 118q17 17 20 41.5t-11 44.5 l-139 194q-14 19 -36.5 22t-40.5 -14l-162 -162q-1 -11 -0.5 -32.5z" />
|
||||||
|
<glyph unicode="" d="M0 50v212q0 20 10.5 45.5t24.5 39.5l365 303v50q0 4 1 10.5t12 22.5t30 28.5t60 23t97 10.5t97 -10t60 -23.5t30 -27.5t12 -24l1 -10v-50l365 -303q14 -14 24.5 -39.5t10.5 -45.5v-212q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-20 0 -35 14.5t-15 35.5zM0 712 q0 -21 14.5 -33.5t34.5 -8.5l202 33q20 4 34.5 21t14.5 38v146q141 24 300 24t300 -24v-146q0 -21 14.5 -38t34.5 -21l202 -33q20 -4 34.5 8.5t14.5 33.5v200q-6 8 -19 20.5t-63 45t-112 57t-171 45t-235 20.5q-92 0 -175 -10.5t-141.5 -27t-108.5 -36.5t-81.5 -40 t-53.5 -36.5t-31 -27.5l-9 -10v-200z" />
|
||||||
|
<glyph unicode="" d="M100 0v100h1100v-100h-1100zM175 200h950l-125 150v250l100 100v400h-100v-200h-100v200h-200v-200h-100v200h-200v-200h-100v200h-100v-400l100 -100v-250z" />
|
||||||
|
<glyph unicode="" d="M100 0h300v400q0 41 -29.5 70.5t-70.5 29.5h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-400zM500 0v1000q0 41 29.5 70.5t70.5 29.5h100q41 0 70.5 -29.5t29.5 -70.5v-1000h-300zM900 0v700q0 41 29.5 70.5t70.5 29.5h100q41 0 70.5 -29.5t29.5 -70.5v-700h-300z" />
|
||||||
|
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v300h-200v100h200v100h-300v-300h200v-100h-200v-100zM600 300h200v100h100v300h-100v100h-200v-500 zM700 400v300h100v-300h-100z" />
|
||||||
|
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h100v200h100v-200h100v500h-100v-200h-100v200h-100v-500zM600 300h200v100h100v300h-100v100h-200v-500 zM700 400v300h100v-300h-100z" />
|
||||||
|
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v100h-200v300h200v100h-300v-500zM600 300h300v100h-200v300h200v100h-300v-500z" />
|
||||||
|
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 550l300 -150v300zM600 400l300 150l-300 150v-300z" />
|
||||||
|
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300v500h700v-500h-700zM300 400h130q41 0 68 42t27 107t-28.5 108t-66.5 43h-130v-300zM575 549 q0 -65 27 -107t68 -42h130v300h-130q-38 0 -66.5 -43t-28.5 -108z" />
|
||||||
|
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v300h-200v100h200v100h-300v-300h200v-100h-200v-100zM601 300h100v100h-100v-100zM700 700h100 v-400h100v500h-200v-100z" />
|
||||||
|
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v400h-200v100h-100v-500zM301 400v200h100v-200h-100zM601 300h100v100h-100v-100zM700 700h100 v-400h100v500h-200v-100z" />
|
||||||
|
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 700v100h300v-300h-99v-100h-100v100h99v200h-200zM201 300v100h100v-100h-100zM601 300v100h100v-100h-100z M700 700v100h200v-500h-100v400h-100z" />
|
||||||
|
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM400 500v200 l100 100h300v-100h-300v-200h300v-100h-300z" />
|
||||||
|
<glyph unicode="" d="M0 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM182 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM400 400v400h300 l100 -100v-100h-100v100h-200v-100h200v-100h-200v-100h-100zM700 400v100h100v-100h-100z" />
|
||||||
|
<glyph unicode="" d="M-14 494q0 -80 56.5 -137t135.5 -57h222v300h400v-300h128q120 0 205 86.5t85 207.5t-85 207t-205 86q-46 0 -90 -14q-44 97 -134.5 156.5t-200.5 59.5q-152 0 -260 -107.5t-108 -260.5q0 -25 2 -37q-66 -14 -108.5 -67.5t-42.5 -122.5zM300 200h200v300h200v-300h200 l-300 -300z" />
|
||||||
|
<glyph unicode="" d="M-14 494q0 -80 56.5 -137t135.5 -57h8l414 414l403 -403q94 26 154.5 104.5t60.5 178.5q0 120 -85 206.5t-205 86.5q-46 0 -90 -14q-44 97 -134.5 156.5t-200.5 59.5q-152 0 -260 -107.5t-108 -260.5q0 -25 2 -37q-66 -14 -108.5 -67.5t-42.5 -122.5zM300 200l300 300 l300 -300h-200v-300h-200v300h-200z" />
|
||||||
|
<glyph unicode="" d="M100 200h400v-155l-75 -45h350l-75 45v155h400l-270 300h170l-270 300h170l-300 333l-300 -333h170l-270 -300h170z" />
|
||||||
|
<glyph unicode="" d="M121 700q0 -53 28.5 -97t75.5 -65q-4 -16 -4 -38q0 -74 52.5 -126.5t126.5 -52.5q56 0 100 30v-306l-75 -45h350l-75 45v306q46 -30 100 -30q74 0 126.5 52.5t52.5 126.5q0 24 -9 55q50 32 79.5 83t29.5 112q0 90 -61.5 155.5t-150.5 71.5q-26 89 -99.5 145.5 t-167.5 56.5q-116 0 -197.5 -81.5t-81.5 -197.5q0 -4 1 -11.5t1 -11.5q-14 2 -23 2q-74 0 -126.5 -52.5t-52.5 -126.5z" />
|
||||||
|
</font>
|
||||||
|
</defs></svg>
|
After Width: | Height: | Size: 62 KiB |
BIN
web-server/public/fonts/glyphicons-halflings-regular.ttf
Normal file
BIN
web-server/public/fonts/glyphicons-halflings-regular.woff
Normal file
BIN
web-server/public/images/cards/10_of_clubs.png
Normal file
After Width: | Height: | Size: 53 KiB |
BIN
web-server/public/images/cards/10_of_diamonds.png
Normal file
After Width: | Height: | Size: 45 KiB |
BIN
web-server/public/images/cards/10_of_hearts.png
Normal file
After Width: | Height: | Size: 45 KiB |
BIN
web-server/public/images/cards/10_of_spades.png
Normal file
After Width: | Height: | Size: 49 KiB |
BIN
web-server/public/images/cards/2_of_clubs.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
web-server/public/images/cards/2_of_diamonds.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
web-server/public/images/cards/2_of_hearts.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
web-server/public/images/cards/2_of_spades.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
web-server/public/images/cards/3_of_clubs.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
web-server/public/images/cards/3_of_diamonds.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
web-server/public/images/cards/3_of_hearts.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
web-server/public/images/cards/3_of_spades.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
web-server/public/images/cards/4_of_clubs.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
web-server/public/images/cards/4_of_diamonds.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
web-server/public/images/cards/4_of_hearts.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
web-server/public/images/cards/4_of_spades.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
web-server/public/images/cards/5_of_clubs.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
web-server/public/images/cards/5_of_diamonds.png
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
web-server/public/images/cards/5_of_hearts.png
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
web-server/public/images/cards/5_of_spades.png
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
web-server/public/images/cards/6_of_clubs.png
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
web-server/public/images/cards/6_of_diamonds.png
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
web-server/public/images/cards/6_of_hearts.png
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
web-server/public/images/cards/6_of_spades.png
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
web-server/public/images/cards/7_of_clubs.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
web-server/public/images/cards/7_of_diamonds.png
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
web-server/public/images/cards/7_of_hearts.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
web-server/public/images/cards/7_of_spades.png
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
web-server/public/images/cards/8_of_clubs.png
Normal file
After Width: | Height: | Size: 47 KiB |
BIN
web-server/public/images/cards/8_of_diamonds.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
web-server/public/images/cards/8_of_hearts.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
web-server/public/images/cards/8_of_spades.png
Normal file
After Width: | Height: | Size: 43 KiB |
BIN
web-server/public/images/cards/9_of_clubs.png
Normal file
After Width: | Height: | Size: 50 KiB |
BIN
web-server/public/images/cards/9_of_diamonds.png
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
web-server/public/images/cards/9_of_hearts.png
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
web-server/public/images/cards/9_of_spades.png
Normal file
After Width: | Height: | Size: 46 KiB |
BIN
web-server/public/images/cards/ace_of_clubs.png
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
web-server/public/images/cards/ace_of_diamonds.png
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
web-server/public/images/cards/ace_of_hearts.png
Normal file
After Width: | Height: | Size: 43 KiB |
BIN
web-server/public/images/cards/ace_of_spades.png
Normal file
After Width: | Height: | Size: 60 KiB |
BIN
web-server/public/images/cards/ace_of_spades2.png
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
web-server/public/images/cards/black_joker.png
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
web-server/public/images/cards/jack_of_clubs.png
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
web-server/public/images/cards/jack_of_clubs2.png
Normal file
After Width: | Height: | Size: 203 KiB |