INFO LOWONGAN KERJA TERBARU KLIK DISINI

Software Engineer Explore Unproblematic Game Algorithms Amongst Coloring Walk: Exercise 6

What's side past times side alongside exploring dissimilar game algorithms using the uncomplicated game last post showed how far nosotros could force the greedy algorithm to aspect ahead as well as essay to pick a meliorate motion that would effect inward to a greater extent than blocks removed upward to 5 moves ahead, but fifty-fifty after improving the information construction for the board, nosotros hitting diminishing returns inward looking to a greater extent than moves ahead. It's fourth dimension to expand our search for algorithms past times looking at other heuristics nosotros tin give notice utilization to pick the best move. The greedy algorithm used a heuristic of "the most blocks removed" for whatsoever given move, but in that location are others that nosotros tin give notice use.

Expand the Perimeter


The greedy algorithm's heuristic tin give notice live considered an expanse heuristic. Each block is ane unit of measurement of area, as well as removing blocks increases the amount of empty expanse on the board. The greedy algorithm tries to maximize that empty expanse on every motion (or eventually when looking ahead to time to come moves). Instead of maximizing area, nosotros could take hold it essay to maximize something else, as well as the most obvious thing after expanse to maximize is the perimeter. Take the next film of the start of a game, for example:

s side past times side alongside exploring dissimilar game algorithms using the uncomplicated game software engineer Explore Simple Game Algorithms alongside Color Walk: Part 6

If nosotros aspect at night blueish equally the side past times side move, therefore nosotros would expand the expanse of the empty infinite past times iv blocks. Instead of the area, nosotros could aspect at the perimeter of these blocks as well as run across that past times removing them, nosotros take hold exposed 7 novel perimeter lengths of empty space. Influenza A virus subtype H5N1 "perimeter length" is just an border of a block, for the purposes of counting. Influenza A virus subtype H5N1 removed block tin give notice add together upward to 3 lengths to the novel perimeter, such equally that solitary pinkish block side past times side to the empty infinite inward the film above. Adding that pinkish block's perimeter to the 'L' shaped pinkish cluster below results inward nine novel perimeter lengths if pinkish is chosen on the side past times side move, as well as that would live the motion to chose if nosotros were maximizing perimeter.

To count the perimeter, nosotros wishing to brand for sure we're non counting edges along the empty infinite because those edges don't matter. They won't live a component subdivision of the novel perimeter if the corresponding blocks were removed, but rather, they would live within the expanded empty space. To assess how much the perimeter has expanded, nosotros wishing to only count edges that would withal be in ane lawsuit the blocks were removed. Now that nosotros take hold an see of what this novel heuristic should do, let's aspect at how to implement it.

You to a greater extent than oft than non take hold ii choices for how to continue when adding a feature: bottom-up or top-down. We tin give notice start either from the bottom alongside adding the novel heuristic count metric, or at the top alongside adding the novel max-perimeter algorithm selection to the listing of options. Let's start at the bottom as well as construct upward this time. First, we'll add together novel functions called areaCount() and perimeterCount() to the full general functions available exterior whatsoever object:
  component subdivision areaCount() {     provide _.filter(blocks, component subdivision (block) {       provide (0 !== block.marked);     }).length;   }    component subdivision perimeterCount() {     var count _.reduce(blocks, component subdivision (accum, block) {       if (block.marked === 0) provide accum;       provide accum + _.filter(block.getNeighbors(), component subdivision (neighbor) {         provide blocks[neighbor].marked === 0 && !blocks[neighbor].isDead;       }).length;     }, 0);      if (count === 0) provide areaCount();     provide count;   }
