Line data Source code
1 : /** 2 : * "Inside-out" implementation of Fisher-Yates shuffle 3 : */ 4 : function shuffleArray(source) 5 : { 6 0 : if (!source.length) 7 0 : return []; 8 : 9 0 : let result = [source[0]]; 10 0 : for (let i = 1; i < source.length; ++i) 11 : { 12 0 : let j = randIntInclusive(0, i); 13 0 : result[i] = result[j]; 14 0 : result[j] = source[i]; 15 : } 16 0 : return result; 17 : } 18 : 19 : /** 20 : * Generates each permutation of the given array and runs the callback function on it. 21 : * Uses the given clone function on each item of the array. 22 : * Creating arrays with all permutations of the given array has a bad memory footprint. 23 : * Algorithm by B. R. Heap. Changes the input array. 24 : */ 25 : function heapsPermute(array, cloneFunc, callback) 26 : { 27 0 : let c = new Array(array.length).fill(0); 28 : 29 0 : callback(array.map(cloneFunc)); 30 : 31 0 : let i = 0; 32 0 : while (i < array.length) 33 : { 34 0 : if (c[i] < i) 35 : { 36 0 : let swapIndex = i % 2 ? c[i] : 0; 37 0 : let swapValue = cloneFunc(array[swapIndex]); 38 0 : array[swapIndex] = array[i]; 39 0 : array[i] = swapValue; 40 : 41 0 : callback(array.map(cloneFunc)); 42 : 43 0 : ++c[i]; 44 0 : i = 0; 45 : } 46 : else 47 : { 48 0 : c[i] = 0; 49 0 : ++i; 50 : } 51 : } 52 : } 53 : 54 : /** 55 : * Compare two variables recursively. This compares better than a quick 56 : * JSON.stringify check since we also check undefineds, Sets and the like. 57 : * 58 : * @param first - Any javascript instance. 59 : * @param second - Any javascript instance. 60 : * @return {boolean} Whether first and second are equal. 61 : */ 62 : function deepCompare(first, second) 63 : { 64 : // If the value of either variable is empty we can instantly compare 65 : // them and check for equality. 66 54 : if (first === null || first === undefined || 67 : second === null || second === undefined) 68 4 : return first === second; 69 : 70 : // Make sure both variables have the same type. 71 50 : if (first.constructor !== second.constructor) 72 1 : return false; 73 : 74 : // We know that the variables are of the same type so all we need to do is 75 : // check what type one of the objects is, and then compare them. 76 : 77 : // Check numbers seperately. Make sure this works with NaN, Infinity etc. 78 49 : if (typeof first == "number") 79 21 : return uneval(first) === uneval(second); 80 : 81 : // Functions and RegExps must have the same reference to be equal. 82 28 : if (first instanceof Function || first instanceof RegExp) 83 0 : return first === second; 84 : 85 : // If we are comparing simple objects, we can just compare them. 86 28 : if (first === second || first.valueOf() === second.valueOf()) 87 0 : return true; 88 : 89 : // Dates would have equal valueOf if they are equal. 90 28 : if (first instanceof Date) 91 0 : return false; 92 : 93 : // From now we will assume we have some kind of objects so that 94 : // we can do a recursive check of the keys and values. 95 28 : if (!(first instanceof Object) || !(second instanceof Object)) 96 0 : return false; 97 : 98 : // We cannot iterate over Sets, so collapse them to Arrays. 99 28 : if (first instanceof Set) 100 : { 101 7 : first = Array.from(first); 102 7 : second = Array.from(second); 103 : } 104 : 105 : // Objects need the same number of keys in order to be equal. 106 28 : const firstKeys = Object.keys(first); 107 28 : if (firstKeys.length != Object.keys(second).length) 108 3 : return false; 109 : 110 : // Make sure that all the object keys on this level of the object are the same. 111 : // Finally, we pass all the values of our of each object recursively to 112 : // make sure everything matches. 113 33 : return Object.keys(second).every(i => firstKeys.includes(i)) && 114 30 : firstKeys.every(i => deepCompare(first[i], second[i])); 115 : } 116 : 117 : /** 118 : * Removes prefixing path from a path or filename, leaving just the file's name (with extension) 119 : * 120 : * ie. a/b/c/file.ext -> file.ext 121 : */ 122 : function basename(path) 123 : { 124 0 : return path.split("/").pop(); 125 : } 126 : 127 : /** 128 : * Returns the directories of a given path. 129 : * 130 : * ie. a/b/c/file.ext -> a/b/c 131 : */ 132 : function dirname(path) 133 : { 134 0 : return path.split("/").slice(0, -1).join("/"); 135 : } 136 : 137 : /** 138 : * Returns names of files found in the given directory, stripping the directory path and file extension. 139 : */ 140 : function listFiles(path, extension, recurse) 141 : { 142 0 : return Engine.ListDirectoryFiles(path, "*" + extension, recurse).map(filename => filename.slice(path.length, -extension.length)); 143 : } 144 :