Computers Conquer Texas Hold'em Poker for First Time
Or did you mean that players can opt to bet 2 or 3 on the river? However I don't know how that would work in general? Can you raise 2-lead on river with 3?
EDIT: Just remembered this topic: http://forumserver.twoplustwo.com/10...enario-869820/
Sounds like 20 trillion isn't really even a big increase fraction-wise to the original number?
Put a preflop flop out where those five cards are dead and they have to deal more than 50 quintillion.
I believe he means that if five cards were burned face up before the game then the game would have to be recomputed and the size would explode.
That actually sounds like a pretty cool game. I think even 3 would be good for starters. Dead Flop Hold'em.
Hmm, I don't understand why does changing river sizing would make the game tree any bigger, hence harder to calculate? Every street would still be capped at 4 bets and both players start with 8 small bets to cover pre+flop, 4 turn bets and 4 river bets = 24 small bets total. (Normal HU LHE game has 12 small bet cap.)
Or did you mean that players can opt to bet 2 or 3 on the river? However I don't know how that would work in general? Can you raise 2-lead on river with 3?
EDIT: Just remembered this topic: http://forumserver.twoplustwo.com/10...enario-869820/
Sounds like 20 trillion isn't really even a big increase fraction-wise to the original number?
Rephrase please? Didn't quite catch your drift.
Or did you mean that players can opt to bet 2 or 3 on the river? However I don't know how that would work in general? Can you raise 2-lead on river with 3?
EDIT: Just remembered this topic: http://forumserver.twoplustwo.com/10...enario-869820/
Sounds like 20 trillion isn't really even a big increase fraction-wise to the original number?
Rephrase please? Didn't quite catch your drift.
With a preflop flop they have 2.6 million strategies to figure out.
I think if you're going down this route, you should find a game that's definitely out of reach.
Even then the cat's out of the bag: the best way to compete against the most skilled players HU is by building smart AI and copying its strategy as much as possible.
So I'm basically querying a supercomputer, no? A one which regular people don't have access to? Could someone grab the source codes and be able to build a similar system which can query the database almost real-time with a moderate investment? (Moderate in this case something in the range of 10-50k?)
Here is a some code from a file cepheus.js I haven't really read through it
Spoiler:
Code:
window.cepheus = { heartsSelect: $('#hearts-select.card'), clubsSelect: $('#clubs-select.card'), spadesSelect: $('#spades-select.card'), diamondsSelect: $('#diams-select.card'), cardSelectModal: $('#card-select-modal'), cardRankSelectList: $('#card-rank-select-list'), cardSuitSelectList: $('#card-suit-select-list'), currentSeletingCard: null, resetCard: $('#reset-card'), selectedCards: { flop: { hearts: [], spades: [], clubs: [], diams: [] }, hand: { hearts: [], spades: [], clubs: [], diams: [] } }, results: [], resultCard: [null, null], init: function(){ var pThis = this; $('#results-back-to-top').hide(); // Add events $('.card-select-option').click(function(event){ $(pThis.cardRankSelectList).removeClass('invisible'); pThis.setRanks($(event.target).closest('.card-select-option')[0].classList[3]); }); pThis.resetCard.click(function(){ pThis.currentSeletingCard.addClass('unspecified-card').removeClass('selecting'); var insideCard = pThis.currentSeletingCard.find('.card'); var insideCardClasses = insideCard[0].className.split(' '); if (pThis.currentSeletingCard.hasClass('flop-card')){ var selectIndex = pThis.selectedCards.flop[insideCardClasses[2]].indexOf(insideCardClasses[1]); pThis.selectedCards.flop[insideCardClasses[2]].splice(selectIndex, 1); } else { var selectIndex = pThis.selectedCards.hand[insideCardClasses[2]].indexOf(insideCardClasses[1]); pThis.selectedCards.hand[insideCardClasses[2]].splice(selectIndex, 1); } insideCard.removeClass().addClass('card'); insideCard.find('.rank').text('?'); insideCard.find('.suit').html(''); pThis.closeModal(); }); $('.specifiable-card').click(function(event){ var card = $(event.target).closest('li'); if (card.hasClass('selecting')){ // if selected card is clicked remove class card.removeClass('selecting'); pThis.currentSeletingCard = null; pThis.closeModal(); } else { var thisCard = $(this); if (!$(this).hasClass('unspecified-card')){ pThis.resetCard.removeClass('invisible'); } else { pThis.resetCard.addClass('invisible'); } if (pThis.currentSeletingCard){ // if a card was already selected pThis.currentSeletingCard.removeClass('selecting'); } $(card).addClass('selecting'); // make the newest one the only selected card pThis.currentSeletingCard = $(card); pThis.openModal(); } }); $('.selectable').click(function(event){ var card = $(event.target).closest('.list-group-item')[0]; pThis.currentSeletingCard.removeClass('selecting'); var selectableCardClasses = $(card).find('.card')[0].className.split(' '); var insideCardClasses = pThis.currentSeletingCard.find('.card')[0].className.split(' '); if (pThis.currentSeletingCard.hasClass('flop-card')){ if(insideCardClasses.length > 1){ // have to remove previous card from selectedCards list var selectIndex = pThis.selectedCards.flop[insideCardClasses[2]].indexOf(insideCardClasses[1]); pThis.selectedCards.flop[insideCardClasses[2]].splice(selectIndex, 1); } pThis.selectedCards.flop[selectableCardClasses[2]].push(selectableCardClasses[1]); } else { if(insideCardClasses.length > 1){ // have to remove previous card from selectedCards list var selectIndex = pThis.selectedCards.hand[insideCardClasses[2]].indexOf(insideCardClasses[1]); pThis.selectedCards.hand[insideCardClasses[2]].splice(selectIndex, 1); } pThis.selectedCards.hand[selectableCardClasses[2]].push(selectableCardClasses[1]); if (pThis.currentSeletingCard[0].id == 'hand1'){ pThis.resultCard[0] = {card: pThis.bigToMiniClass(selectableCardClasses[2]), rank: 'mini-' + selectableCardClasses[1]}; } else if (pThis.currentSeletingCard[0].id == 'hand2'){ pThis.resultCard[1] = {card: pThis.bigToMiniClass(selectableCardClasses[2]), rank: 'mini-' + selectableCardClasses[1]}; } pThis.updateMainResults(); } if (pThis.currentSeletingCard[0].id == 'flop3' || pThis.currentSeletingCard[0].id == 'flop4'){ pThis.currentSeletingCard.html(card.innerHTML + '<hr class ="line"></hr>'); } else { pThis.currentSeletingCard.html(card.innerHTML); } pThis.currentSeletingCard.removeClass('unspecified-card'); pThis.currentSeletingCard = null; pThis.closeModal(); }); $('#betting-rounds-list').on('click', '.add-action', function(event){ event.preventDefault(); var bettingRound = $($(this).closest('.betting-round')); var bettingSequenceList = bettingRound.find('.betting-sequence-list'); bettingSequenceList.find('.betting-round-no-action').remove(); var numActions = bettingSequenceList.find('.round-bet').length; if (numActions < 6) { var labelText = (numActions == 0 && bettingRound.index() > 0) ? 'Bet' : 'Raise'; var betActionHTML = '<div class="betting-round-action round-bet"> ' + '<label class="label label-success">' + labelText + '</label> ' + '</div>'; if (numActions > 0){ $(bettingSequenceList.find('.round-bet')[numActions - 1]).after(betActionHTML); } else if (bettingSequenceList.find('.round-check').length > 0){ $(bettingSequenceList.find('.round-check')[0]).after(betActionHTML); } else if (bettingSequenceList.find('.betting-round-blinds').length > 0){ $(bettingSequenceList.find('.betting-round-blinds')[0]).after(betActionHTML); } else { bettingSequenceList.prepend(betActionHTML); } if ((bettingRound.index() == 0 && numActions >= 2) || numActions >= 3){ $(this).addClass('disabled'); } } }); $('#betting-rounds-list').on('click', '.remove-action', function(event){ event.preventDefault(); var bettingRounds = $('.betting-round'); var bettingRound = $($(this).closest('.betting-round')); var bettingActionList = bettingRound.find('.round-bet'); var removeCheck = false; if (bettingRound.find('.round-call').length > 0 && ((bettingRounds.length - 1) == bettingRound.index())) { // if last round bettingRound.find('.round-call').remove(); // remove the call if there is one } else { bettingActionList.last().remove(); // else remove the last bet if (bettingActionList.length == 1 && !(bettingRounds.length - 1 == bettingRound.index()) && (bettingRound.find('.round-check').length == 0)){ // if there was only one bet (now its 0) and this isnt the last round and there isnt a check, add a check // so its not invalid $(bettingRound.find('.check-action')).click(); } else if (bettingActionList.length == 0 && (bettingRounds.length - 1 == bettingRound.index()) && bettingRound.find('.round-check').length > 0){ // else if this is the last round and there were no bets to remove bettingRound.find('.round-check').remove(); removeCheck = true; } } if (bettingActionList.length <= 1){ // remove last and add noAction if((bettingRound.find('.round-check').length == 0 || removeCheck) && ((bettingRounds.length - 1) == bettingRound.index()) && (bettingRound.index() != 0)){ var noActionHTML = '<div class="betting-round-no-action">' + '<label class="label label-warning">No Action</label>' + '</div>'; bettingRound.find('.betting-sequence-list').html(noActionHTML); } } else if (bettingRound.find('.betting-round-no-action').length == 1 && bettingRounds.length > 1){ bettingRound.remove(); $('#add-betting-round').removeClass('disabled'); } if ((bettingRound.index() == 0 && bettingActionList.length < 4) || (bettingRound.index() > 0 && bettingActionList.length < 5)){ bettingRound.find('.add-action').removeClass('disabled'); } if (bettingRound.find('.round-check').length == 0){ $(bettingRound.find('.check-action')).removeClass('btn-info'); $(bettingRound.find('.check-action')).addClass('btn-primary'); } }); $('#betting-rounds-list').on('click', '.check-action', function(event){ event.preventDefault(); var bettingRounds = $('.betting-round'); var bettingRound = $($(this).closest('.betting-round')); var bettingSequenceList = bettingRound.find('.betting-sequence-list'); var checkText = ((bettingRound.index() == 0) ? 'Call' : 'Check'); var checkActionHTML = '<div class="betting-round-action round-check"> ' + '<label class="label label-primary">' + checkText + '</label> ' + '</div>'; bettingSequenceList.find('.betting-round-no-action').remove(); if ($(this).hasClass('btn-primary')) { if (bettingRound.index() == 0){ $(bettingSequenceList.find('.betting-round-blinds')[0]).after(checkActionHTML); } else { bettingSequenceList.prepend(checkActionHTML); } $(this).removeClass('btn-primary').addClass('btn-info'); } else if (bettingSequenceList.find('.round-bet').length > 0 || (bettingRound.index() == (bettingRounds.length - 1))) { bettingSequenceList.find('.round-check').remove(); $(this).removeClass('btn-info').addClass('btn-primary'); } if (bettingSequenceList.find('.betting-round-action').length == 0 && (bettingRound.index() != 0)){ var noActionHTML = '<div class="betting-round-no-action">' + '<label class="label label-warning">No Action</label>' + '</div>'; bettingSequenceList.html(noActionHTML); } }); $('#remove-betting-round').click(function(){ var bettingRoundsList = $('#betting-rounds-list'); var bettingRounds = bettingRoundsList.find('.betting-round'); var flopCards = $('.flop-card'); flopCards.removeClass('card-greyout'); $(flopCards[4]).addClass('card-greyout'); if (bettingRounds.length < 4){ $(flopCards[3]).addClass('card-greyout'); if (bettingRounds.length < 3){ $(flopCards[2]).addClass('card-greyout'); $(flopCards[1]).addClass('card-greyout'); $(flopCards[0]).addClass('card-greyout'); } } if (bettingRounds.length == 2){ $(this).addClass('disabled'); } bettingRounds.last().remove(); bettingRoundsList = $('#betting-rounds-list'); bettingRounds = bettingRoundsList.find('.betting-round'); bettingRounds.last().find('.remove-action').click(); $('#add-betting-round').removeClass('disabled'); }); $('#add-betting-round').click(function(){ var bettingRoundsList = $('#betting-rounds-list'); var bettingRounds = bettingRoundsList.find('.betting-round'); var flopCards = $('.flop-card'); flopCards.addClass('card-greyout'); $(flopCards[0]).removeClass('card-greyout'); $(flopCards[1]).removeClass('card-greyout'); $(flopCards[2]).removeClass('card-greyout'); if (bettingRounds.length > 1){ $(flopCards[3]).removeClass('card-greyout'); if (bettingRounds.length > 2) { $(flopCards[4]).removeClass('card-greyout'); } } var callActionHTML = '<div class="betting-round-action round-call"> ' + '<label class="label label-primary"> </label> ' + '</div>'; var lastBettingRound = $(bettingRounds[bettingRounds.length - 1]); if (lastBettingRound.find('.betting-round-no-action').length > 0 || ((bettingRounds.length == 1) && lastBettingRound.find('.round-bet').length == 0 && lastBettingRound.find('.round-check').length == 0)){ lastBettingRound.find('.betting-round-no-action').remove(); lastBettingRound.find('.check-action').click(); } var bettingSequenceList = $(lastBettingRound.find('.betting-sequence-list')); if (bettingSequenceList.find('.round-call').length == 0){ bettingSequenceList.append(callActionHTML); } $('#remove-betting-round').removeClass('disabled'); var numRounds = bettingRounds.length; var toggleText = (numRounds == 0) ? 'Call' : 'Check'; if (numRounds < 4) { var bettingRoundGuid = pThis.guid(); var roundHTML = '<li class="betting-round" id="' + bettingRoundGuid + '"> ' + '<div class="betting-sequence-list col-md-10 col-sm-10 col-xs-10"> ' + '<div class="betting-round-no-action">' + '<label class="label label-warning">No Action</label>' + '</div>' + '</div> ' + '<div class="betting-round-action-buttons col-md-2 col-sm-2 col-xs-2">' + '<a href="#" class="btn btn-primary check-action" data-toggle="tooltip" data-placement="top" title="Toggle ' + toggleText + '" data-container="body">' + '<i class="glyphicon glyphicon-ok"></i>' + '</a>' + '<a href="#" class="btn btn-danger remove-action" data-toggle="tooltip" data-placement="top" title="Remove Action">' + '<i class="glyphicon glyphicon-minus"></i>' + '</a>' + '<a href="#" class="btn btn-success add-action" data-toggle="tooltip" data-placement="top" title="Add Bet/Raise">' + '<i class="glyphicon glyphicon-plus"></i>' + '</a>' + '</div>' + '</li>'; bettingRoundsList.append(roundHTML); $('a[data-toggle]').tooltip(); if (numRounds == 3){ $(this).addClass('disabled'); } } }); $('#query-submit-button').click(function(event){ $('#query-submit-button').addClass('disabled'); var popoverContent = ''; var bettingString = ''; var specifiedCards = $('.flop-card').not('.unspecified-card').not('.card-greyout'); var bettingRounds = $('.betting-round'); switch (specifiedCards.length){ case 0: if (bettingRounds.length > 1){ popoverContent = 'There can not be more than 1 betting round before the first flop.'; } else { // verifying the betting rounds var verifiedObj = pThis.verifyBettingRounds(bettingRounds); popoverContent = verifiedObj.error; bettingString = verifiedObj.bettingString; } break; case 1: popoverContent = 'The betting has reached the second round, so the first three flop cards must be set.'; break; case 2: popoverContent = 'The betting has reached the second round, so the first three flop cards must be set.'; break; case 3: if (specifiedCards[2].id != 'flop3'){ popoverContent = 'The betting has reached the second round, so the first three board cards must be set.'; break; } else if (bettingRounds.length != 2) { popoverContent = 'The first flop needs exactly 2 betting rounds.'; break; } else { // verifying the betting rounds var verifiedObj = pThis.verifyBettingRounds(bettingRounds); popoverContent = verifiedObj.error; bettingString = verifiedObj.bettingString; } break; case 4: if (specifiedCards[3].id != 'flop4'){ popoverContent = 'The betting has reached the third round, so the first four board cards must be set.'; break; } else if (bettingRounds.length != 3){ popoverContent = 'The second flop needs exactly 3 betting rounds.'; break; } else { // verifying the betting rounds var verifiedObj = pThis.verifyBettingRounds(bettingRounds); popoverContent = verifiedObj.error; bettingString = verifiedObj.bettingString; } break; case 5: if (bettingRounds.length != 4){ popoverContent = 'The betting has reached the final round, so all board cards must be set.'; break; } else { // verifying the betting rounds var verifiedObj = pThis.verifyBettingRounds(bettingRounds); popoverContent = verifiedObj.error; bettingString = verifiedObj.bettingString; } break; } if (popoverContent){ pThis.submitPopUp(popoverContent); event.stopPropagation(); if (window.event){ window.event.cancelBubble = true; } $('#query-submit-button').removeClass('disabled'); } else { // construct card string var cardString = pThis.constructCardString(specifiedCards); console.log(cardString); var query = bettingString + ':' + cardString; // the query is good to go $(this).addClass('submit-loading'); pThis.sendQuery(query); console.log('query sent', query); } }); $('#results-header button').click(function(){ var direction = -1; if ($(this).hasClass('sort-up')){ $(this).removeClass('sort-up').addClass('sort-down'); } else if ($(this).hasClass('sort-down')){ $(this).removeClass('sort-down').addClass('sort-up'); direction = 1; } else { var sortButtons = $('#results-header button'); sortButtons.removeClass('sort-up sort-down'); $(this).addClass('sort-down'); } pThis.sortResultList($(this).data().sortby, direction); }); $('#results-list').on('click', '.result-row-li', function(){ pThis.selectedCards.hand = { hearts: [], spades: [], clubs: [], diams: [] }; pThis.resultCard = []; var htmlFromMini = function(miniCard, handCardNumber){ var miniCardClass = miniCard.className.split(' '); miniCardClass = (miniCardClass[1] != 'mini-card') ? miniCardClass[1] : miniCardClass[0]; pThis.resultCard.push({rank: 'mini-rank-' + miniCard.innerHTML, card: miniCardClass}); console.log(pThis.resultCard[handCardNumber]); switch (miniCardClass){ case 'mini-d': miniCardClass = 'diams'; break; case 'mini-c': miniCardClass = 'clubs'; break; case 'mini-h': miniCardClass = 'hearts'; break; case 'mini-s': miniCardClass = 'spades'; break; } miniCardInnerHTML = (miniCard.innerHTML.toLowerCase() == 't') ? '10' : miniCard.innerHTML.toLowerCase(); pThis.selectedCards.hand[miniCardClass].push('rank-' + miniCardInnerHTML); return '<a href="#" class="card rank-' + miniCardInnerHTML + ' ' + miniCardClass + '"> \ <span class="rank">' + miniCardInnerHTML.toUpperCase() + '</span> \ <span class="suit">&' + miniCardClass + ';</span> \ </a>'; }; var resultObj = { fold: $(this).data().resultfold, call: $(this).data().resultcall, raise: $(this).data().resultraise, P1Prob: $(this).data().resultp1, P2Prob: $(this).data().resultp2 }; $('#hand1').removeClass('unspecified-card'); $('#hand2').removeClass('unspecified-card'); $('#hand1').html(htmlFromMini($(this).find('.mini-card')[0], 0)); $('#hand2').html(htmlFromMini($(this).find('.mini-card')[1], 1)); console.log('pThis.resultCard', pThis.resultCard); pThis.generatePie(resultObj); $('#main-stats').html('<div class="stat-summary" > \ <table class="table"> \ <tr> \ <td class="col-md-6">Player Probability</td> \ <td class="col-md-6">' + resultObj.P1Prob + '</td> \ </tr> \ <tr> \ <td class="col-md-6">Opponent Probability</td> \ <td class="col-md-6">' + resultObj.P2Prob + '</td> \ </tr> \ <tr> \ <td class="col-md-6">Fold</td> \ <td class="col-md-6">' + resultObj.fold + '%</td> \ </tr> \ <tr> \ <td class="col-md-6">Call</td> \ <td class="col-md-6">' + resultObj.call + '%</td> \ </tr> \ <tr> \ <td class="col-md-6">Raise</td> \ <td class="col-md-6">' + resultObj.raise + '%</td> \ </tr> \ </table> \ </div> \ '); }); $('[data-toggle]').tooltip(); $('#results-list').scroll(function() { console.log("scrolling"); if($(this).scrollTop() == 0){ console.log("Scrolled to the top"); $('#results-back-to-top').hide(); } else { $('#results-back-to-top').show(); } }); $('#results-back-to-top').click(function () { $(this).hide(); $('#results-list').animate({ scrollTop: 0 }); }); }, submitPopUp: function(content){ $('#query-submit-button').attr('data-content', content); $('#query-submit-button').popover({ placement: 'bottom', title: 'Oops', trigger: 'click', }).popover('show'); setTimeout(function() { $('#query-submit-button').popover('destroy'); }, 3000); }, sendQuery: function(query){ var pThis = this; console.log('sendQuery:', query); pThis.previousSort = null; var sortButtons = $('.sort-down,.sort-up'); if (sortButtons.length != 0){ pThis.previousSort = { sortby: sortButtons.data().sortby, direction: (sortButtons.hasClass('sort-up')) ? 1 : -1 }; } $.get('/query', {queryString: query}, function(data) { $('#query-submit-button').removeClass('disabled'); pThis.results = []; var rows = data.split('\n'); if (rows[0].split(' ') == ''){ pThis.submitPopUp('The query was sent but there was nothing for the strategy to do.'); return; } console.log('Number of Rows:', rows[0], 'Actual:', rows.length - 2); for (var i = 1; i < rows.length-1; i++) { rows[i] = rows[i].split(' '); var miniCardRank1 = (rows[i][0][0] == 'T') ? 10 : rows[i][0][0]; var miniCardRank2 = (rows[i][0][2] == 'T') ? 10 : rows[i][0][2]; var resultObj = { cardindex: i-1, cardHTML: '<div class="results-card-pair"><span class="mini-card mini-' + rows[i][0][1] + ' mini-rank-' + miniCardRank1 + '">' + miniCardRank1 + '</span><span class="mini-card mini-' + rows[i][0][3] + ' mini-rank-' + miniCardRank2 + '">' + miniCardRank2 + '</span></div> ', fold: (rows[i][1]*100), call: (rows[i][2]*100), raise: (rows[i][3]*100), P1Prob: rows[i][4], P2Prob: rows[i][5] }; resultObj.html = pThis.createResultRowHTML(resultObj); pThis.results.push(resultObj); } console.log(pThis.results); pThis.setResults(); pThis.updateMainResults(); //$('#results-header button').removeClass('sort-down sort-up'); //$('#card-sort').removeClass('sort-down').addClass('sort-up'); if (!pThis.previousSort){ $('#p1-sort').click(); } else { pThis.sortResultList(pThis.previousSort.sortby, pThis.previousSort.direction); } }); }, updateMainResults: function(){ var pThis = this; if (pThis.resultCard && pThis.resultCard[0] && pThis.resultCard[1]){ $('.' + pThis.resultCard[0].card + '.' + pThis.resultCard[0].rank) .closest('.results-card-pair') .find('.' + pThis.resultCard[1].card + '.' + pThis.resultCard[1].rank).click(); } }, constructCardString: function(specifiedCards){ console.log('constructCardString'); var cardString = ''; if (specifiedCards.length > 0) { for (var i = 0; i < specifiedCards.length; i++) { console.log(specifiedCards[i]); var innerCardClasses = $(specifiedCards[i]).find('.card')[0].className.split(' ');; console.log(innerCardClasses); var innerCardRank = ''; var innerCardSuit = ''; for (var j = 0; j < innerCardClasses.length; j++){ var tempClassName = innerCardClasses[j]; switch (tempClassName.substring(0,4)){ case 'rank': if(tempClassName == "rank-10"){ innerCardRank = 'T'; } else { innerCardRank = tempClassName.toUpperCase().substring(tempClassName.length-1); } break; case 'card': break; default: innerCardSuit = tempClassName.substr(0,1); break; } } cardString += innerCardRank + innerCardSuit; } } return cardString; }, verifyBettingRounds: function(bettingRounds){ console.log('verifyBettingRounds'); var error = ''; var finalBettingString = ''; for (var i = 0; i < bettingRounds.length; i++){ var tempBettingString = ''; var roundActions = $(bettingRounds[i]).find('.betting-round-action'); for (var j = 0; j < roundActions.length; j++){ var action = $(roundActions[j]); if (action.hasClass('round-bet')){ tempBettingString += 'r'; } else { tempBettingString += 'c'; } } if (bettingRounds.length == 1){ // only 1 round, special case console.log('only round'); var bettingStringRegex = new RegExp('^c?r{0,3}c?$'); if (bettingStringRegex.test(tempBettingString)){ finalBettingString += tempBettingString; // if it ends with a call/check, close the round if (tempBettingString.length > 1 && tempBettingString.indexOf('c', tempBettingString.length -1) !== -1){ finalBettingString += '/'; } } else { error = 'The first betting round is not valid. \ There are a max of 3 bets in the first round. \ Make sure that it follows the form: "call, 0 to 3 bets, call/check".'; } } else if (i == 0){ // first round, must be complete console.log('first round, must be complete'); var bettingStringRegex = new RegExp('^c?r{0,3}c$'); console.log(tempBettingString); console.log(tempBettingString.length); if (bettingStringRegex.test(tempBettingString) && tempBettingString.length > 1){ finalBettingString += tempBettingString + '/'; } else { error = 'The 1st betting round is not valid. Make sure it ends with a call/check and has at least 2 actions, eg: "bet, call or check, check".'; } } else if (i < bettingRounds.length - 1){ // is not the last round, check if complete console.log('not last round'); var bettingStringRegex = new RegExp('^c?r{0,4}c$'); if (bettingStringRegex.test(tempBettingString)){ finalBettingString += tempBettingString + '/'; } else { error = 'The ' + (i+1) + ((i == 1) ? 'nd' : 'rd') + ' betting row is not valid. Make sure it ends with a "call/check".'; } } else { console.log('last round, need not be completed'); var bettingStringRegex = new RegExp('^c?r{0,4}c?$'); if (bettingStringRegex.test(tempBettingString)){ finalBettingString += tempBettingString; } else { error = 'The last betting row is not valid. Make sure that it follows the form: "call, 0 to 4 bet(s), call/check".'; } } if (error != '') break; } return {bettingString: finalBettingString, error: error}; }, openModal: function(){ var pThis = this; this.cardSelectModal.modal('show'); $('.modal-backdrop').click(function(event){ $('.selecting').removeClass('selecting'); pThis.currentSeletingCard = null; pThis.closeModal(); }); }, closeModal: function(){ $(this.cardRankSelectList).addClass('invisible'); this.cardSelectModal.modal('hide'); }, setResults: function(){ console.log('setResults'); var pThis = this; var list = $('#results-list')[0]; var listHTML = ''; for (var i = 0; i < pThis.results.length; i++) { listHTML += pThis.results[i].html; } list.innerHTML = listHTML; }, createResultRowHTML: function(resultObj){ var diff = ((resultObj.fold + resultObj.call + resultObj.raise) - 100); if (diff != 0){ resultObj.fold = Math.round(resultObj.fold); resultObj.call = Math.round(resultObj.call); resultObj.raise = Math.round(resultObj.raise); diff = (100 - (resultObj.fold + resultObj.call + resultObj.raise)) / 3; if (resultObj.fold + resultObj.call + resultObj.raise == 99){ console.log('here'); } resultObj.fold += diff, resultObj.call += diff, resultObj.raise += diff; } return '<li class="result-row-li" \ data-resultcardindex="' + resultObj.cardindex + '" \ data-resultfold="' + resultObj.fold + '" \ data-resultcall="' + resultObj.call + '" \ data-resultraise="' + resultObj.raise + '" \ data-resultp1="' + resultObj.P1Prob + '" \ data-resultp2="' + resultObj.P2Prob + '">' + resultObj.cardHTML + ' \ <div class="result-bar"> \ <div class="results-fold" style="width: ' + resultObj.fold + '%">' + Math.round(resultObj.fold) + '</div> \ <div class="results-call" style="width: ' + resultObj.call + '%">' + Math.round(resultObj.call) + '</div> \ <div class="results-bet" style="width: ' + resultObj.raise + '%">' + Math.round(resultObj.raise) + '</div> \ </div> \ <div class="results-p1seq" >' + parseFloat(resultObj.P1Prob).toFixed(3) + '</div> \ <div class="results-p2seq" >' + parseFloat(resultObj.P2Prob).toFixed(3) + '</div> \ </li>'; }, generatePie: function(resultObj) { var canvas = $('#pie-canvas')[0]; var ctx = canvas.getContext("2d"); var lastend = 0, colors = ['#d43f3a', '#428bca', '#5cb85c'], data = [resultObj.fold,resultObj.call,resultObj.raise]; console.log(data); for (var i = 0; i < data.length; i++) { ctx.fillStyle = colors[i]; ctx.beginPath(); ctx.moveTo(canvas.width/2,canvas.height/2); ctx.arc(canvas.width/2,canvas.height/2,canvas.height/2,lastend,lastend+(Math.PI*2*(data[i]/100)),false); ctx.lineTo(canvas.width/2,canvas.height/2); ctx.fill(); console.log('lastend', lastend); lastend += Math.PI*2*(data[i]/100); } }, sortResultList: function(sortBy, direction){ var pThis = this; pThis.results.sort(compare); pThis.setResults(); console.log('sortBy',sortBy,'direction', direction); function compare(a, b){ return (b[sortBy]) < (a[sortBy]) ? direction : -direction; } }, setRanks: function(suit){ this.cardRankSelectList.find('.card').removeClass('diams hearts spades clubs').addClass(suit); this.cardRankSelectList.find('.suit').html('&' + suit +';'); $('.hide-card').removeClass('hide-card'); this.hideSelectedCards(suit); }, hideSelectedCards: function(suit){ var selectedCardsOfSuit = this.selectedCards.flop[suit].concat(this.selectedCards.hand[suit]); for (var i = 0; i < selectedCardsOfSuit.length; i++){ $(this.cardRankSelectList.find('.' + selectedCardsOfSuit[i])[0]).addClass('hide-card'); } }, bigToMiniClass: function(bigClass){ switch (bigClass){ case 'diams': return 'mini-d'; case 'clubs': return 'mini-c'; case 'hearts': return 'mini-h'; case 'spades': return 'mini-s'; } }, guid: (function(){ function s4() { return Math.floor((1 + Math.random()) * 0x10000) .toString(16) .substring(1); } return function() { return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); }; })(), }; window.cepheus.init(); if(!!navigator.userAgent.match(/Trident.*rv\:11\./)){ // F**Kin IE $('html').addClass('ie11'); }
So how many decision points? I think someone mentioned 400 quads, so a factor of 1000 increase? Doesn't seem too far fetched with improved hardware and perhaps some tweaking of the algorithm. They would be finished before you could get a decent amount of people to start playing the game.
I think if you're going down this route, you should find a game that's definitely out of reach.
But "kill the game" sort of feels like the wrong answer to preventing someone from cheating, especially for competitive play rather than open tables online :/
(not that I have a fantastic solution to offer...)
You're looking at the user interface code on the web page. There's not much poker knowledge to be gained there...
I'll happily go on record as saying heads-up NLHE 100b won't be solved in anyone's lifetime. Adding more players won't make that problem easier...
It's an interesting question of how small the stacks would need to be. We could certainly solve a 2 chip game. I need more free time :/
It's an interesting question of how small the stacks would need to be. We could certainly solve a 2 chip game. I need more free time :/
A question about some general stuff. Since it is one of possibly many (possibly infinite) Nash equilibria strategies, we would expect to see different convergence points if you ran the iterations many times. Are there any plans to run it again to see how different the next optimal play is? Obviously this may be a big use of resources for little in the way of scientific interest, but it would be very interesting from a poker perspective. Also, if you were to run the process and generate many different optimal strategies, it would be super interesting to see what the equilibria landscape looks like.
In this post you state HUNLHE won't be solved in our lifetimes. Are you sure about this? I've read somewhere that HUNLHE with 200bb stacks has something like 10^40 decision points. In the past 9 years you stated (and you have a chart which shows) that we have gone from solving order 10^6 games to HULHE at 10^14 states - so roughly increasing the exponent by 1.33 every year. At that rate of progress, I make it 19.5 years to hit 10^40. So I would estimate 20 years, give or take, before HUNLHE is a solved game.
Final question: are you accepting PhD students, and do you take people from a maths/machine learning background with no economics knowledge?
When does the speed of light start coming into play?
Lay off da weed bro
A question about some general stuff. Since it is one of possibly many (possibly infinite) Nash equilibria strategies, we would expect to see different convergence points if you ran the iterations many times. Are there any plans to run it again to see how different the next optimal play is? Obviously this may be a big use of resources for little in the way of scientific interest, but it would be very interesting from a poker perspective. Also, if you were to run the process and generate many different optimal strategies, it would be super interesting to see what the equilibria landscape looks like.
It's an interesting question. We've tried looking at this in smaller games, just to see how variable the strategies are. There are definitely strategies that have different actions in a bunch of places, but looking at a bunch of randomly selected strategies has mostly just resulted in an unusable big list of changes in an even bigger sea of numbers. Even answering the question "how different are these strategies" seems to be hard to do well in a way that satisfies human intuition.
In this post you state HUNLHE won't be solved in our lifetimes. Are you sure about this? I've read somewhere that HUNLHE with 200bb stacks has something like 10^40 decision points. In the past 9 years you stated (and you have a chart which shows) that we have gone from solving order 10^6 games to HULHE at 10^14 states - so roughly increasing the exponent by 1.33 every year. At that rate of progress, I make it 19.5 years to hit 10^40. So I would estimate 20 years, give or take, before HUNLHE is a solved game.
FullyCompletely lists a bunch of sizes in http://webdocs.cs.ualberta.ca/~games...rt-nl-size.pdf -- if you only care about the sizes, you can find them starting on page 10 for a few different HU NLHE games. You're looking for the one sided canonical, total values. The "Infosets" column tells you how many place you'll need to make a decision, and the "Actions" column tells you how many different numbers you'll need in a strategy.
So, for a 200bb game with 1 chip / 2 chip blinds, a strategy has about 1.8*10^47 values. That's a big table, and I don't see hard drives growing that quickly. That's out.
We now have safe methods for throwing away parts of the strategy, and then recovering them as needed, but they've got a big computation cost. Let's ignore that computation cost (bigger than HU LHE in this case) and split the game in half. Best case, for a balanced binary tree, we use the square root of the original space -- 4.2*10^23 values. Lets say we get the same great compression, and use less than a single bit per value -- about 5 * 10^22 bytes. That's still 10 billion TB. I guess that's a "maybe in my lifetime" amount, but it still leaves looking up an answering being a computation 10 billion times larger than HU LHE. So, that's out.
That leaves something unexpected, but it's a pretty surprising unexpected something. 10^47 is pretty big even for a perfect information game.
Final question: are you accepting PhD students, and do you take people from a maths/machine learning background with no economics knowledge?
Just out of curiosity, how long would it take to solve 20bb or 25bb deep HUNLHE?
Surely if bet sizing were continuous, there would be a way to converge towards a GTO strategy without considering an infinite amount of bet sizes?
backgammon has been solved for some time now, yet theres still plenty of (big) action to be had
the game (or other games) will not die out as a result of it being solved
the game (or other games) will not die out as a result of it being solved
to breakeven against a gto bot, doesnt we just have to go to the river everytime and bet allin?
I think we've just solved NLHE guys, time to quit playing.
Is there big ONLINE action to be had in backgammon?
People should stop mentioning GTO as it is supposedly refering to a possibly non exploitative strategy. This bot clearly uses the knowledge from past hands to decide how to play future hands, so it has an exploitative strategy.
What they claim here is that this bot applies a perfect exploitative strategy, or set of strategies, that exploits the bet/raise/call/check/fold ranges on every street, neglecting human tells {chat dialog, clicking speed}.
Basically they are claiming that Cepheus is the mathematical Nemesis for HU FLHE.
What they claim here is that this bot applies a perfect exploitative strategy, or set of strategies, that exploits the bet/raise/call/check/fold ranges on every street, neglecting human tells {chat dialog, clicking speed}.
Basically they are claiming that Cepheus is the mathematical Nemesis for HU FLHE.
People should stop mentioning GTO as it is supposedly refering to a possibly non exploitative strategy. This bot clearly uses the knowledge from past hands to decide how to play future hands, so it has an exploitative strategy.
What they claim here is that this bot applies a perfect exploitative strategy, or set of strategies, that exploits the bet/raise/call/check/fold ranges on every street, neglecting human tells {chat dialog, clicking speed}.
Basically they are claiming that Cepheus is the mathematical Nemesis for HU FLHE.
What they claim here is that this bot applies a perfect exploitative strategy, or set of strategies, that exploits the bet/raise/call/check/fold ranges on every street, neglecting human tells {chat dialog, clicking speed}.
Basically they are claiming that Cepheus is the mathematical Nemesis for HU FLHE.
What would a game theorist know about a game theory optimal strategy right?
Everyone, let's turn our attention to donkem here instead of listening to what these crackpot 'scientists' from the 'university' of 'alberta' have to say
Just out of curiosity, how long would it take to solve 20bb or 25bb deep HUNLHE?
If you allow more bet sizes it depends what granularity you want to have. CFR is not really well suited for NL. You can use it to solve games with many bet sizes but you lose a lot of potentially useful information so it's "dumb brute force" approach (nevertheless it's feasible if you have a lot of hardware available).
to breakeven against a gto bot, doesnt we just have to go to the river everytime and bet allin?
There are objectively good betting hands on the flop and objectively good calling hands so getting to the river in the first place without losing a lot of EV is not easy either.
What a great thread. Congrats to team cepheus with this amazing achievement.
Make the river bet 3 BB instead of 2 and they have to deal another 20 trillion hands.
Put a preflop flop out where those five cards are dead and they have to deal more than 50 quintillion.
Put a preflop flop out where those five cards are dead and they have to deal more than 50 quintillion.
If you deal 5 dead cards before every hand and put them face-up on the table, then the searching space again will be way too big to attempt to solve the whole thing (at least for a while)
Going all in on the river against a gto bot is never that bad with a hand that has no showdown value tbh (unless it's spot where gto says to check your entire range, or to a lesser extent- when you should never have a shoving range).
Bluffing with hands with bad card removal can only be a small mistake when you consider how rare the spot is- you have to arrive at some river etc on a specific board run out.
NL holdem is much more complex than limit and I doubt it will ever be strongly solved. People already mentioned an infinite number of GTO lines you can take in limit (although it could just be noise with the limping preflop etc), but I'm pretty confident that there's going to be a crazy amount of GTO betsizing/ranges in NL.
There are just many spots where it seems to me impossible for there to be exactly 1 proper betsizing/range splitting (ofc wet boards prob means many hands will be indifferent, but even something like a dry AAx board in a 3bet pot I'm sure you can have a normal size cbet and a checking range, or you could go for very small cbets with a wide range since you are oop and I don't see how it one can be vastly superior to the other).
Bluffing with hands with bad card removal can only be a small mistake when you consider how rare the spot is- you have to arrive at some river etc on a specific board run out.
NL holdem is much more complex than limit and I doubt it will ever be strongly solved. People already mentioned an infinite number of GTO lines you can take in limit (although it could just be noise with the limping preflop etc), but I'm pretty confident that there's going to be a crazy amount of GTO betsizing/ranges in NL.
There are just many spots where it seems to me impossible for there to be exactly 1 proper betsizing/range splitting (ofc wet boards prob means many hands will be indifferent, but even something like a dry AAx board in a 3bet pot I'm sure you can have a normal size cbet and a checking range, or you could go for very small cbets with a wide range since you are oop and I don't see how it one can be vastly superior to the other).
Feedback is used for internal purposes. LEARN MORE