The areaCount() component subdivision does the piece of work of counting marked blocks from the master copy greedy algorithm heuristic, as well as it's just a packaging upward of code we've already seen into a function. The perimeterCount() component subdivision does all of the dingy piece of work of counting perimeter edges for the novel heuristic. The outer _.reduce() performance scans through all blocks accumulating border counts. For each block, if the block isn't marked, nosotros just ignore it as well as provide the accumulator unchanged. For whatsoever block that is marked (meaning, the block is existence considered for removal), nosotros wishing to _.filter() all of it's neighbors as well as count the ones that are non marked as well as non dead. That combination identifies those blocks that are non currently existence considered for removal or component subdivision of the empty space. Any such block that's a vecino of a marked block volition live on an border that makes upward the novel perimeter, therefore nosotros count it. It should live clear from this component subdivision that it volition piece of work inward full general whether nosotros are looking ane motion ahead or more, therefore nosotros tin give notice utilization this heuristic inward a look-ahead version of max-perimeter, too.

Notice that the perimeter instance volition autumn dorsum on areaCount() if in that location is no novel perimeter count found. This province of affairs happens close the goal of a game, when all of the blocks that are left on the board are disjoint. If nosotros didn't autumn dorsum on areaCount(), the algorithm would acquire stuck inward an infinite loop, picking the firstly color indefinitely since all of the colors effect inward aught novel perimeter count. When that happens, nosotros wishing to become ahead as well as pick the color that removes the most blocks to complete upward the game.

