| Conditions | 63 | 
| Paths | > 20000 | 
| Total Lines | 251 | 
| Lines | 0 | 
| Ratio | 0 % | 
| Tests | 54 | 
| CRAP Score | 63 | 
| Changes | 1 | ||
| Bugs | 0 | Features | 0 | 
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
Complex classes like ai.js ➔ nextBestMove often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
| 1 | /**  | 
            ||
| 12 | function nextBestMove(board, boardSize) { | 
            ||
| 13 | var m,  | 
            ||
| 14 | l,  | 
            ||
| 15 | position,  | 
            ||
| 16 | type,  | 
            ||
| 17 | line,  | 
            ||
| 18 | totalCells,  | 
            ||
| 19 | consecutiveCells,  | 
            ||
| 20 | emptySides,  | 
            ||
| 21 | bestScore,  | 
            ||
| 22 | cellScore,  | 
            ||
| 23 | directionScore,  | 
            ||
| 24 | score;  | 
            ||
| 25 | |||
| 26 | // iterate through all the board's cells  | 
            ||
| 27 | 16 |     for (var i = boardSize * boardSize; i--; ) { | 
            |
| 28 | // skip to next cell if this cell is owned by the computer  | 
            ||
| 29 | // if (board[i] == 1) continue;  | 
            ||
| 30 | |||
| 31 | // by default, the best move is the first free cell  | 
            ||
| 32 | // (position, attack, defense)  | 
            ||
| 33 | |||
| 34 | 1600 |         if (bestScore === undefined) { | 
            |
| 
                                                                                                    
                        
                         | 
                |||
| 35 | 16 | bestScore = [i, 0, 0];  | 
            |
| 36 | }  | 
            ||
| 37 | // we will give a "score" to the position,  | 
            ||
| 38 | // based on its surroundings horizontally, vertically and  | 
            ||
| 39 | // on both diagonals; for example: for a row like 000XXPXX000 (where "0" means empty,  | 
            ||
| 40 | // "X" represents the opponent's pieces and "P" is the position for which we are  | 
            ||
| 41 | // determining the "score", we would check  | 
            ||
| 42 | // 0|00XXP|XX000, 00|0XXPX|X000, 000|XXPXX|000, 000X|XPXX0|00, 000XX|PXX00|0,  | 
            ||
| 43 | // and then we would do the same on the vertical, and on both diagonals)  | 
            ||
| 44 | |||
| 45 | // cell's default score (attack and defense)  | 
            ||
| 46 | 1600 | cellScore = [0, 0];  | 
            |
| 47 | |||
| 48 | // the 4 directions to check: vertical, horizontal, diagonal /, diagonal \ (in this order)  | 
            ||
| 49 | 1600 |         for (var j = 4; j--; ) { | 
            |
| 50 | // the default score for the direction we're checking  | 
            ||
| 51 | 6400 | directionScore = [0, 0];  | 
            |
| 52 | |||
| 53 | // check the 5 possible outcomes, as described above  | 
            ||
| 54 | // (if we're checking whether the player won,  | 
            ||
| 55 | // we'll do this iteration only once, checking for 5 in a row)  | 
            ||
| 56 | 6400 |             for (var k = !board[i] ? 5 : 1; k--; ) { | 
            |
| 57 | // initialize the type of cells we're looking for,  | 
            ||
| 58 | // and the array with the cells on the current direction  | 
            ||
| 59 | 15680 | type = board[i] || undefined;  | 
            |
| 60 | 15680 | line = [];  | 
            |
| 61 | |||
| 62 | // check the 5 pieces for each possible outcome, plus the 2 sides  | 
            ||
| 63 | 15680 |                 for (l = 7; l--; ) { | 
            |
| 64 | // used to compute position  | 
            ||
| 65 | 73128 | m = -5 + k + l;  | 
            |
| 66 | 73128 | var n = i % boardSize;  | 
            |
| 67 | |||
| 68 | 73128 | if (  | 
            |
| 69 | // vertical  | 
            ||
| 70 | ((j === 0 &&  | 
            ||
| 71 | (position = i + boardSize * m) !== false &&  | 
            ||
| 72 | n == position % boardSize) ||  | 
            ||
| 73 | // horizontal  | 
            ||
| 74 | (j == 1 &&  | 
            ||
| 75 | (position = i + m) !== false &&  | 
            ||
| 76 | ~~(position / boardSize) == ~~(i / boardSize)) ||  | 
            ||
| 77 | // diagonal /  | 
            ||
| 78 | (j == 2 &&  | 
            ||
| 79 | (position = i - boardSize * m + m) !== false &&  | 
            ||
| 80 | ((position > i && position % boardSize < n) ||  | 
            ||
| 81 | (position < i && position % boardSize > n) ||  | 
            ||
| 82 | position == i)) ||  | 
            ||
| 83 | // diagonal \  | 
            ||
| 84 | ((j == 3 &&  | 
            ||
| 85 | (position = i + boardSize * m + m) !== false &&  | 
            ||
| 86 | ((position < i && position % boardSize < n) ||  | 
            ||
| 87 | (position > i && position % boardSize > n))) ||  | 
            ||
| 88 | position == i)) &&  | 
            ||
| 89 | // the position is not off-board  | 
            ||
| 90 | position >= 0 &&  | 
            ||
| 91 | position < boardSize * boardSize &&  | 
            ||
| 92 | // the cell is of the same type as the ones we are looking for  | 
            ||
| 93 | // or, we are checking the score for an empty cell,  | 
            ||
| 94 | // the current position is empty,  | 
            ||
| 95 | // or is "undefined" (meaning we didn't yet find any non-empty cells)  | 
            ||
| 96 | (board[position] == type ||  | 
            ||
| 97 | (!board[i] && (!board[position] || undefined === type)) ||  | 
            ||
| 98 | // or we're just checking the sides  | 
            ||
| 99 | !l ||  | 
            ||
| 100 | l == 6)  | 
            ||
| 101 |                     ) { | 
            ||
| 102 | // add position to the line  | 
            ||
| 103 | 56572 | line.push(position);  | 
            |
| 104 | |||
| 105 | // if we're not just checking the sides,  | 
            ||
| 106 | // this is not an empty cell, and is of the same type as  | 
            ||
| 107 | // the ones we're looking for,  | 
            ||
| 108 | // update the type of cells we're looking for  | 
            ||
| 109 | // (we use ^ instead of !=)  | 
            ||
| 110 | 56572 |                         if (l && l ^ 6 && undefined === type && board[position]) { | 
            |
| 111 | 1118 | type = board[position];  | 
            |
| 112 | }  | 
            ||
| 113 | |||
| 114 | // if the computed position is off-board,  | 
            ||
| 115 | // but this is a side - cell, save it as "undefined"  | 
            ||
| 116 | 16556 |                     } else if (!l || l == 6) { | 
            |
| 117 | 6532 | line.push(undefined);  | 
            |
| 118 |                     } else { | 
            ||
| 119 | // skip the rest of the test if we found this row to be "non-compliant"  | 
            ||
| 120 | // (a different type of cell than the ones we're looking for,  | 
            ||
| 121 | // one of the 5 cells is off - board)  | 
            ||
| 122 | 10024 | break;  | 
            |
| 123 | }  | 
            ||
| 124 | }  | 
            ||
| 125 | |||
| 126 | // if we added exactly 7 position to our line, and  | 
            ||
| 127 | // the line is not containing * only * empty cells  | 
            ||
| 128 | 15680 |                 if (line.length == 7 && undefined !== type) { | 
            |
| 129 | // if we are checking whether the player won,  | 
            ||
| 130 | // set this flag so that later on we do not  | 
            ||
| 131 | // overwrite the value of the cell  | 
            ||
| 132 | 1326 | m = board[i] ? true : false;  | 
            |
| 133 | |||
| 134 | // calculate the score when setting the current cell to  | 
            ||
| 135 | // the same type as the other ones we found  | 
            ||
| 136 | 1326 | board[i] = type;  | 
            |
| 137 | |||
| 138 | // calculate the number of consecutive cells we get like this  | 
            ||
| 139 | // (we'll do this by looking in our "line" array)  | 
            ||
| 140 | 1326 | consecutiveCells = 0;  | 
            |
| 141 | 1326 | totalCells = 0;  | 
            |
| 142 | 1326 | emptySides = 0;  | 
            |
| 143 | |||
| 144 | // the total number of cells of the same type  | 
            ||
| 145 | 1326 |                     for (l = 5; l--; ) { | 
            |
| 146 | 6630 |                         if (board[line[l + 1]] == type) { | 
            |
| 147 | 4570 | totalCells++;  | 
            |
| 148 | }  | 
            ||
| 149 | }  | 
            ||
| 150 | |||
| 151 | // look to the left of the current cell  | 
            ||
| 152 | 1326 | for (  | 
            |
| 153 | l = line.indexOf(i) - 1;  | 
            ||
| 154 | l >= 0;  | 
            ||
| 155 | // if the cell is of the same type,  | 
            ||
| 156 | //increment the number of consecutive cells  | 
            ||
| 157 | l--  | 
            ||
| 158 |                     ) { | 
            ||
| 159 | 1528 |                         if (board[line[l]] == type) { | 
            |
| 160 | 718 | consecutiveCells++;  | 
            |
| 161 |                         } else { | 
            ||
| 162 | // otherwise  | 
            ||
| 163 | // if the adjacent cell is empty, increment the number of empty sides  | 
            ||
| 164 | // we have to use === 0 (instead of !) because it  | 
            ||
| 165 | // can also be "undefined"  | 
            ||
| 166 | 810 |                             if (board[line[l]] === 0) { | 
            |
| 167 | 646 | emptySides++;  | 
            |
| 168 | }  | 
            ||
| 169 | |||
| 170 | // don't look further  | 
            ||
| 171 | 810 | break;  | 
            |
| 172 | }  | 
            ||
| 173 | }  | 
            ||
| 174 | |||
| 175 | // look to the right of the current cell  | 
            ||
| 176 | 1326 | for (  | 
            |
| 177 | l = line.indexOf(i);  | 
            ||
| 178 | l < line.length;  | 
            ||
| 179 | // if the cell is of the same type,  | 
            ||
| 180 | // increment the number of consecutive cells  | 
            ||
| 181 | l++  | 
            ||
| 182 |                     ) { | 
            ||
| 183 | 5194 |                         if (board[line[l]] == type) { | 
            |
| 184 | 4370 | consecutiveCells++;  | 
            |
| 185 |                         } else { | 
            ||
| 186 | // otherwise  | 
            ||
| 187 | // if the adjacent cell is empty, increment the number of empty sides  | 
            ||
| 188 | // we have to use === 0 (instead of !)  | 
            ||
| 189 | // because it can also be "undefined"  | 
            ||
| 190 | 824 |                             if (board[line[l]] === 0) { | 
            |
| 191 | 652 | emptySides++;  | 
            |
| 192 | }  | 
            ||
| 193 | |||
| 194 | // don't look further  | 
            ||
| 195 | 824 | break;  | 
            |
| 196 | }  | 
            ||
| 197 | }  | 
            ||
| 198 | |||
| 199 | // give a score to the row based on the  | 
            ||
| 200 | // array below(number of cells / empty sides)  | 
            ||
| 201 | 1326 | score = [[0, 1], [2, 3], [4, 12], [10, 64], [256, 256]][  | 
            |
| 202 | consecutiveCells >= totalCells  | 
            ||
| 203 | ? Math.min(consecutiveCells, 5) - 1  | 
            ||
| 204 | : totalCells - 1  | 
            ||
| 205 | ][consecutiveCells >= totalCells ? (emptySides ? emptySides - 1 : 0) : 0];  | 
            ||
| 206 | |||
| 207 | // reset the cell's value (unless we are looking to see if the player won)  | 
            ||
| 208 | 1326 |                     if (!m) { | 
            |
| 209 | 734 | board[i] = 0;  | 
            |
| 210 | }  | 
            ||
| 211 |                     // } else if (score >= 256) { | 
            ||
| 212 | // // if the player won, update the score  | 
            ||
| 213 | // score = 1024;  | 
            ||
| 214 | // }  | 
            ||
| 215 | |||
| 216 | // if, so far, this is the best  | 
            ||
| 217 | // attack / defense score(depending on the value of "type")  | 
            ||
| 218 | // for this direction, update it  | 
            ||
| 219 | 1326 |                     if (score > directionScore[type - 1]) { | 
            |
| 220 | 1016 | directionScore[type - 1] = score;  | 
            |
| 221 | }  | 
            ||
| 222 | }  | 
            ||
| 223 | }  | 
            ||
| 224 | |||
| 225 | // update the cell's attack and defense score  | 
            ||
| 226 | // (we simply sum the best scores of all 4 directions)  | 
            ||
| 227 | 6400 |             for (k = 2; k--; ) { | 
            |
| 228 | 12800 | cellScore[k] += directionScore[k];  | 
            |
| 229 | }  | 
            ||
| 230 | }  | 
            ||
| 231 | |||
| 232 | // used below  | 
            ||
| 233 | 1600 | j = cellScore[0] + cellScore[1];  | 
            |
| 234 | 1600 | k = bestScore[1] + bestScore[2];  | 
            |
| 235 | |||
| 236 | // if cell's attack + defense score is better than  | 
            ||
| 237 | // the current best attack and defense score  | 
            ||
| 238 | // or, cell's score is equal to the best score,  | 
            ||
| 239 | // but computer's move is better or equal to the player's,  | 
            ||
| 240 | // and the current best move is not *exactly* the same  | 
            ||
| 241 | 1600 | if (  | 
            |
| 242 | (j > k || (j == k && cellScore[0] >= bestScore[1])) &&  | 
            ||
| 243 | // we're checking the score of an empty cell,  | 
            ||
| 244 | // or we're checking to see if the player won and he won  | 
            ||
| 245 | // (we don't update the score when checking if the  | 
            ||
| 246 | // player won * unless * the player actually won)  | 
            ||
| 247 | (!board[i] || cellScore[1] >= 1024)  | 
            ||
| 248 | |||
| 249 | // update best score (position, attack, defense)  | 
            ||
| 250 |         ) { | 
            ||
| 251 | 234 | bestScore = [i, cellScore[0], cellScore[1]];  | 
            |
| 252 | }  | 
            ||
| 253 | }  | 
            ||
| 254 | |||
| 255 | // Calculation done return x, y  | 
            ||
| 256 | var x, y;  | 
            ||
| 257 | |||
| 258 | 16 | y = Math.floor(bestScore[0] / boardSize);  | 
            |
| 259 | 16 | x = bestScore[0] % boardSize;  | 
            |
| 260 | |||
| 261 | 16 |     return { x, y }; | 
            |
| 262 | }  | 
            ||
| 263 | |||
| 303 |