Comparing the player and compositions of the quest
In this task, we create logic to make multilayered pattern compositions from player's selection and then compare it with the quest pattern's composition.
Prepare for lift off
We need a way to represent the pattern overlapping and non-overlapping relationships.
Representing a pattern overlapping relationship
We will use the data structure of this section to remember the overlapping relationship. Most patterns will overlap others, so we would need to think the other way round. So, we store those patterns that do not overlap together.
The pattern is a two-dimensional (2D) array. The first dimension contains every pattern. Each pattern is a list of the other patterns that do not overlay with it.
For example, the following A
pattern does not overlap with the C
and D
shapes. We represent it with the ensuing equation:
array['A'] = [ 'C', 'D'];
For a pattern that always overlaps with the others, an empty array will be assigned.
Engage thrusters
In the following steps, we code the logic that allows us to compare two given quest compositions:
- In the
composition.js
file, we have the following class variable to represent the relationship of the overlapping patterns. It indicates the pattern that overlaps with other patterns. The index of thenonOverlappedPattern
array is the pattern ID, and the corresponding array value is the list of patterns that do not overlap with that pattern:// static variable. available as only one copy among all composition instances. Composition.nonOverlappedPattern = [ [], // pattern 0 [2], // pattern 1, doesn't overlap with pattern 2. [1], // pattern 2, doesn't overlap with pattern 1. [], // pattern 3 [], // pattern 4 [6], // pattern 5, doesn't overlap with pattern 6. [5], // pattern 6, doesn't overlap with pattern 5. ];
- We create the following new method in the
Composition
method that can turn a composition back to a one-dimension array:Composition.prototype.toSequence = function() { var seq = []; for (var i=0; i < this.data.length; i++) { for (var j=0; j <this.data[i].length; j++ ) { seq.push(this.data[i][j]); } } return seq; }
- Then, we create the
createFromSequence
method with the following code that turns a sequence back to a composition:Composition.createFromSequence = function(sequence) { // function to determine if 2 given patterns overlap. var allowPatternsInSameLevel = function(patternA, patternB) { // iterate the array to see if current pattern overlaps the var nonOverlappedPattern = Composition.nonOverlappedPattern[patternA] var len = nonOverlappedPattern.length; for (var i=0; i<len; i++) { if (nonOverlappedPattern[i] === parseInt(patternB)) { return true; } } return false; }; // layer is an array that contains existing pattern var layerAllowsPattern = function(layer, pattern) { for (var i=0, len=layer.length; i<len; i++) { if (!allowPatternsInSameLevel(layer[i], pattern)) { return false; } } return true; }; // end helper functions var newComposition = new Composition(); var layer = []; for (var i=0, len=sequence.length; i<len; i++) { if (layerAllowsPattern(layer, sequence[i])) { // we are still in same layer. layer.push(sequence[i]); } else { // two patterns overlapped, // we push the current layer to composition // and use a new layer for the current pattern. newComposition.data.push(layer); layer = []; // new array instance to prevent browser using the same array and crashes the data. layer.push(sequence[i]); } } // for the last layer if (layer.length> 0) newComposition.data.push(layer); return newComposition; }
- We add a new method to the
Quest
method that can compare two multilayered pattern compositions and check whether they are equal to each other:Quest.prototype.isEqualToComposition = function(composition) { var a = this.data; var b = composition.data; // sort each level in both array for (var i=0, len=a.length; i<len; i++) { a[i].sort(); } for (var i=0, len=b.length; i<len; i++) { b[i].sort(); } // flatten both compositions into sequence. a = this.toSequence(); b = composition.toSequence(); if (a.length !== b.length) return false; for (var i=0, len=a.length; i<len; i++) { if (parseInt(a[i]) !== parseInt(b[i])) return false; } return true; }
- In the
composition-view.js
file, we check whether the player's latest selection matches the quest level. Therefore, in both theselectPattern
andundo
methods, we keep a composition from the sequence and check it with the quest level:selectPattern: function(pattern) { ... game.composition = game.Composition.createFromSequence(game.compositionSeq); if (game.quest.isEqualToComposition(game.composition)){ game.flow.gameWin(); } }, undo: function() { ... game.composition = game.Composition.createFromSequence(game.compositionSeq); if (game.quest.isEqualToComposition(game.composition)) { game.flow.gameWin(); } },
Objective complete – mini debriefing
The comparison requires us to create a multilayered pattern composition from the selection sequence. Then, we compare the player's one with that of the quest level.
Composition is a two-dimensional array that we need to compare between the quest and the player's selection. However, the player's selection is a sequence that does not reflect our layer's structure. This is the reason why we need a conversion between the composition and the sequence. Moreover, we need to keep a copy of the player's sequence because we need that information for the undo feature.
Comparing players and compositions of the quest
In our createFromSequence
method, we created two helper functions to manipulate multilayered patterns. The allowPatternsInSameLevel
function checks whether the given two patterns can be overlapped together by looking up the non-overlapped pattern table. The layerAllowsPattern
function makes use of the previous function and checks whether the given pattern fits all other patterns inside the given layer, which is an array of all non-overlapped patterns put together.
The createFromSequence
method makes use of the function to scan the selection array in a sequence and then put each pattern into either the same layer (without overlapping) or a new layer.
Finally, we have the isEqualToComposition
method in the quest instance to compare the player's selection composition with the quest level's one. This method sorts all the layers in both the compositions into the same order and then converts the composition back to a one-dimension array using the toSequence
method.
This ensures the player's selection can match our defined level of data on non-overlapping patterns, regardless of the order of selection.
Classified intel
We attach the relationship of non-overlapping patterns directly to the composition object because they are the same, regardless of each instance. Attaching it to the composition instead of the instance's prototype prevents this block of data from being copied to each instance.