Advertisement
depp

Bashni.org game solver

Dec 5th, 2011
1,814
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. (function() {
  2.   var spendingLimit, solutionExpectation;
  3.  
  4.   var firstWaveLevelLimit = 6;
  5.  
  6.   //solutionExpectation = 90;
  7.   //spendingLimit = 16;
  8.  
  9.   var totalVariantsLimit = 7000000;
  10.   var statStep = 100000;
  11.  
  12.   var startTime = new Date();
  13.  
  14.   var colors = [
  15.     {value: 'rgb(0, 150, 150)', name: 'azure'},
  16.     {value: 'rgb(208, 0, 0)', name: 'red'},
  17.     {value: 'rgb(0, 51, 153)', name: 'blue'},
  18.     {value: 'rgb(150, 100, 0)', name: 'brown'},
  19.     {value: 'rgb(0, 102, 0)', name: 'green'},
  20.     {value: 'rgb(198, 0, 149)', name: 'purple'},
  21.     {value: 'rgb(248, 128, 0)', name: 'orange'},
  22.     {value: 'rgb(182, 219, 222)', name: 'white'},
  23.     {value: 'rgb(231, 243, 162)', name: 'yellow'},
  24.     {value: 'rgb(102, 51, 0)', name: 'chocolate'}
  25.   ];
  26.  
  27.   var GetColorByValue = function(value) {
  28.     for(var i = 0; i < colors.length; i++)
  29.       if(colors[i].value == value) return i;
  30.   }
  31.  
  32.   //Get matrix
  33.   var matrix = [];
  34.   var table = $('table.bigcell');
  35.   $('dd', table).each(function(){
  36.     var id = this.id.split('_');
  37.     if(!(id[2] in matrix)) matrix[id[2]] = [];
  38.    
  39.     if(this.className == 'bricks') {
  40.       var num = parseInt(this.innerHTML) || 0;
  41.       //Block format - [colorIndex, topNum, bottomNum]
  42.       matrix[id[2]][id[1]] = [GetColorByValue(this.style.backgroundColor), num, num];
  43.     }
  44.   });
  45.  
  46.   //Normalize
  47.   for(var i = 0; i < matrix.length; i++) {
  48.     var column = matrix[i];
  49.    
  50.     for(var n = 0; n < column.length; n++) {
  51.       var block = column[n];
  52.      
  53.       //Concat sibling blocks
  54.       if(n > 0) {
  55.         var prev = column[n-1];
  56.         if(block[0] == prev[0] && prev[1] - block[2] == 1) {
  57.           prev[1]--;
  58.           column.splice(n, 1);
  59.           n--;
  60.         }
  61.       }
  62.     }
  63.   }
  64.  
  65.   var emptyFunction = function(){};
  66.  
  67.   var solution = null;
  68.   var matrixHash = {};
  69.   var queue = [];
  70.  
  71.   //History
  72.   var history = {
  73.     length: 0,
  74.     spending: 0,
  75.    
  76.     add: function(value) {
  77.       if(!(value.status == 2 || (value.status == 0 && value.block[2] == 6 && value.sourceSnap)))
  78.         this.spending++;
  79.       this[this.length++] = value;
  80.     },
  81.    
  82.     statusList: {
  83.       0: 'empty column',
  84.       1: 'move',
  85.       2: 'siblings'
  86.     },
  87.    
  88.     show: function() {
  89.       for(var i = 0; i < this.length; i++) {
  90.         var row = this[i];
  91.         var block = '[' + colors[row.block[0]].name + ' ' + row.block[1] + ' - ' + row.block[2] + ']';
  92.         console.log((i + 1) + '\t' + block + '\tfrom ' + (row.from + 1) + ' to ' + (row.to + 1)
  93.           + '\t(' + this.statusList[row.status] + ')');
  94.       }
  95.     },
  96.    
  97.     checkCycling: function() {
  98.       if(this.length > 1) {
  99.         var last = this[this.length - 1];
  100.         for(var n = this.length - 1; n--;) {
  101.           var row = this[n];
  102.          
  103.           if(row.block[0] == last.block[0] && row.block[2] == last.block[2]) {
  104.             //Check previous moving
  105.             if(n == this.length - 2) return false;
  106.             else {
  107.               //Check block returns (exlude empty columns)
  108.               if(last.targetSnap == row.sourceSnap && last.targetSnap != '') return false;
  109.             }
  110.           }
  111.         }
  112.       }
  113.      
  114.       return true;
  115.     },
  116.    
  117.     price: function() {
  118.       return this.length - this.spending;
  119.     },
  120.    
  121.     proto: function() {
  122.       emptyFunction.prototype = this;
  123.       return new emptyFunction();
  124.     }
  125.   }
  126.  
  127.   var totalCounter = 0;
  128.   var varCounter = 0;
  129.   var varReadyCounter = 0;
  130.  
  131.   var variant = function(matrix, history, moving) {
  132.     //First solution cap check
  133.     if(solution && (history.length >= solution.length - 1 || history.spending >= solution.spending))
  134.       return this;
  135.      
  136.     totalCounter++;
  137.    
  138.     if(totalCounter > totalVariantsLimit) return this;
  139.    
  140.     if(totalCounter%statStep == 0) ShowStat('Pending');
  141.    
  142.     this.history = history.proto();
  143.    
  144.     //Clone matrix
  145.     this.matrix = [];
  146.     for(var i = 0; i < matrix.length; i++) {
  147.       this.matrix[i] = matrix[i].slice(0);
  148.     }
  149.    
  150.     //Apply moving
  151.     if(moving) {
  152.       this.moveBlock.apply(this, moving);
  153.       if(!this.history.checkCycling()) return this;
  154.     }
  155.    
  156.     //Check siblings
  157.     this.checkSiblings();
  158.    
  159.     //Second solution cap check (after moving and siblings)
  160.     if(solution && this.history.length >= solution.length) return this;
  161.    
  162.     //Check spending limit
  163.     if(spendingLimit && this.history.spending > spendingLimit) return this;
  164.    
  165.     //Check matrix hash
  166.     var hash = [];
  167.     for(var i = 0; i < this.matrix.length; i++) hash.push(this.matrix[i].toString());
  168.     hash = hash.join('|');
  169.    
  170.     var price = this.history.spending;
  171.     if(matrixHash[hash] !== undefined && price >= matrixHash[hash]) return this;
  172.     else matrixHash[hash] = price;
  173.    
  174.     varCounter++;
  175.  
  176.     //Check ready
  177.     if(this.checkReady()) {
  178.       solution = this.history;
  179.       console.log('Solution found: ' + this.history.length);
  180.      
  181.       varReadyCounter++;
  182.       return this;
  183.     }
  184.    
  185.     this.prepared = true;
  186.   }
  187.   variant.prototype = {
  188.     findVariants: function() {
  189.       varReadyCounter++;
  190.    
  191.       for(var i = 0; i < this.matrix.length; i++) {
  192.         var column = this.matrix[i];
  193.         if(column.length == 0) continue;
  194.        
  195.         var block = column[column.length - 1];
  196.         if(block[2] == 6 && column.length == 1) continue; //skip 6-bottom single block
  197.        
  198.         for(var n = 0; n < this.matrix.length; n++) {
  199.           if(i == n) continue;
  200.          
  201.           var status = this.checkMove(block, n);
  202.           if(status == 0 && column.length == 1) continue; //skip moving between empty columns
  203.          
  204.           if(status >= 0) {
  205.             var branch = new variant(this.matrix, this.history, [block, i, n, status]);
  206.             if(branch.prepared) {
  207.               if(branch.history.spending < firstWaveLevelLimit) branch.findVariants();
  208.               else {
  209.                 var price = branch.history.price();
  210.                 if(!queue[price]) queue[price] = [];
  211.                 queue[price].push(branch);
  212.               }
  213.             }
  214.           }
  215.         }
  216.       }
  217.     },
  218.  
  219.     checkReady: function() {
  220.       var emptyCount = 0;
  221.       for(var i = 0; i < this.matrix.length; i++) {
  222.         if(this.matrix[i].length > 1) return false;
  223.         if(this.matrix[i].length == 0) emptyCount++;
  224.       }
  225.      
  226.       return emptyCount == 2;
  227.     },
  228.  
  229.     checkSiblings: function() {
  230.       var movingsCount = 0;
  231.    
  232.       main:for(var i = 0; i < this.matrix.length; i++) {
  233.         var column = this.matrix[i];
  234.        
  235.         if(column.length > 0) {
  236.           var block = column[column.length - 1];
  237.          
  238.           for(var n = 0; n < this.matrix.length; n++) {
  239.             if(n == i) continue;
  240.            
  241.             if(this.checkMove(block, n) == 2 && block[1] <= 1) {
  242.               this.moveBlock(block, i, n, 2);
  243.               movingsCount++;
  244.               continue main;
  245.             }
  246.           }
  247.         }
  248.       }
  249.      
  250.       if(movingsCount > 0) arguments.callee.call(this);
  251.     },
  252.    
  253.     moveBlock: function(block, sourceColumnIndex, targetColumnIndex, status) {
  254.       var sourceColumn = this.matrix[sourceColumnIndex];
  255.       var targetColumn = this.matrix[targetColumnIndex];
  256.      
  257.       sourceColumn.length--;
  258.      
  259.       var targetSnap = targetColumn.toString();
  260.      
  261.       if(status == 2) {
  262.         var last = targetColumn[targetColumn.length - 1];
  263.         targetColumn[targetColumn.length - 1] = [last[0], block[1], last[2]];
  264.       }
  265.       else targetColumn.push(block);
  266.      
  267.       this.history.add({
  268.         block: block,
  269.         from: sourceColumnIndex,
  270.         to: targetColumnIndex,
  271.         status: status,
  272.         sourceSnap: sourceColumn.toString(),
  273.         targetSnap: targetSnap
  274.       });
  275.     },
  276.    
  277.     // 0 - empty column, -1 - can't move, 1 - can move, 2 - siblings
  278.     checkMove: function(block, targetColumnIndex) {
  279.       var targetColumn = this.matrix[targetColumnIndex];
  280.      
  281.       if(targetColumn.length == 0) return 0;
  282.      
  283.       var last = targetColumn[targetColumn.length - 1];
  284.       if(block[0] == last[0] && block[2] < last[1])
  285.         return (block[2] == last[1] - 1) ? 2 : 1;
  286.      
  287.       return -1;
  288.     },
  289.   };
  290.  
  291.   function FormatNumber(num) {
  292.     var num = num.toString();
  293.     var result = [];
  294.     for(var l = num.length; l--; ) {
  295.       result.push(num.charAt(l));
  296.       if((num.length - l)%3 == 0 && l > 0) result.push(' ');
  297.     }
  298.     return result.reverse().join('');
  299.   }
  300.  
  301.   function ShowStat(text) {
  302.     console.log(text
  303.       + ' || Created variants total: ' + FormatNumber(totalCounter)
  304.       + ', useful: ' + FormatNumber(varCounter)
  305.       + ', processed: ' + FormatNumber(varReadyCounter)
  306.       + ', queue size: ' + FormatNumber(varCounter - varReadyCounter)
  307.     );
  308.   }
  309.  
  310.   console.log('Start solving');
  311.  
  312.   //Run first wave
  313.   var root = new variant(matrix, history);
  314.   root.findVariants();
  315.  
  316.   ShowStat('Initial wave');
  317.  
  318.   if(!solution) {
  319.     var i = 100000000;
  320.     while(i--) {
  321.       if(solutionExpectation && solution && solution.length <= solutionExpectation) {
  322.         ShowStat('Solution with the expected length was found');
  323.         break;
  324.       }
  325.    
  326.       var n = queue.length;
  327.       if(n == 0) {
  328.         ShowStat('Queue is over');
  329.         break;
  330.       }
  331.      
  332.       while(n--) {
  333.         var row = queue[n];
  334.         if(row && row.length) {
  335.           row.shift().findVariants();
  336.           break;
  337.         }
  338.         else queue.length--;
  339.       }
  340.     }
  341.   }
  342.  
  343.   var totalTime = (new Date() - startTime) / 1000;
  344.   console.log('Total time: ' + Math.round(totalTime / 60) + 'm ' + Math.round(totalTime % 60) + 's');
  345.  
  346.   //Help GC to free the memory
  347.   matrixHash = null;
  348.   queue = null;
  349.  
  350.   if(solution) {
  351.     solution.show();
  352.    
  353.     if(confirm('Run solution: ' + solution.length + '?')) {
  354.       var area = $('table.bigcell tr:first');
  355.      
  356.       var i = 0;      
  357.       var solver = function() {
  358.         var step = solution[i];
  359.      
  360.         $('td:nth-child(' + (parseInt(step.from) + 1) + ') dd', area).trigger('click');
  361.         $('td:nth-child(' + (parseInt(step.to) + 1) + ') dd', area).trigger('click');
  362.        
  363.         if(i < solution.length - 1) {
  364.           i++;
  365.           setTimeout(solver, 500);
  366.         }
  367.       }
  368.      
  369.       solver();
  370.     }
  371.   }
  372. })();
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement