3
\$\begingroup\$

I have the following code (it looks long but it's not too bad):

function evaluate_cards(hand){
    var hand_length = hand.length;
    var num_of_sets = 0;
    var num_of_runs = 0;
    var cards_in_run = 0;
    var suit = '';
    var buckets = {
         three_bucket: [],
         four_bucket: [],
         five_bucket: [],
         six_bucket: [],
         seven_bucket: [],
         eight_bucket: [],
         nine_bucket: [],
         ten_bucket: [],
         j_bucket: [],
         q_bucket: [],
         k_bucket: [],
         w_bucket: [] //bucket for wilds 
    }

    for(var i = 0; i < hand_length; i++){
        if(hand[0].is_wild){
            buckets.w_bucket.push(hand.splice(0, 1));
        }else if(hand[0].value == 3){
            buckets.three_bucket.push(hand.splice(0, 1));   
        }else if(hand[0].value == 4){
            buckets.four_bucket.push(hand.splice(0, 1));    
        }else if(hand[0].value == 5){
            buckets.five_bucket.push(hand.splice(0, 1));    
        }else if(hand[0].value == 6){
            buckets.six_bucket.push(hand.splice(0, 1)); 
        }else if(hand[0].value == 7){
            buckets.seven_bucket.push(hand.splice(0, 1));   
        }else if(hand[0].value == 8){
            buckets.eight_bucket.push(hand.splice(0, 1));   
        }else if(hand[0].value == 9){
            buckets.nine_bucket.push(hand.splice(0, 1));    
        }else if(hand[0].value == 10){
            buckets.ten_bucket.push(hand.splice(0, 1)); 
        }else if(hand[0].value == 11){
            buckets.j_bucket.push(hand.splice(0, 1));   
        }else if(hand[0].value == 12){
            buckets.q_bucket.push(hand.splice(0, 1));   
        }else if(hand[0].value == 13){
            buckets.k_bucket.push(hand.splice(0, 1));   
        }
    }

    for(var k in buckets){
        if(buckets.hasOwnProperty(k)){
            if(buckets[k].length >= 3 && buckets[k] != buckets.w_bucket){
                num_of_sets++;  
            }else if(buckets.w_bucket.length > 0 && buckets[k].length == 2){
                buckets[k].push(buckets.w_bucket.shift());
                num_of_sets++;
            }else if(buckets[k].length < 3 && buckets.w_bucket.length > 1){
                    for(var i = 0; i < buckets.w_bucket.length; i++){
                        buckets[k].push(buckets.w_bucket.shift());
                    }
                    if(buckets[k].length >= 3){
                        num_of_sets++;
                    }
            }else if(num_of_sets == 0){
                if(buckets[k].length > 0){
                    if(suit == ''){
                        suit = buckets[k][0].suit;      
                    }else if(suit == buckets[k][0].suit){
                        cards_in_run++; 
                    }else{
                        suit = '';
                    }
                    if(cards_in_run != 0 && cards_in_run < 3 && buckets.w_bucket.length != 0){
                        cards_in_run++; 
                    }
                }
                if(cards_in_run >= 3){
                    num_of_runs++;
                    cards_in_run = 0;   
                }
            }   
        }
    }       
    return {'runs':num_of_runs, 'sets': num_of_sets};       
}

It's based off of the second answer to this question on game-dev.se. There are two major differences, however.

  1. There are wilds in my game, which makes finding things like runs much more difficult.
  2. Most sets/runs are going to be 3 or four cards but could be up to 5 cards, obviously anything 6 and over can be divided (set or run has to be a minimum of 3 cards).

The following is passed a hand will determine almost all sets of three cards, but only a few runs.

My request is that it be reviewed for optimization and where my logic goes wrong with runs. I'm totally open to any suggestions.

\$\endgroup\$
4
\$\begingroup\$

Your sorting of cards to buckets takes a lot of code, which is because the names of your buckets don't match with the names of cards. The cards have just numeric values (10, 11), while buckets are named with words (ten_bucket, j_bucket). I would represent the cards as such:

{kind: 3, suit: "club"},
{kind: 10, suit: "heart"},
{kind: "K", suit: "spade"},
{wild: true},

And then I could write a much simpler algorithm for putting them to buckets:

var wilds = [];
var kinds = {3:[], 4:[], 5:[], 6:[], 7:[], 8:[], 9:[], 10:[], J:[], Q:[], K:[]};

hand.forEach(function(card) {
    if (card.wild) {
        wilds.push(card);
    } else {
        kinds[card.kind].push(card);
    }
});

I'm also handling the wild cards as a completely separate category, which makes it much easier to check if we have three-of-a-kind:

for (var k in kinds){
    if (kinds.hasOwnProperty(k)){
        if (kinds[k].length + wilds.length >= 3) {
            // Yes, we have a three-of-a-kind
        }
    }
}

Checking for runs

A basic check for runs is quite simple. You just loop through all the buckets in order and check for the longest sequence without holes:

function longest_run() {
    var count = 0;
    var max_run = 0;
    ["3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"].forEach(function(k) {
        if (kinds[k].length >= 1) {
            count += 1;
        } else {
            if (count > max_run) {
                max_run = count;
            }
            count = 0;
        }
    });
    return max_run;
}

But taking the wild cards into account complicates the whole thing considerably. Now it really becomes a question of combinatorics. A brute-force approach would be to try using the wildcards in each of the empty positions. This can be optimized of course, but I suggest you turn to StackOverflow with this question - some math-minded people might be able to provide you with a better algorithm over there.

\$\endgroup\$
  • \$\begingroup\$ Thanks, I hadn't thought of that, though my card structure was already set up for me to do so. Do you have any thoughts on checking for runs? That's really where my logic has an issue. \$\endgroup\$ – Ryan Sep 27 '13 at 19:48
  • \$\begingroup\$ I've updated the answer with an example for finding runs, though further discussion of this is better suited for StackOverflow. \$\endgroup\$ – Rene Saarsoo Sep 30 '13 at 11:00
  • \$\begingroup\$ There's not much benefit to having an array of {wild: true} objects over having a simple integer count. \$\endgroup\$ – Peter Taylor Sep 30 '13 at 14:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.