Algorithm to find optimal groups in 2D array -


i have deck of 24 cards - 8 red, 8 blue , 8 yellow cards.

red    |1|2|3|4|5|6|7|8| yellow |1|2|3|4|5|6|7|8| blue   |1|2|3|4|5|6|7|8| 

i can take 3 of cards (same numbers, straight, straigh flush), whereas each of type scored differently. question is, how calculate maximal possible score (find optimal groups) game in progress, cards missing. example:

red    |1|2|3|4|5|6|7|8| yellow |1|2|3| |5| |7|8| blue   |1|2| |4|5|6| |8| 

the score three-of-a-kind is:

1-1-1    20   2-2-2    30   3-3-3    40   4-4-4    50   5-5-5    60   6-6-6    70   7-7-7    80   8-8-8    90   

the score straight is:

1-2-3    10   2-3-4    20   3-4-5    30   4-5-6    40   5-6-7    50   6-7-8    60   

the score straight flush is:

1-2-3    50   2-3-4    60   3-4-5    70   4-5-6    80   5-6-7    90   6-7-8   100  

a solution recursively tries every combination go this:

start looking @ combinations have red 8 highest card: three-of-a-kind r8-y8-b8, straight flush r6-r7-r8, , every possible straight *6-*7-r8. each of these, remove cards set, , recurse check combinations yellow 8, blue 8, red 7, yellow 7, blue 7, red 6 ... until you've checked except 2's , 1's; add three-of-a-kind 2-2-2 , 1-1-1 if available. @ each step, check recursion returns maximum score, , return maximum.


let's @ happens in each of these steps. we're looking @ combinations red 8; have available cards like:

red    ...|6|7|8| yellow ...|6| |8| blue   ...| |7|8| 

first, use three-of-a-kind r8-y8-b8, if possible. create copy of available cards, remove 8's, , recurse straight 7's:

score = 90 + max_score(cards_copy, next = red 7) 

(trying three-of-a-kind should done when current card red, avoid duplicate solutions.)

then, use straight flush r6-r7-r8, if possible. create copy of available cards, remove r6, r7 , r8, , recurse yellow 8:

score = 100 + max_score(cards_copy, next = yellow 8) 

then, use every possible non-flush straight containing red 8; in example, r6-b7-r8, y6-r7-r8 , y6-b7-r8 (there nine). each of these, create copy of available cards, remove 3 cards , recurse yellow 8:

score = 60 + max_score(cards_copy, next = yellow 8) 

then, finally, recurse without using red 8: create copy of available cards, remove red 8 , recurse yellow 8:

score = max_score(cards_copy, next = yellow 8) 

you calculate of these options has greatest score (with score returned recursion added), , return maximum score.


a quick test in javascript shows full set of 24 cards, algorithm goes through 30 million recursions find maximum score 560, , becomes quite slow. however, 3 higher-value cards have been removed, number of recursions falls below 1 million , takes around 1 second, , 6 higher-value cards removed, falls below 20,000 , returns instantly.

for almost-complete sets, pre-compute maximum scores, , calculate score once number of cards have been removed. lot of sets duplicates anyway; removing r6-r7-r8 result in same maximum score removing y6-y7-y8; removing r6-y7-b8 duplicate of removing b6-y7-r8... first change input canonical version, , pre-computed score. e.g. using pre-computed scores sets 3 or 6 cards removed require storing 45,340 scores.


as code example, here's javascript code tested algorithm with:

function clone(array) {                                   // copy 2-dimensional array      var copy = [];      array.foreach(function(item) {copy.push(item.slice())});      return copy;  }  function max_score(cards, suit, rank) {      suit = suit || 0; rank = rank || 7;                             // start @ red 8      var max = 0;      if (rank < 2) {                               // try 3-of-a-kind rank 1 , 2          if (cards[0][0] && cards[1][0] && cards[2][0]) max += 20;          if (cards[0][1] && cards[1][1] && cards[2][1]) max += 30;          return max;      }      var next_rank = suit == 2 ? rank - 1: rank;      var next_suit = (suit + 1) % 3;      max = max_score(clone(cards), next_suit, next_rank);    // try skipping card      if (! cards[suit][rank]) return max;      if (suit == 0 && cards[1][rank] && cards[2][rank]) {           // try 3-of-a-kind          var score = rank * 10 + 20 + max_score(clone(cards), 0, rank - 1);          if (score > max) max = score;      }      (var = 0; < 3; i++) {                       // try possible straights          if (! cards[i][rank - 2]) continue;          (var j = 0; j < 3; j++) {              if (! cards[j][rank - 1]) continue;              var copy = clone(cards);              copy[j][rank - 1] = 0; copy[i][rank - 2] = 0;              var score = rank * 10 - 10 + max_score(copy, next_suit, next_rank);              if (i == suit && j == suit) score += 40;    // straight straight flush              if (score > max) max = score;          }      }      return max;  }  document.write(max_score([[1,1,1,1,1,0,1,1], [1,1,1,1,1,1,1,0], [1,1,1,0,1,1,1,1]]));

an obvious way speed algorithm use 24-bit pattern instead of 3x8 bit array represent cards; way array cloning no longer necessary, , of code turned bit manipulation. in javascript, it's 8 times faster:

function max_score(cards, suit, rank) {      suit = suit || 0; rank = rank || 7;                             // start @ red 8      var max = 0;      if (rank < 2) {                               // try 3-of-a-kind rank 1 , 2          if ((cards &  65793) ==  65793) max += 20;     // 65793 = rank 1 of suits          if ((cards & 131586) == 131586) max += 30;    // 131586 = rank 2 of suits          return max;      }      var next_rank = suit == 2 ? rank - 1: rank;      var next_suit = (suit + 1) % 3;      var this_card = 1 << rank << suit * 8;      max = max_score(cards, next_suit, next_rank);           // try skipping card      if (! (cards & this_card)) return max;      if (suit == 0 && cards & this_card << 8 && cards & this_card << 16) { // try 3oak          var score = rank * 10 + 20 + max_score(cards, 0, rank - 1);          if (score > max) max = score;      }      (var = 0; < 3; i++) {                       // try possible straights          var mid_card = 1 << rank - 1 << * 8;          if (! (cards & mid_card)) continue;          (var j = 0; j < 3; j++) {              var low_card = 1 << rank - 2 << j * 8;              if (! (cards & low_card)) continue;              var cards_copy = cards - mid_card - low_card;              var score = rank * 10 - 10 + max_score(cards_copy, next_suit, next_rank);              if (i == suit && j == suit) score += 40;    // straight straight flush              if (score > max) max = score;          }      }      return max;  }  document.write(max_score(parseint("111101110111111111011111", 2)));  //                                 b       y       r  //                                 876543218765432187654321

the speed almost-complete sets can further improved using observation if straight flush 3 suits can be made current rank, best option. reduces number of recursions drastically, because 9 cards can skipped @ once. check should added after trying 3-of-a-kind rank 1 , 2:

    if (suit == 0) {                              // try straight flush suits         var flush3 = 460551 << rank - 2;     // 460551 = rank 1, 2 , 3 of suits         if ((cards & flush3) == flush3) {             max = rank * 30 + 90;             if (rank > 2) max += max_score(cards - flush3, 0, rank - 3);             return max;         }     } 

Comments

Popular posts from this blog

android - InAppBilling registering BroadcastReceiver in AndroidManifest -

python Tkinter Capturing keyboard events save as one single string -

sql server - Why does Linq-to-SQL add unnecessary COUNT()? -