If yous also noticed the telephone scream upward to block.getNeighbors() as well as wondered where that came from, the component subdivision getNeighbors() was moved from Cluster to Block, because it definitely makes to a greater extent than feel to take hold it inward the Block:
  component subdivision Block(x, y, color, i, isDead) {     // ...      this.getNeighbors = function() {       var neighbors = [];       var i = this.position;       if (i % grid_length > 0) {         neighbors.push(i - 1);       }       if (i % grid_length + 1 < grid_length) {         neighbors.push(i + 1);       }       if (i - grid_length > 0) {         neighbors.push(i - grid_length);       }       if (i + grid_length + 1 < grid_length * grid_height) {         neighbors.push(i + grid_length);       }        provide neighbors;     };   }
Naturally, the other calls to getNeighbors() postulate to live changed to block.getNeighbors() as well. Now this component subdivision tin give notice live called from exterior of Cluster so nosotros tin give notice utilization it inward the perimeterCount() metric to count perimeter edges.

What we're genuinely doing hither is adding a novel alternative to the greedy algorithm instead of creating an solely novel algorithm. To utilization this novel perimeterCount() function, nosotros wishing to telephone scream upward it at the goal of Control.checkGameBoard() where nosotros were counting the removed blocks before:
    this.checkGameBoard = function(check_move, metric) {       _.each(blocks, component subdivision (block) {         if (block.marked >= check_move) {           block.marked = 0;         }       });        blocks[0].cluster.markNeighbors(this.color, check_move);        provide metric();     }
Here nosotros changed the code that counts marked blocks to instead telephone scream upward a component subdivision that describes which metric nosotros wishing to utilization when assessing the marked blocks. Currently those functions tin give notice live areaCount() or perimeterCount(), but nosotros tin give notice easily add together to a greater extent than inward the future. All of the places that used to telephone scream upward checkGameBoard() similar a shot postulate to pass areaCount equally the concluding declaration as well as the default metric to use, as well as we'll live adding a novel telephone scream upward alongside perimeterCount as the declaration for the max-perimeter algorithm inward Solver:
  component subdivision Solver() {     // ...      this.maxPerimeter = function() {       var max_control = _.max(controls, function(control) {         provide control.checkGameBoard(1, perimeterCount);       });       this.index = max_control.index;     }   }
This is the beauty of functional programming. We're just passing unopen to the component subdivision that nosotros wishing to utilization for a for sure action, as well as it ends upward existence wonderfully clean, elegant, as well as flexible. This algorithm nosotros just created happens to take hold the exact same construction equally the greedy algorithm, therefore nosotros tin give notice genuinely modify the Solver.greedy() component subdivision to adapt both metrics at once:
    this.greedy = function() {       var max_control = _.max(controls, function(control) {         provide control.checkGameBoard(1, that.metric);       });       this.index = max_control.index;     }
And therefore nosotros laid the metric used inward this algorithm inward the switch declaration that selects the algorithm to run, along alongside adding inward the novel instance for the max-perimeter algorithm:
      $('#solver_type').change(function () {         switch (this.value) {           // ...           instance 'greedy':             that.solverType = that.greedy;             that.metric = areaCount;             break;           instance 'greedy-look-ahead':             that.solverType = that.greedyLookAhead;             that.metric = areaCount;             break;           instance 'max-perimeter':             that.solverType = that.greedy;             that.metric = perimeterCount;             break;           default:             that.solverType = that.roundRobin;             break;         }
With the novel algorithm added, nosotros just postulate to add together max-perimeter to the listing of algorithms inward the UI, as well as nosotros tin give notice essay it out:

s side past times side alongside exploring dissimilar game algorithms using the uncomplicated game software engineer Explore Simple Game Algorithms alongside Color Walk: Part 6

Surprisingly, this novel heuristic outperforms the base of operations area-counting heuristic for the greedy algorithm. It also has some interesting conduct that tin give notice live seen spell it's running. Quite oft it volition brand choices inward moves that goal upward leaving ane block color on the board for an extended laid of moves spell it clears away the other colors:


This delayed clearing of ane color may assist explicate why this metric does meliorate than area-counting alongside no look-ahead, because ane color selection is saved spell the others are existence cleared, as well as a few moves of that saved color tin give notice live combined into ane color-clearing move. This strategy plant good plenty that it's worth nigh equally much equally looking ane to a greater extent than motion ahead, equally tin give notice live seen when stacking upward the results alongside the other algorithms:

AlgorithmMinMeanMaxStdev
RR alongside Skipping 37 46.9 59 4.1
Random alongside Skipping 43 53.1 64 4.5
Greedy 31 39.8 48 3.5
Greedy Look-Ahead-2 28 37.0 45 3.1
Greedy Look-Ahead-3 25 34.2 40 2.7
Greedy Look-Ahead-4 25 33.3 39 2.6
Greedy Look-Ahead-5 25 33.1 41 2.8
Max Perimeter 29 37.4 44 3.2

This just goes to present that it's worthwhile to essay out dissimilar things as well as run across how they work. I wasn't expecting a dissimilar heuristic to brand much of a departure here, but max-perimeter was genuinely significant. It should live interesting to run across what happens when nosotros extend the max-perimeter heuristic to look-ahead, which is similar a shot quite piece of cake to do.

Max Perimeter Look-Ahead


Since the max-perimeter algorithm is a heuristic that already plant alongside the greedy algorithm, all nosotros take hold to practice to acquire it working alongside the greedy look-ahead algorithm is modify Solver.greedyLookAhead() to utilization the metric that's existence laid inward the switch statement:
    this.greedyLookAhead = function() {       var max_control = _.max(controls, function(control) {         if (control.checkGameBoard(1, that.metric) === 0) {           provide 0;         }         provide greedyLookAheadN(2);       });       this.index = max_control.index;     }
And nosotros brand the same alter inward greedyLookAheadN(), since that component subdivision also calls Control.checkGameBoard():
    component subdivision greedyLookAheadN(move) {       provide _.max(_.map(controls, function(control) {         var matches = control.checkGameBoard(move, that.metric);         if (matches === 0 || motion >= max_moves) {           provide matches;         }         provide greedyLookAheadN(move + 1);       }));     }
Finally, nosotros tin give notice add together the max-perimeter look-ahead algorithm to the switch declaration as well as to the UI algorithm list:
      $('#solver_type').change(function () {         switch (this.value) {           // ...           instance 'max-perimeter':             that.solverType = that.greedy;             that.metric = perimeterCount;             break;           instance 'max-perimeter-look-ahead':             that.solverType = that.greedyLookAhead;             that.metric = perimeterCount;             break;           default:             that.solverType = that.roundRobin;             break;         }
And voila, nosotros already take hold some other algorithm that nosotros tin give notice evaluate for multiple levels of look-ahead. After running upward to look-ahead-by-5, the next tabular array shows how the max-perimeter look-ahead algorithm compares to the master copy greedy algorithm:

AlgorithmMinMeanMaxStdev
RR alongside Skipping 37 46.9 59 4.1
Random alongside Skipping 43 53.1 64 4.5
Greedy 31 39.8 48 3.5
Greedy Look-Ahead-2 28 37.0 45 3.1
Greedy Look-Ahead-3 25 34.2 40 2.7
Greedy Look-Ahead-4 25 33.3 39 2.6
Greedy Look-Ahead-5 25 33.1 41 2.8
Max Perimeter 29 37.4 44 3.2
Max Perimeter Look-Ahead-2 27 35.0 44 2.8
Max Perimeter Look-Ahead-3 27 35.0 41 2.9
Max Perimeter Look-Ahead-4 26 34.8 43 3.3
Max Perimeter Look-Ahead-5 28 34.9 46 2.9

The look-ahead-by-2 run looks meliorate than the corresponding greedy look-ahead-by-2 run alongside area-counting, but beyond that, the max-perimeter heuristic seems to hitting a wall of diminishing returns, as well as it tin give notice never quite acquire equally skillful equally the later on greedy area-counting runs. This is useful information, as well as in that location may live a reasonable explanation for this lagging performance.

The max-perimeter heuristic seems to live to a greater extent than efficient, reaching meliorate performance without looking equally far ahead, but it seems to endure close the goal of the game. At the goal of the game, when the board is mostly cleared as well as in that location are only a few colored blocks left, in that location won't live much of whatsoever novel perimeter made alongside whatsoever given motion choice. It's to a greater extent than of import to increment the perimeter quickly early on inward the game, as well as later on inward the game nosotros wishing to complete off alongside removing equally many blocks equally possible on each move. This strategy calls for a hybrid approach, where nosotros utilization the max-perimeter heuristic for the first, oh twenty moves, let's say, as well as therefore switch to the area-counting heuristic to complete it off. We tin give notice easily brand such a heuristic to explore this hypothesis:
  component subdivision perimeterAreaHybrid() {     if (moves >= 20) provide areaCount();     provide perimeterCount();   }
And nosotros add together this hybrid heuristic to the listing of choices:
      $('#solver_type').change(function () {         switch (this.value) {           // ...           instance 'perimeter-area':             that.solverType = that.greedy;             that.metric = perimeterAreaHybrid;             break;           instance 'perimeter-area-look-ahead':             that.solverType = that.greedyLookAhead;             that.metric = perimeterAreaHybrid;             break;           default:             that.solverType = that.roundRobin;             break;         }
Now nosotros tin give notice run it as well as run across how it compares to the other algorithms:

AlgorithmMinMeanMaxStdev
RR alongside Skipping 37 46.9 59 4.1
Random alongside Skipping 43 53.1 64 4.5
Greedy 31 39.8 48 3.5
Greedy Look-Ahead-2 28 37.0 45 3.1
Greedy Look-Ahead-3 25 34.2 40 2.7
Greedy Look-Ahead-4 25 33.3 39 2.6
Greedy Look-Ahead-5 25 33.1 41 2.8
Max Perimeter 29 37.4 44 3.2
Max Perimeter Look-Ahead-2 27 35.0 44 2.8
Perimeter-Area Hybrid 31 39.0 49 3.8
Perim-Area Hybrid Look-Ahead-2 27 35.2 43 3.2
Perim-Area Hybrid Look-Ahead-3 27 33.5 41 2.7
Perim-Area Hybrid Look-Ahead-4 26 33.2 41 3.1
Perim-Area Hybrid Look-Ahead-5 28 33.0 41 2.5

Again, I'm surprised at how this algorithm performs, but this fourth dimension it doesn't piece of work equally good equally I see it would. It does seem to perform a fleck meliorate than the max-perimeter look-ahead-by-2 version in ane lawsuit it gets to look-ahead-by-3, but I was expecting it to practice meliorate than the greedy look-ahead-by-3 as well as later on versions as well as it never quite achieves that. Maybe the hybrid approach doesn't piece of work equally good equally I'd hoped, or we're just reaching the natural limits of what an algorithm tin give notice practice inward this game. Let's explore some other avenue.

Constructing a Deep-Path Algorithm


The see of clearing a deep path of blocks into the game board earlier expanding outward is some other strategy that has some potential to piece of work well. In manual attempts at playing the game, I constitute that this strategy of making a path deep into the pump of the board firstly to a greater extent than oft than non worked pretty good for clearing the board inward a lower divulge of moves, as well as I could easily acquire below 35 moves when I drove towards the middle of the board first.

We should live able to covert this strategy inward some other heuristic to utilization alongside the greedy algorithm, as well as it seems somewhat similar to maximizing perimeter. We tin give notice accentuate that type of search past times trying to maximize the perimeter-area ratio, which should give added weight to color choices that effect inward long, narrow paths into the board. The corresponding metric component subdivision is straightforward:
  component subdivision ratioCalc() {     var expanse = areaCount();     if (area === 0) provide area;     provide perimeterCount() / area;   }
Here nosotros are careful to non dissever past times zero, as well as nosotros tin give notice add together a duo to a greater extent than algorithms to the listing of choices:
      $('#solver_type').change(function () {         switch (this.value) {           // ...           instance 'deep-path':             that.solverType = that.greedy;             that.metric = ratioCalc;             break;           instance 'deep-path-look-ahead':             that.solverType = that.greedyLookAhead;             that.metric = ratioCalc;             break;           default:             that.solverType = that.roundRobin;             break;         }
This heuristic is fascinating because it does pretty much precisely what I wanted it to do. Take a aspect at an instance game inward progress:

s side past times side alongside exploring dissimilar game algorithms using the uncomplicated game software engineer Explore Simple Game Algorithms alongside Color Walk: Part 6

Deep, narrow paths are made into the blocks, equally desired, but the algorithm performs terribly. Take a aspect at some runs alongside dissimilar look-aheads compared to the other algorithms:

AlgorithmMinMeanMaxStdev
RR alongside Skipping 37 46.9 59 4.1
Random alongside Skipping 43 53.1 64 4.5
Greedy 31 39.8 48 3.5
Greedy Look-Ahead-2 28 37.0 45 3.1
Greedy Look-Ahead-3 25 34.2 40 2.7
Max Perimeter 29 37.4 44 3.2
Max Perimeter Look-Ahead-2 27 35.0 44 2.8
Perimeter-Area Hybrid 31 39.0 49 3.8
Deep-Path 51 74.8 104 9.4
Deep-Path Look-Ahead-2 50 74.9 112 9.8
Deep-Path Look-Ahead-3 50 75.2 112 9.8
Deep-Path Look-Ahead-4 50 75.4 112 9.5

It doesn't fifty-fifty improve equally it looks farther as well as farther ahead. The work is probable that spell it may live a skillful see to create a deep path at the kickoff of the game, it's a waste product of moves to maintain doing it on the 30th move, as well as definitely has run its course of report past times the 70th move. It may live worth seeing if using this heuristic only inward the kickoff volition improve things alongside a hybrid heuristic similar to the perimeter-area hybrid approach nosotros tried before. We tin give notice brand a uncomplicated heuristic to essay out this theory:
  component subdivision ratioAreaHybrid() {     if (moves >= 12) provide areaCount();     provide ratioCalc();   }
I picked a motion threshold of 12 just past times looking at where the deep path to a greater extent than oft than non stops usefully extending when clicking through a game manually using the deep-path heuristic. We tin give notice also add together a duo to a greater extent than choices to the listing of algorithms:
      $('#solver_type').change(function () {         switch (this.value) {           // ...           instance 'path-area':             that.solverType = that.greedy;             that.metric = ratioAreaHybrid;             break;           instance 'path-area-look-ahead':             that.solverType = that.greedyLookAhead;             that.metric = ratioAreaHybrid;             break;           default:             that.solverType = that.roundRobin;             break;         }
This version of the algorithm performs much meliorate than the pure deep-path version, but it never quite achieves the performance of the master copy greedy algorithm or fifty-fifty the max-perimeter heuristic:

AlgorithmMinMeanMaxStdev
RR alongside Skipping 37 46.9 59 4.1
Random alongside Skipping 43 53.1 64 4.5
Greedy 31 39.8 48 3.5
Greedy Look-Ahead-2 28 37.0 45 3.1
Greedy Look-Ahead-3 25 34.2 40 2.7
Max Perimeter 29 37.4 44 3.2
Max Perimeter Look-Ahead-2 27 35.0 44 2.8
Perimeter-Area Hybrid 31 39.0 49 3.8
Deep-Path 51 74.8 104 9.4
Path-Area Hybrid 35 44.2 54 3.5
Path-Area Hybrid Look-Ahead-2 34 40.8 49 3.0
Path-Area Hybrid Look-Ahead-3 31 39.0 47 3.2
Path-Area Hybrid Look-Ahead-4 32 38.7 45 2.7

Even though it seemed similar a skillful see to start alongside a deep path toward the pump of the board earlier spreading out as well as clearing to a greater extent than blocks, it just doesn't brand progress fast plenty to overcome the initial slowness of creating that deep path at the start. Oh well, it was worth a try, at least.

We've covered a lot of soil inward this exploration of other heuristics for the greedy algorithm, as well as we've learned a lot nigh what makes a skillful heuristic. While nosotros constitute some promising options past times attempting to maximize the perimeter or take hold a hybrid approach of combining dissimilar heuristics, it turns out that it's fairly hard to practice meliorate than the basic greedy algorithm of clearing out the most blocks on the electrical flow motion or, fifty-fifty better, looking ahead to run across how to clear out the most blocks a few moves ahead.

Even though nosotros didn't respect a meliorate heuristic, it's withal worth exploring because nosotros improved the flexibility of the code as well as made it easier to add together inward to a greater extent than heuristics inward instance nosotros come upward up alongside other ideas for potentially meliorate ones inward the future. We similar a shot take hold a ton of knobs as well as dials to fiddle with, as well as it's possible to pass an awful lot of fourth dimension tweaking as well as tuning things. It also cannot live overstated how of import as well as useful it is to gain equally much noesis nigh a work infinite equally possible. You never know when you'll come upward across a primal insight that leads to a solution alongside much meliorate performance. Next fourth dimension we'll seat additional heuristics for the greedy algorithm aside as well as instead plough to exploring other types of criterion graph search algorithms. There are plenty of other algorithms to try, as well as nosotros tin give notice run across if it's possible to utilization them to some extent alongside the harsh exponential growth of motion choices alongside this game.


Article Index
Part 1: Introduction & Setup
Part 2: Tooling & Round-Robin
Part 3: Random & Skipping
Part 4: The Greedy Algorithm
Part 5: Greedy Look Ahead
Part 6: Heuristics & Hybrids
Part 7: Breadth-First Search
Part 8: Depth-First Search
Part 9: Dijkstra's Algorithm
Part 10: Dijkstra's Hybrids
Part 11: Priority Queues
Part 12: Summary

INFO LOWONGAN KERJA TERBARU KLIK DISINI

Iklan Atas Artikel


Iklan Tengah Artikel 1

Iklan Tengah Artikel 2


Iklan Bawah Artikel