Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Holdem Game: All In functionality implemented + several bug fixes #37

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 86 additions & 64 deletions lib/holdem_game.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,26 +101,10 @@ HoldemGame.prototype.details = function() {

HoldemGame.prototype.tick = function() {
Room.prototype.tick.call(this);

var room = this;
if(room.is_ingame) {
var gamer = room.in_gamers[0];
if(room.turn_countdown > 0) {
room.notifyAll('countdown', {
seat: gamer.seat,
sec: room.turn_countdown
});
room.turn_countdown --;

} else if(room.turn_countdown === 0) {
// TODO: for test only
room.gamerMoveTurn(true);
//room.gamerGiveUp( gamer );

} else {
// not started, just wait
}

} else {
if(room.ready_countdown > 0) {
room.notifyAll('countdown', {
Expand Down Expand Up @@ -166,6 +150,7 @@ HoldemGame.prototype.gameStart = function() {
gamer = gamers[ uid ];
if(gamer.is_ready) {
in_gamers.push( gamer );
room.chips[gamer.seat] = 0;
}
}
}
Expand Down Expand Up @@ -306,7 +291,7 @@ HoldemGame.prototype.gameOver = function() {
scorelist.push(item);
}

room.notifyAll('gameover', scorelist);
room.notifyAll('gameover', {scores: scorelist, showdown: room.ingamers_count > 0});

room.notifyAll('prompt', {
fold: null,
Expand All @@ -323,8 +308,18 @@ HoldemGame.prototype.gameOver = function() {
room.ingamers_count = 0;

// for next round, move deal seat to next
room.dealer_seat = room.deal_order[0].seat;

for (i=0; i<room.deal_order.length; i++) {
if (room.deal_order[i].seat === room.dealer_seat) {
if (i < room.deal_order.length - 1) {
room.dealer_seat = room.deal_order[i + 1].seat;
} else {
room.dealer_seat = room.deal_order[0].seat;
}
break;
}
}


room.deal_order = [];
room.cards = {};
room.chips = {};
Expand Down Expand Up @@ -402,16 +397,17 @@ HoldemGame.prototype.moveTurnToNext = function() {
call: null,
raise: null,
all_in: null
});

});
do {
in_gamers.push( in_gamers.shift() );

next = in_gamers[0];
if(next.seat === room.first_turn) room.round_counter ++;

// to avoid dead loop
if(next.seat === last.seat) break;
if(next.seat === last.seat){
room.no_raise_counter = room.ingamers_count;
break;
}

// we find the next one in game
if(next.is_ingame) {
Expand All @@ -423,6 +419,7 @@ HoldemGame.prototype.moveTurnToNext = function() {
}

} while(true);

};

HoldemGame.prototype.gamerMoveTurn = function(move) {
Expand All @@ -432,7 +429,7 @@ HoldemGame.prototype.gamerMoveTurn = function(move) {
if(move) room.moveTurnToNext();

var deal_card = false;
if(room.no_raise_counter === room.ingamers_count) {
if(room.no_raise_counter >= room.ingamers_count) { // >= for all in case
room.state ++;

switch(room.state) {
Expand Down Expand Up @@ -556,10 +553,10 @@ HoldemGame.prototype.gamerShowDown = function() {
var i, gamer, maxFive, someone_allin = false;
for(i=0; i<in_gamers.length; i++) {
gamer = in_gamers[i];
gamers_bychips.push( gamer );

if(! gamer.is_ingame) continue;
if(gamer.is_allin) someone_allin = true;
gamers_bychips.push( gamer );

maxFive = Holdem.maxFive(gamer.cards, room.shared_cards);
if(maxFive) {
Expand All @@ -571,41 +568,54 @@ HoldemGame.prototype.gamerShowDown = function() {
finals.sort( function(a,b){ return b.maxFiveRank - a.maxFiveRank; } );

if(someone_allin) {
// if someone allin, the pot distribution will be complex
/*
* 当有一或多个牌手全押时,德州扑克的彩池分配较为复杂,超过牌手押注金额的部份将会形成一或多个边池。
* 牌手参与投注该彩池才有机会于该彩池胜出分配奖金。
*
* 当一局结束而且有“全押”的牌手赢牌时,该牌手有参与投注的主池边池奖金均归该牌手。
* 而其他边池由参与该边池投注里,持有最大牌面的牌手赢得。
* 在几个牌手全押形成多个边池时,依全押的顺序分配给各边池中最佳牌面的牌手。
* 无人跟注的边池(仅有一位牌手下注,剩下其他牌手都盖牌)将会直接赢得该边池。
*
* 彩池分配范例:
*
* 例如ABCDEF六名牌手参与牌局,F于中途盖牌退出,最终A全押投入$50,B全押投入$250,C全押投入$350,
* DE各投入$800,F投入$500,此时总彩池大小为$2750,形成了一个主池为50*6=$300,
* 边池各为(250-50)*5=$1000,(350-250)*4=$400,(500-350)*3=$450,(800-500)*2=$600,
* 若最终组成牌面大小为F>A>B>D>E>C,但F已盖牌不能分配任何彩池,则此局主池即为A于此局赢得的筹码($300),
* B可赢得第一个边池($1000),D参与至最后一个边池,且牌面胜过参与第二、第三及第四边池的所有牌手,
* 因此可赢得剩下所有的边池(400+450+600=$1450)。
*/
gamers_bychips.sort( function(a,b) { return a.chips - b.chips; } );
num_players = gamers_bychips.length;
var not_in_game_prize = room.pot;
for (i=0; i < num_players; i++) {
gamers_bychips[i].chips_tmp = gamers_bychips[i].chips;
not_in_game_prize -= gamers_bychips[i].chips;
}
while (gamers_bychips[num_players - 1].chips_tmp > 0) {
var price_step = 0;
var current_prize = 0;
var start_index = -1;
for (i=0; i < num_players; i++) {
if (gamers_bychips[i].chips_tmp === 0) {
continue;
}
if (gamers_bychips[i].chips_tmp > 0 && price_step === 0) {
price_step = gamers_bychips[i].chips_tmp;
start_index = i;
}
gamers_bychips[i].chips_tmp -= price_step;
current_prize += price_step;
}

// we must add the chips of players that are not left in the game, but
// only for the first pot evaluation.
current_prize += not_in_game_prize;
not_in_game_prize = 0;
// we must only consider those players with the highest rank
// for winning the (side) pot
var pot_winners = gamers_bychips.slice(start_index);
pot_winners.sort( function(a,b){ return b.maxFiveRank - a.maxFiveRank; } );
for (i=0; i<pot_winners.length-1; i++) {
if (pot_winners[i+1].maxFiveRank < pot_winners[i].maxFiveRank) {
pot_winners.splice(i+1);
break;
}
}
room.evaluate_pot(pot_winners, current_prize);

}


} else {
// only keep the largest one, may be one, two, or more same big
/*
* 当没有牌手全押(all-in)时,彩池由未盖牌的牌手中牌型最大的者独得。
* 如多于一名牌手拥有最大的手牌,彩池会由他们平等均分。
* 不能平分的零头数筹码由发牌者后依顺时针方向,尚未盖牌的第一个牌手获得(即位置相对最不利者)。
*
* 举例来说:
*
* 有ABCDE依顺时钟方向入座,A为本局发牌者,最小面额筹码为$10,所有牌手皆未盖牌至斗牌,
* 最终由CDE胜出平分本局彩池$1000时,则DE各分到$330,而多出的$10将分配给最靠近A的赢家C,C于本局可分到$340。
*/
// only keep the largest one or all with the same rank
for(i=0; i<finals.length-1; i++) {
if(finals[i].maxFiveRank > finals[i+1].maxFiveRank) {
// if rank at i+1 is strictly lower than rank of i, all gamers
// behind i are deleted.
var losers = finals.splice(i+1, Number.MAX_VALUE);
while(losers.length > 0) {
var loser = losers.shift();
Expand All @@ -615,15 +625,29 @@ HoldemGame.prototype.gamerShowDown = function() {
break;
}
}

var prize = room.pot;
room.evaluate_pot(finals, room.pot);
}

room.gameOver();
};

HoldemGame.prototype.evaluate_pot = function(finals, prize) {
/*
* Compute how much each gamer in finals shall receive
* of the prize. Prize value is adapted in finals. Prize
* is added up as to support several pots with the same
* gamer objects.
*/
var n = finals.length;
if(n > 1) {

if(n > 1) { // pot is splitted due to tie
var average = Math.floor( prize / n );
for(i=0; i<finals.length; i++) {
finals[i].prize = average;
finals[i].prize += average;
}

// distribution of single coins if prize cannot be
// divided evenly by number of entitled players
var odd = prize % n;
if(odd > 0) {
// find the nearest winner after dealer seat
Expand All @@ -642,13 +666,11 @@ HoldemGame.prototype.gamerShowDown = function() {
finals[0].prize += odd;
}
} else {
finals[0].prize = prize;
finals[0].prize += prize;
}
}

room.gameOver();
};


HoldemGame.prototype.onGamer_ready = function(req, reply) {
var room = this;
var uid = req.uid;
Expand Down
44 changes: 37 additions & 7 deletions www/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,12 +163,14 @@ $(document).ready(function(){
client.on('countdown', function(ret){
addMsg(_T('count down:') + ret.seat + ', ' + ret.sec);
});


client.on('fold', function(ret){
addMsg( ret.uid + _T_('at seat') + ret.seat + _T_('fold'));
});

client.on('call', function(ret){

var seat = parseInt(ret.seat);
addMsg( ret.uid + _T_('at seat') + seat + _T_('call') + ret.call);

Expand Down Expand Up @@ -207,6 +209,24 @@ $(document).ready(function(){
showRoom(client.room);
});

client.on('all_in', function(ret){

var seat = parseInt(ret.seat);
var gamers = client.room.gamers;
raise_sum = gamers[ret.uid].coins
client.room.pot += raise_sum
gamers[ret.uid].coins = 0
addMsg(ret.uid + _T_('at seat') + seat + ' all in with ' + raise_sum );
gamers[ret.uid].is_allin = true;

var chips = client.room.chips;
if(chips) {
chips[ seat ] += raise_sum;
}

showRoom(client.room);
});

client.on('pk', function(ret){
addMsg( ret.uid + _T_('at seat') + ret.seat + _T('pk') + ret.pk_uid + _T_('at seat') + ret.pk_seat + ', ' + _T('result') + ': ' + (ret.win?_T('win'):_T('fail')));

Expand Down Expand Up @@ -235,29 +255,40 @@ $(document).ready(function(){
}
});

client.on('gameover', function(ret){
client.on('gameover', function(args){
addMsg( _T('game over!'));

// if only one player remained, we should not show the cards
var showdown = args.showdown;
var ret = args.scores;
if (!showdown) {
addMsg('Only one player remained.');
}
var shared_cards = client.room.shared_cards;
var gamers = client.room.gamers;
var cards = client.room.cards;
var chips = client.room.chips;
while(ret.length > 0) {

var gamer = ret.shift();
var uid = gamer.uid;

var n = (gamer.prize - gamer.chips);
if(n > 0) n = '+' + n;

var mycards = gamer.cards;
var pattern = '';
if(mycards.length === 3) {
pattern = Jinhua.patternString(mycards);
addMsg( '#' + gamer.seat + ', ' + uid + ': ' + n + ', ' + _T_(pattern) );
} else {
var maxFive = Holdem.sort( Holdem.maxFive(mycards, shared_cards) );
pattern = Holdem.patternString( maxFive );
addMsg( '#' + gamer.seat + ', ' + uid + ': ' + n + ', ' + _T_(pattern) + ' (' + Poker.visualize(maxFive) + ')' );
if (showdown) {
var maxFive = Holdem.sort( Holdem.maxFive(mycards, shared_cards) );
pattern = Holdem.patternString( maxFive );

addMsg( '#' + gamer.seat + ', ' + uid + ': ' + n + ', ' + _T_(pattern) + ' (' + Poker.visualize(maxFive) + ')' );} else {
addMsg( '#' + gamer.seat + ', ' + uid + ': ' + n);
}

}

cards[ gamer.seat ] = gamer.cards;
Expand Down Expand Up @@ -598,7 +629,6 @@ function showRoom(room) {
if(g) {
str += g.uid + ' (' + g.name + ') [' + g.coins + ', ' + g.score + ', ' + g.exp + ', ' + g.level + ']';
if(cards && cards[i]) {
str += _T_('private cards') + '[ ' + Poker.visualize( cards[i] ) + ' ]';

if(g.uid === client.uid) {
$('#mycards').html( client.uid + ', ' + _T('my cards') + ': <br/>' + Poker.toHTML(cards[i]) );
Expand Down