How to use sudoku._search method in Cypress

Best JavaScript code snippet using cypress

Run Cypress automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

sudoku_generator_01.js

Source: sudoku_generator_01.js Github

copy
1/*
2// Integration:
3
4				case 'create':
5						var generated = sudoku.generate('hard');
6						console.log(generated);
7						var puzzle = {
8							givens: generated
9								.split('')
10								.map((n, i) => (n !== '.') ? `r${i % 9 + 1}c${Math.floor(i / 9) + 1}: ${n}` : undefined)
11								.filter(given => given !== undefined),
12							cages: [
13								{cells: 'r1c1-r3c3', style: 'box', sum: 45}, {cells: 'r1c4-r3c6', style: 'box', sum: 45}, {cells: 'r1c7-r3c9', style: 'box', sum: 45},
14								{cells: 'r4c1-r6c3', style: 'box', sum: 45}, {cells: 'r4c4-r6c6', style: 'box', sum: 45}, {cells: 'r4c7-r6c9', style: 'box', sum: 45},
15								{cells: 'r7c1-r9c3', style: 'box', sum: 45}, {cells: 'r7c4-r9c6', style: 'box', sum: 45}, {cells: 'r7c7-r9c9', style: 'box', sum: 45},
16							]
17						};
18						console.log(puzzle);
19						this.createPuzzle({rows: 9, cols: 9});
20						this.loadPuzzle(puzzle);
21					break;
22*/
23
24sudoku = (() => {
25	var sudoku = {};
26	sudoku.init = function() {
27		if(sudoku.initialised === true) return;
28		sudoku.initialised = true;
29		sudoku.DIGITS = '123456789';
30		sudoku.ROWS = 'ABCDEFGHI';
31		sudoku.COLS = sudoku.DIGITS;
32		sudoku.SQUARES = null;
33		sudoku.UNITS = null;
34		sudoku.SQUARE_UNITS_MAP = null;
35		sudoku.SQUARE_PEERS_MAP = null;
36		sudoku.MIN_GIVENS = 17;
37		sudoku.NR_SQUARES = 81;
38		sudoku.DIFFICULTY = {
39			"easy": 62,
40			"medium":53,
41			"hard":44,
42			"very-hard": 35,
43			"insane": 26,
44			"inhuman": 17,
45		};
46		sudoku.BLANK_CHAR = '.';
47		sudoku.SQUARES             = sudoku._cross(sudoku.ROWS, sudoku.COLS);
48		sudoku.UNITS               = sudoku._get_all_units(sudoku.ROWS, sudoku.COLS);
49		sudoku.SQUARE_UNITS_MAP    = sudoku._get_square_units_map(sudoku.SQUARES, sudoku.UNITS);
50		sudoku.SQUARE_PEERS_MAP    = sudoku._get_square_peers_map(sudoku.SQUARES, sudoku.SQUARE_UNITS_MAP);
51	};
52	sudoku.generate = function(difficulty, unique){
53		/* Generate a new Sudoku puzzle of a particular `difficulty`, e.g.,
54		`difficulty` must be a number between 17 and 81 inclusive. If it's
55		outside of that range, `difficulty` will be set to the closest bound,
56		e.g., 0 -> 17, and 100 -> 81.	
57		By default, the puzzles are unique, uless you set `unique` to false. 
58		(Note: Puzzle uniqueness is not yet implemented, so puzzles are *not* 
59		guaranteed to have unique solutions)
60		TODO: Implement puzzle uniqueness
61		*/
62		// If `difficulty` is a string or undefined, convert it to a number or
63		// default it to "easy" if undefined.
64		sudoku.init();
65		if(typeof difficulty === "string" || typeof difficulty === 'undefined') {
66			difficulty = sudoku.DIFFICULTY[difficulty] || sudoku.DIFFICULTY.easy;
67		}
68		// Force difficulty between 17 and 81 inclusive
69		difficulty = sudoku._force_range(difficulty, sudoku.NR_SQUARES + 1, sudoku.MIN_GIVENS);
70		// Default unique to true
71		unique = unique || true;
72		// Get a set of squares and all possible candidates for each square
73		var blank_board = "";
74		for(var i = 0; i < sudoku.NR_SQUARES; ++i) blank_board += '.';
75		var candidates = sudoku._get_candidates_map(blank_board);
76		// For each item in a shuffled list of squares
77		var shuffled_squares = sudoku._shuffle(sudoku.SQUARES);
78		for(var si in shuffled_squares) {
79			var square = shuffled_squares[si];
80			// If an assignment of a random chioce causes a contradictoin, give
81			// up and try again
82			var rand_candidate_idx = sudoku._rand_range(candidates[square].length);
83			var rand_candidate = candidates[square][rand_candidate_idx];
84			if(!sudoku._assign(candidates, square, rand_candidate)) break;
85			var single_candidates = [];
86			for(var si in sudoku.SQUARES) {
87				var square = sudoku.SQUARES[si];
88				if(candidates[square].length == 1) single_candidates.push(candidates[square]);
89			}
90			// If we have at least difficulty, and the unique candidate count is
91			// at least 8, return the puzzle!
92			if(single_candidates.length >= difficulty && sudoku._strip_dups(single_candidates).length >= 8){
93				var board = '';
94				var givens_idxs = [];
95				for(var i in sudoku.SQUARES){
96					var square = sudoku.SQUARES[i];
97					if(candidates[square].length == 1){
98						board += candidates[square];
99						givens_idxs.push(i);
100					}
101					else {
102						board += sudoku.BLANK_CHAR;
103					}
104				}
105				// If we have more than `difficulty` givens, remove some random
106				// givens until we're down to exactly `difficulty`
107				var nr_givens = givens_idxs.length;
108				if(nr_givens > difficulty){
109					givens_idxs = sudoku._shuffle(givens_idxs);
110					for(var i = 0; i < nr_givens - difficulty; ++i){
111						var target = parseInt(givens_idxs[i]);
112						board = board.substr(0, target) + sudoku.BLANK_CHAR + board.substr(target + 1);
113					}
114				}
115				// Double check board is solvable
116				// TODO: Make a standalone board checker. Solve is expensive.
117				if(sudoku.solve(board)) return board;
118			}
119		}
120		return sudoku.generate(difficulty);
121	};
122	sudoku.solve = function(board, reverse) {
123		sudoku.init();
124		var report = sudoku.validate_board(board);
125		if(report !== true) throw report;
126		var nr_givens = 0;
127		for(var i in board) {
128			if(board[i] !== sudoku.BLANK_CHAR && sudoku.DIGITS.includes(board[i])) ++nr_givens;
129		}
130		if(nr_givens < sudoku.MIN_GIVENS) throw 'Too few givens. Minimum givens is' + sudoku.MIN_GIVENS;
131		reverse = reverse || false;
132		var candidates = sudoku._get_candidates_map(board);
133		var result = sudoku._search(candidates, reverse);
134		if(result) {
135			var solution = '';
136			for(var square in result) solution += result[square];
137			return solution;
138		}
139		return false;
140	};
141	sudoku._get_candidates_map = function(board){
142		var report = sudoku.validate_board(board);
143		if(report !== true) throw report;
144		var candidate_map = {};
145		var squares_values_map = sudoku._get_square_vals_map(board);
146		for(var si in sudoku.SQUARES) candidate_map[sudoku.SQUARES[si]] = sudoku.DIGITS;
147		for(var square in squares_values_map){
148			var val = squares_values_map[square];
149			if(sudoku.DIGITS.includes(val)) {
150				var new_candidates = sudoku._assign(candidate_map, square, val);
151				if(!new_candidates) return false;
152			}
153		}
154		return candidate_map;
155	};
156	sudoku._search = function(candidates, reverse){
157		if(!candidates) return false;
158		reverse = reverse || false;
159		var max_nr_candidates = 0;
160		var max_candidates_square = null;
161		for(var si in sudoku.SQUARES) {
162			var square = sudoku.SQUARES[si];
163			var nr_candidates = candidates[square].length;
164			if(nr_candidates > max_nr_candidates) {
165				max_nr_candidates = nr_candidates;
166				max_candidates_square = square;
167			}
168		}
169		if(max_nr_candidates === 1) return candidates;
170		var min_nr_candidates = 10;
171		var min_candidates_square = null;
172		for(si in sudoku.SQUARES) {
173			var square = sudoku.SQUARES[si];
174			var nr_candidates = candidates[square].length;
175			if(nr_candidates < min_nr_candidates && nr_candidates > 1){
176				min_nr_candidates = nr_candidates;
177				min_candidates_square = square;
178			}
179		}
180		var min_candidates = candidates[min_candidates_square];
181		if(!reverse) {
182			//for(var vi in min_candidates) {
183			for(var vi = 0; vi < min_candidates.length; vi++) {
184				var val = min_candidates[vi];
185				var candidates_copy = Object.assign({}, candidates);
186				var candidates_next = sudoku._search(sudoku._assign(candidates_copy, min_candidates_square, val));
187				if(candidates_next) return candidates_next;
188			}
189		}
190		else {
191			for(var vi = min_candidates.length - 1; vi >= 0; --vi) {
192				var val = min_candidates[vi];
193				var candidates_copy = Object.assign({}, candidates);
194				var candidates_next = sudoku._search(sudoku._assign(candidates_copy, min_candidates_square, val), reverse);
195				if(candidates_next) return candidates_next;
196			}
197		}
198		return false;
199	};
200	sudoku._assign = function(candidates, square, val){
201		var other_vals = candidates[square].replace(val, '');
202		for(var ovi in other_vals) {
203			var other_val = other_vals[ovi];
204			var candidates_next = sudoku._eliminate(candidates, square, other_val);
205			if(!candidates_next) return false;
206		}
207		return candidates;
208	};
209	sudoku._eliminate = function(candidates, square, val){
210		if(!candidates[square].includes(val)) return candidates;
211		candidates[square] = candidates[square].replace(val, '');
212		var nr_candidates = candidates[square].length;
213		if(nr_candidates === 1){
214			var target_val = candidates[square];
215			for(var pi in sudoku.SQUARE_PEERS_MAP[square]){
216				var peer = sudoku.SQUARE_PEERS_MAP[square][pi];
217				var candidates_new = sudoku._eliminate(candidates, peer, target_val);
218				if(!candidates_new) return false;
219			}
220		}
221		if(nr_candidates === 0) return false;
222		for(var ui in sudoku.SQUARE_UNITS_MAP[square]) {
223			var unit = sudoku.SQUARE_UNITS_MAP[square][ui];
224			var val_places = [];
225			for(var si in unit){
226				var unit_square = unit[si];
227				if(candidates[unit_square].includes(val)) val_places.push(unit_square);
228			}
229			if(val_places.length === 0) return false;
230			if(val_places.length === 1) {
231				var candidates_new = sudoku._assign(candidates, val_places[0], val);
232				if(!candidates_new) return false;
233			}
234		}
235		return candidates;
236	};
237	sudoku._get_square_vals_map = function(board) {
238		var squares_vals_map = {};
239		if(board.length != sudoku.SQUARES.length) throw new Error("Board/squares length mismatch.");
240		for(var i in sudoku.SQUARES) squares_vals_map[sudoku.SQUARES[i]] = board[i];
241		return squares_vals_map;
242	};
243	sudoku._get_square_units_map = function(squares, units){
244		var square_unit_map = {};
245		for(var si in squares){
246			var cur_square = squares[si];
247			var cur_square_units = [];
248			for(var ui in units){
249				var cur_unit = units[ui];
250				if(cur_unit.indexOf(cur_square) !== -1) cur_square_units.push(cur_unit);
251			}
252			square_unit_map[cur_square] = cur_square_units;
253		}
254		return square_unit_map;
255	};
256	sudoku._get_square_peers_map = function(squares, units_map){
257		var square_peers_map = {};
258		for(var si in squares) {
259			var cur_square = squares[si];
260			var cur_square_units = units_map[cur_square];
261			var cur_square_peers = [];
262			for(var sui in cur_square_units){
263				var cur_unit = cur_square_units[sui];
264				for(var ui in cur_unit){
265					var cur_unit_square = cur_unit[ui];
266					if(cur_square_peers.indexOf(cur_unit_square) === -1 && cur_unit_square !== cur_square) {
267						cur_square_peers.push(cur_unit_square);
268					}
269				}
270			}
271			square_peers_map[cur_square] = cur_square_peers;
272		}
273		return square_peers_map;
274	};
275	sudoku._get_all_units = function(rows, cols){
276		/* Return a list of all units (rows, cols, boxes)
277		*/
278		var units = [];
279		// sudoku.ROWS
280		for(var ri in rows){
281			units.push(sudoku._cross(rows[ri], cols));
282		}
283		// Columns
284		for(var ci in cols){
285			 units.push(sudoku._cross(rows, cols[ci]));
286		}
287		// Boxes
288		var row_squares = ["ABC", "DEF", "GHI"];
289		var col_squares = ["123", "456", "789"];
290		for(var rsi in row_squares){
291			for(var csi in col_squares){
292				units.push(sudoku._cross(row_squares[rsi], col_squares[csi]));
293			}
294		}
295		return units;
296	};
297	sudoku.validate_board = function(board){
298		if(!board) return "Empty board";	
299		if(board.length !== sudoku.NR_SQUARES) return "Invalid board size. Board must be exactly " + sudoku.NR_SQUARES + " squares.";
300		for(var i in board) {
301			if(!sudoku.DIGITS.includes(board[i]) && board[i] !== sudoku.BLANK_CHAR){
302				return "Invalid board character encountered at index " + i + ": " + board[i];
303			}
304		}
305		return true;
306	};
307	sudoku._cross = function(a, b) {
308		var result = [];
309		for(var ai in a)
310			for(var bi in b)
311				result.push(a[ai] + b[bi]);
312		return result;
313	};
314	sudoku._shuffle = function(seq){
315		var shuffled = [];
316		for(var i = 0; i < seq.length; ++i) shuffled.push(false);
317		for(var i in seq) {
318			var ti = sudoku._rand_range(seq.length);
319			while(shuffled[ti]) ti = (ti + 1) > (seq.length - 1) ? 0 : (ti + 1);
320			shuffled[ti] = seq[i];
321		}	
322		return shuffled;
323	};
324	sudoku._rand_range = (max = 0, min = 0) => Math.floor(Math.random() * (max - min)) + min;
325	sudoku._strip_dups = (seq = []) => [...new Set(seq)];
326	sudoku._force_range = (nr, max = 0, min = 0) => Math.min(max || 0, Math.max(min || 0, nr));
327	return sudoku;
328})();
329
Full Screen

main.js

Source: main.js Github

copy
1// var sudoku = {}; 
2
3// sudoku.DIGITS = "123456789"; // Allowed sudoku.DIGITS
4// var ROWS = "ABCDEFGHI"; // Row lables
5// var COLS = sudoku.DIGITS; // Column lables
6// var SQUARES = null; // Square IDs
7
8// var UNITS = null; // All units (row, column, or box)
9// var SQUARE_UNITS_MAP = null; // Squares -> units map
10// var SQUARE_PEERS_MAP = null; // Squares -> peers map
11
12// var MIN_GIVENS = 17; // Minimum number of givens
13// var NR_SQUARES = 81; // Number of squares
14
15// sudoku.BLANK_CHAR = ".";
16// sudoku.BLANK_BOARD =
17
18// function initialize() {
19
20//   SQUARES = sudoku._cross(ROWS, COLS);
21//   UNITS = sudoku._get_all_units(ROWS, COLS);
22//   SQUARE_UNITS_MAP = sudoku._get_square_units_map(SQUARES, UNITS);
23//   SQUARE_PEERS_MAP = sudoku._get_square_peers_map(SQUARES, SQUARE_UNITS_MAP);
24// }
25// sudoku.generate = function (no_of_squares, unique) {
26//   no_of_squares = sudoku._force_range(
27//     no_of_squares,
28//     NR_SQUARES + 1,
29//     MIN_GIVENS
30//   );
31
32//   var blank_board = "";
33//   for (var i = 0; i < NR_SQUARES; ++i) {
34//     blank_board += ".";
35//   }
36//   var candidates = sudoku._get_candidates_map(blank_board);
37
38//   var shuffled_squares = sudoku._shuffle(SQUARES);
39//   for (var si in shuffled_squares) {
40//     var square = shuffled_squares[si];
41
42//     var rand_candidate_idx = sudoku._rand_range(candidates[square].length);
43//     var rand_candidate = candidates[square][rand_candidate_idx];
44//     if (!sudoku._assign(candidates, square, rand_candidate)) {
45//       break;
46//     }
47
48//     var single_candidates = [];
49//     for (var si in SQUARES) {
50//       var square = SQUARES[si];
51
52//       if (candidates[square].length == 1) {
53//         single_candidates.push(candidates[square]);
54//       }
55//     }
56
57//     if (
58//       single_candidates.length >= no_of_squares &&
59//       sudoku._strip_dups(single_candidates).length >= 8
60//     ) {
61//       var board = "";
62//       var givens_idxs = [];
63//       for (var i in SQUARES) {
64//         var square = SQUARES[i];
65//         if (candidates[square].length == 1) {
66//           board += candidates[square];
67//           givens_idxs.push(i);
68//         } else {
69//           board += sudoku.BLANK_CHAR;
70//         }
71//       }
72
73//       var nr_givens = givens_idxs.length;
74//       if (nr_givens > no_of_squares) {
75//         givens_idxs = sudoku._shuffle(givens_idxs);
76//         for (var i = 0; i < nr_givens - no_of_squares; ++i) {
77//           var target = parseInt(givens_idxs[i]);
78//           board =
79//             board.substr(0, target) +
80//             sudoku.BLANK_CHAR +
81//             board.substr(target + 1);
82//         }
83//       }
84//       if (sudoku.solve(board)) {
85//         return board;
86//       }
87//     }
88//   }
89
90//   return sudoku.generate(no_of_squares);
91// };
92
93// sudoku.solve = function (board, reverse) {
94//   // Assure a valid board
95//   var report = sudoku.validate_board(board);
96//   if (report !== true) {
97//     throw report;
98//   }
99//   var nr_givens = 0;
100//   for (var i in board) {
101//     if (board[i] !== sudoku.BLANK_CHAR && sudoku._in(board[i], sudoku.DIGITS)) {
102//       ++nr_givens;
103//     }
104//   }
105//   if (nr_givens < MIN_GIVENS) {
106//     throw "Too few givens. Minimum givens is " + MIN_GIVENS;
107//   }
108
109//   reverse = reverse || false;
110
111//   var candidates = sudoku._get_candidates_map(board);
112//   var result = sudoku._search(candidates, reverse);
113
114//   if (result) {
115//     var solution = "";
116//     for (var square in result) {
117//       solution += result[square];
118//     }
119//     return solution;
120//   }
121//   return false;
122// };
123
124// sudoku.get_candidates = function (board) {
125//   var report = sudoku.validate_board(board);
126//   if (report !== true) {
127//     throw report;
128//   }
129
130//   var candidates_map = sudoku._get_candidates_map(board);
131
132//   if (!candidates_map) {
133//     return false;
134//   }
135
136//   var rows = [];
137//   var cur_row = [];
138//   var i = 0;
139//   for (var square in candidates_map) {
140//     var candidates = candidates_map[square];
141//     cur_row.push(candidates);
142//     if (i % 9 == 8) {
143//       rows.push(cur_row);
144//       cur_row = [];
145//     }
146//     ++i;
147//   }
148//   return rows;
149// };
150
151// sudoku._get_candidates_map = function (board) {
152
153//   var report = sudoku.validate_board(board);
154//   if (report !== true) {
155//     throw report;
156//   }
157
158//   var candidate_map = {};
159//   var squares_values_map = sudoku._get_square_vals_map(board);
160//   for (var si in SQUARES) {
161//     candidate_map[SQUARES[si]] = sudoku.DIGITS;
162//   }
163
164//   for (var square in squares_values_map) {
165//     var val = squares_values_map[square];
166
167//     if (sudoku._in(val, sudoku.DIGITS)) {
168//       var new_candidates = sudoku._assign(candidate_map, square, val);
169
170//       if (!new_candidates) {
171//         return false;
172//       }
173//     }
174//   }
175
176//   return candidate_map;
177// };
178
179// sudoku._search = function (candidates, reverse) {
180//   if (!candidates) {
181//     return false;
182//   }
183
184//   reverse = reverse || false;
185//   var max_nr_candidates = 0;
186//   var max_candidates_square = null;
187//   for (var si in SQUARES) {
188//     var square = SQUARES[si];
189
190//     var nr_candidates = candidates[square].length;
191
192//     if (nr_candidates > max_nr_candidates) {
193//       max_nr_candidates = nr_candidates;
194//       max_candidates_square = square;
195//     }
196//   }
197//   if (max_nr_candidates === 1) {
198//     return candidates;
199//   }
200//   var min_nr_candidates = 10;
201//   var min_candidates_square = null;
202//   for (si in SQUARES) {
203//     var square = SQUARES[si];
204
205//     var nr_candidates = candidates[square].length;
206
207//     if (nr_candidates < min_nr_candidates && nr_candidates > 1) {
208//       min_nr_candidates = nr_candidates;
209//       min_candidates_square = square;
210//     }
211//   }
212//   var min_candidates = candidates[min_candidates_square];
213//   if (!reverse) {
214//     for (var vi in min_candidates) {
215//       var val = min_candidates[vi];
216//       var candidates_copy = JSON.parse(JSON.stringify(candidates));
217//       var candidates_next = sudoku._search(
218//         sudoku._assign(candidates_copy, min_candidates_square, val)
219//       );
220
221//       if (candidates_next) {
222//         return candidates_next;
223//       }
224//     }
225//   } else {
226//     for (var vi = min_candidates.length - 1; vi >= 0; --vi) {
227//       var val = min_candidates[vi];
228//       var candidates_copy = JSON.parse(JSON.stringify(candidates));
229//       var candidates_next = sudoku._search(
230//         sudoku._assign(candidates_copy, min_candidates_square, val),
231//         reverse
232//       );
233
234//       if (candidates_next) {
235//         return candidates_next;
236//       }
237//     }
238//   }
239//   return false;
240// };
241
242// sudoku._assign = function (candidates, square, val) {
243//   var other_vals = candidates[square].replace(val, "");
244
245//   for (var ovi in other_vals) {
246//     var other_val = other_vals[ovi];
247
248//     var candidates_next = sudoku._eliminate(candidates, square, other_val);
249
250//     if (!candidates_next) {
251//       return false;
252//     }
253//   }
254
255//   return candidates;
256// };
257
258// sudoku._eliminate = function (candidates, square, val) {
259//   if (!sudoku._in(val, candidates[square])) {
260//     return candidates;
261//   }
262//   candidates[square] = candidates[square].replace(val, "");
263//   var nr_candidates = candidates[square].length;
264//   if (nr_candidates === 1) {
265//     var target_val = candidates[square];
266
267//     for (var pi in SQUARE_PEERS_MAP[square]) {
268//       var peer = SQUARE_PEERS_MAP[square][pi];
269
270//       var candidates_new = sudoku._eliminate(candidates, peer, target_val);
271
272//       if (!candidates_new) {
273//         return false;
274//       }
275//     }
276
277//   }
278//   if (nr_candidates === 0) {
279//     return false;
280//   }
281
282//   for (var ui in SQUARE_UNITS_MAP[square]) {
283//     var unit = SQUARE_UNITS_MAP[square][ui];
284
285//     var val_places = [];
286//     for (var si in unit) {
287//       var unit_square = unit[si];
288//       if (sudoku._in(val, candidates[unit_square])) {
289//         val_places.push(unit_square);
290//       }
291//     }
292
293//     if (val_places.length === 0) {
294//       return false;
295
296//     } else if (val_places.length === 1) {
297//       var candidates_new = sudoku._assign(candidates, val_places[0], val);
298
299//       if (!candidates_new) {
300//         return false;
301//       }
302//     }
303//   }
304
305//   return candidates;
306// };
307// sudoku._get_square_vals_map = function (board) {
308//   /* Return a map of squares -> values
309//    */
310//   var squares_vals_map = {};
311
312//   // Make sure `board` is a string of length 81
313//   if (board.length != SQUARES.length) {
314//     throw "Board/squares length mismatch.";
315//   } else {
316//     for (var i in SQUARES) {
317//       squares_vals_map[SQUARES[i]] = board[i];
318//     }
319//   }
320
321//   return squares_vals_map;
322// };
323
324// sudoku._get_square_units_map = function (squares, units) {
325//   var square_unit_map = {};
326
327//   for (var si in squares) {
328//     var cur_square = squares[si];
329
330//     var cur_square_units = [];
331//     for (var ui in units) {
332//       var cur_unit = units[ui];
333
334//       if (cur_unit.indexOf(cur_square) !== -1) {
335//         cur_square_units.push(cur_unit);
336//       }
337//     }
338
339//     // Save the current square and its units to the map
340//     square_unit_map[cur_square] = cur_square_units;
341//   }
342
343//   return square_unit_map;
344// };
345
346// sudoku._get_square_peers_map = function (squares, units_map) {
347//   var square_peers_map = {};
348
349//   for (var si in squares) {
350//     var cur_square = squares[si];
351//     var cur_square_units = units_map[cur_square];
352
353//     var cur_square_peers = [];
354
355//     for (var sui in cur_square_units) {
356//       var cur_unit = cur_square_units[sui];
357
358//       for (var ui in cur_unit) {
359//         var cur_unit_square = cur_unit[ui];
360
361//         if (
362//           cur_square_peers.indexOf(cur_unit_square) === -1 &&
363//           cur_unit_square !== cur_square
364//         ) {
365//           cur_square_peers.push(cur_unit_square);
366//         }
367//       }
368//     }
369//     square_peers_map[cur_square] = cur_square_peers;
370//   }
371
372//   return square_peers_map;
373// };
374
375// sudoku._get_all_units = function (rows, cols) {
376//   var units = [];
377
378//   // Rows
379//   for (var ri in rows) {
380//     units.push(sudoku._cross(rows[ri], cols));
381//   }
382
383//   // Columns
384//   for (var ci in cols) {
385//     units.push(sudoku._cross(rows, cols[ci]));
386//   }
387
388//   // Boxes
389//   var row_squares = ["ABC", "DEF", "GHI"];
390//   var col_squares = ["123", "456", "789"];
391//   for (var rsi in row_squares) {
392//     for (var csi in col_squares) {
393//       units.push(sudoku._cross(row_squares[rsi], col_squares[csi]));
394//     }
395//   }
396
397//   return units;
398// };
399// sudoku.validate_board = function (board) {
400//   if (!board) {
401//     return "Empty board";
402//   }
403
404//   // Invalid board length
405//   if (board.length !== NR_SQUARES) {
406//     return (
407//       "Invalid board size. Board must be exactly " + NR_SQUARES + " squares."
408//     );
409//   }
410
411//   // Check for invalid characters
412//   for (var i in board) {
413//     if (
414//       !sudoku._in(board[i], sudoku.DIGITS) &&
415//       board[i] !== sudoku.BLANK_CHAR
416//     ) {
417//       return (
418//         "Invalid board character encountered at index " + i + ": " + board[i]
419//       );
420//     }
421//   }
422//   return true;
423// };
424
425// sudoku._cross = function (a, b) {
426//   var result = [];
427//   for (var ai in a) {
428//     for (var bi in b) {
429//       result.push(a[ai] + b[bi]);
430//     }
431//   }
432//   return result;
433// };
434
435// sudoku._in = function (v, seq) {
436//   return seq.indexOf(v) !== -1;
437// };
438
439// sudoku._first_true = function (seq) {
440//   for (var i in seq) {
441//     if (seq[i]) {
442//       return seq[i];
443//     }
444//   }
445//   return false;
446// };
447
448// sudoku._shuffle = function (seq) {
449//   var shuffled = [];
450//   for (var i = 0; i < seq.length; ++i) {
451//     shuffled.push(false);
452//   }
453
454//   for (var i in seq) {
455//     var ti = sudoku._rand_range(seq.length);
456
457//     while (shuffled[ti]) {
458//       ti = ti + 1 > seq.length - 1 ? 0 : ti + 1;
459//     }
460
461//     shuffled[ti] = seq[i];
462//   }
463
464//   return shuffled;
465// };
466
467// sudoku._rand_range = function (max, min) {
468//   min = min || 0;
469//   if (max) {
470//     return Math.floor(Math.random() * (max - min)) + min;
471//   } else {
472//     throw "Range undefined";
473//   }
474// };
475
476// sudoku._strip_dups = function (seq) {
477//   var seq_set = [];
478//   var dup_map = {};
479//   for (var i in seq) {
480//     var e = seq[i];
481//     if (!dup_map[e]) {
482//       seq_set.push(e);
483//       dup_map[e] = true;
484//     }
485//   }
486//   return seq_set;
487// };
488
489// sudoku._force_range = function (nr, max, min) {
490//   min = min || 0;
491//   nr = nr || 0;
492//   if (nr < min) {
493//     return min;
494//   }
495//   if (nr > max) {
496//     return max;
497//   }
498//   return nr;
499// };
500
501// // Initialize library after load
502// initialize();
503const medium = [
504    "--9-------4----6-758-31----15--4-36-------4-8----9-------75----3-------1--2--3--",
505    "619472583243985617587316924158247369926531478734698152891754236365829741472163895"
506];
507var timer;
508var timeremaining;
509var lives;
510var selectednum;
511var selectedtile;
512var disableselect;
513// var unsolvedboard = sudoku.generate(40);
514// var solvedboard = sudoku.solve(unsolvedboard);
515
516
517window.onload = function () {
518    id("start-btn").addEventListener("click", startGame);
519    for (let i = 0; i < id("number-container").children.length; i++) {
520        id("number-container").children[i].addEventListener("click", function () {
521            if (!disableselect) {
522                if (this.classList.contains("selected")) {
523                    this.classList.remove("selected");
524                    selectednum = null;
525                } else {
526                    for (let i = 0; i < 9; i++) {
527                        id("number-container").children[i].classList.remove("selected");
528                    }
529                    this.classList.add("selected");
530                    selectednum = this;
531                    updateMove();
532
533
534                }
535            }
536        });
537
538    }
539}
540
541function startGame() {
542    let board;
543    board = medium[0];
544    lives = 3;
545    disableselect = false;
546    id("lives").textContent = "Lives Remaining: 3";
547    generateBoard(board);
548    startTimer();
549    id("number-container").classList.remove("hidden");
550}
551
552function startTimer() {
553    if (id("time-3").checked) timeremaining = 180;
554    else if (id("time-5").checked) timeremaining = 300;
555    else timeremaining = 600;
556    id("timer").textContent = timeConversion(timeremaining);
557    timer = setInterval(function () {
558        timeremaining--;
559        if (timeremaining === 0) endGame();
560        id("timer").textContent = timeConversion(timeremaining);
561    }, 1000)
562}
563
564function timeConversion(time) {
565    let minutes = Math.floor(time / 60);
566    if (minutes < 10) minutes = "0" + minutes;
567    let seconds = time % 60;
568    if (seconds < 10) seconds = "0" + seconds;
569    return minutes + ":" + seconds;
570}
571
572function generateBoard(board) {
573    clearPrevious();
574    let idCount = 0;
575    for (let i = 0; i < 81; i++) {
576        let tile = document.createElement("p");
577        if (board.charAt(i) != "-") {
578            tile.textContent = board.charAt(i);
579        } else {
580            tile.addEventListener("click", function () {
581                if (!disableselect) {
582                    if (tile.classList.contains("selected")) {
583                        tile.classList.remove("selected");
584                        selectedtile = null;
585                    } else {
586                        for (let i = 0; i < 81; i++) {
587                            qsa(".tile")[i].classList.remove("selected");
588                        }
589                        tile.classList.add("selected");
590                        selectedtile = tile;
591                        updateMove();
592                    }
593                }
594            });
595        }
596        tile.id = idCount;
597        idCount++;
598        tile.classList.add("tile");
599        if ((tile.id > 17 && tile.id < 27) || (tile.id > 44 & tile.id < 54)) {
600            tile.classList.add("bottomborder")
601        }
602        if ((tile.id + 1) % 9 == 3 || (tile.id + 1) % 9 == 6) {
603            tile.classList.add("rightborder");
604        }
605        id("board").appendChild(tile);
606    }
607}
608
609function updateMove(){
610    if(selectedtile && selectednum){
611        selectedtile.textContent= selectednum.textContent; 
612        if (checkCorrect(selectedtile)){
613            selectedtile.classList.remove("selected");
614            selectednum.classList.remove("selected");
615            selectednum= null;
616            selectedtile= null; 
617            if (checkDone()){
618                endGame; 
619            }
620
621        } else {
622            disableselect = true; 
623            selectedtile.classList.add("incorrect");
624            setTimeout(function(){
625                lives --; 
626                if (lives===0){
627                    endGame(); 
628                } else {
629                    id("lives").textContent = "Lives Remaining: " + lives; 
630                    disableselect = false; 
631                }
632                selectedtile.classList.remove("incorrect");
633                selectedtile.classList.remove("selected");
634                selectednum.classList.remove("selected");
635                selectedtile.textContent= "";
636                selectedtile = null; 
637                selectednum = null; 
638            }, 1000); 
639        }
640    }
641}
642function checkDone(){
643    let tiles = qsa(".tile");
644    for (let i = 0; i < tiles.length; i++) {
645        if (tiles.textContent === "") return false;
646    }
647    return true; 
648}
649function endGame(){
650    disableselect = true; 
651    clearTimeout(timer) ; 
652    if (lives === 0 || timeremaining === 0 ){
653        id("lives"). textContent = "You Lost!";
654    } else {
655        id("lives"). textContent = "You Won!";
656    }
657}
658function checkCorrect(tile){
659    let solution; 
660    solution = medium[1]; 
661    if ( solution.charAt(tile.id)===tile.textContent) return true; 
662    else return false; 
663}
664
665function clearPrevious() {
666    let tiles = qsa(".tile");
667    for (let i = 0; i < tiles.length; i++) {
668        tiles[i].remove();
669    }
670    if (timer) clearTimeout(timer);
671    for (let i = 0; i < id("number-container").children.length; i++) {
672        id("number-container").children[i].classList.remove("selected");
673    }
674    selectedtile = null;
675    selectednum = null;
676}
677
678function id(id) {
679    return document.getElementById(id);
680}
681
682function qs(selector) {
683    return document.querySelector(selector);
684}
685
686function qsa(selector) {
687    return document.querySelectorAll(selector);
688}
Full Screen

sudoku.js

Source: sudoku.js Github

copy
1/*
2    Sudoku.js
3    ---------
4
5    A Sudoku puzzle generator and solver JavaScript library.
6
7    Please see the README for more details.
8*/
9
10//(function(root){
11    //var sudoku = root.sudoku = {};  // Global reference to the sudoku library
12    var sudoku = {};
13    sudoku.DIGITS = "123456789";    // Allowed sudoku.DIGITS
14    var ROWS = "ABCDEFGHI";         // Row lables
15    var COLS = sudoku.DIGITS;       // Column lables
16    var SQUARES = null;             // Square IDs
17
18    var UNITS = null;               // All units (row, column, or box)
19    var SQUARE_UNITS_MAP = null;    // Squares -> units map
20    var SQUARE_PEERS_MAP = null;    // Squares -> peers map
21
22    var MIN_GIVENS = 17;            // Minimum number of givens
23    var NR_SQUARES = 81;            // Number of squares
24
25    // Define difficulties by how many squares are given to the player in a new
26    // puzzle.
27    var DIFFICULTY = {
28        "easy":         62,
29        "medium":       53,
30        "hard":         44,
31        "very-hard":    35,
32        "insane":       26,
33        "inhuman":      17,
34    };
35
36    // Blank character and board representation
37    sudoku.BLANK_CHAR = '.';
38    sudoku.BLANK_BOARD = "...................................................."+
39            ".............................";
40
41    // Init
42    // -------------------------------------------------------------------------
43    function initialize(){
44        /* Initialize the Sudoku library (invoked after library load)
45        */
46        SQUARES             = sudoku._cross(ROWS, COLS);
47        UNITS               = sudoku._get_all_units(ROWS, COLS);
48        SQUARE_UNITS_MAP    = sudoku._get_square_units_map(SQUARES, UNITS);
49        SQUARE_PEERS_MAP    = sudoku._get_square_peers_map(SQUARES,
50                                    SQUARE_UNITS_MAP);
51    }
52
53    // Generate
54    // -------------------------------------------------------------------------
55    sudoku.generate = function(difficulty, unique){
56        /* Generate a new Sudoku puzzle of a particular `difficulty`, e.g.,
57
58            // Generate an "easy" sudoku puzzle
59            sudoku.generate("easy");
60
61
62        Difficulties are as follows, and represent the number of given squares:
63
64                "easy":         61
65                "medium":       52
66                "hard":         43
67                "very-hard":    34
68                "insane":       25
69                "inhuman":      17
70
71
72        You may also enter a custom number of squares to be given, e.g.,
73
74            // Generate a new Sudoku puzzle with 60 given squares
75            sudoku.generate(60)
76
77
78        `difficulty` must be a number between 17 and 81 inclusive. If it's
79        outside of that range, `difficulty` will be set to the closest bound,
80        e.g., 0 -> 17, and 100 -> 81.
81
82
83        By default, the puzzles are unique, uless you set `unique` to false.
84        (Note: Puzzle uniqueness is not yet implemented, so puzzles are *not*
85        guaranteed to have unique solutions)
86
87        TODO: Implement puzzle uniqueness
88        */
89
90        // If `difficulty` is a string or undefined, convert it to a number or
91        // default it to "easy" if undefined.
92        if(typeof difficulty === "string" || typeof difficulty === "undefined"){
93            difficulty = DIFFICULTY[difficulty] || DIFFICULTY.easy;
94        }
95
96        // Force difficulty between 17 and 81 inclusive
97        difficulty = sudoku._force_range(difficulty, NR_SQUARES + 1,
98                MIN_GIVENS);
99
100        // Default unique to true
101        unique = unique || true;
102
103        // Get a set of squares and all possible candidates for each square
104        var blank_board = "";
105        for(var i = 0; i < NR_SQUARES; ++i){
106            blank_board += '.';
107        }
108        var candidates = sudoku._get_candidates_map(blank_board);
109
110        // For each item in a shuffled list of squares
111        var shuffled_squares = sudoku._shuffle(SQUARES);
112        for(var si in shuffled_squares){
113            var square = shuffled_squares[si];
114
115            // If an assignment of a random chioce causes a contradictoin, give
116            // up and try again
117            var rand_candidate_idx =
118                    sudoku._rand_range(candidates[square].length);
119            var rand_candidate = candidates[square][rand_candidate_idx];
120            if(!sudoku._assign(candidates, square, rand_candidate)){
121                break;
122            }
123
124            // Make a list of all single candidates
125            var single_candidates = [];
126            for(si in SQUARES){
127                square = SQUARES[si];
128
129                if(candidates[square].length === 1){
130                    single_candidates.push(candidates[square]);
131                }
132            }
133
134            // If we have at least difficulty, and the unique candidate count is
135            // at least 8, return the puzzle!
136            if(single_candidates.length >= difficulty &&
137                    sudoku._strip_dups(single_candidates).length >= 8){
138                var board = "";
139                var givens_idxs = [];
140                for(i in SQUARES){
141                    square = SQUARES[i];
142                    if(candidates[square].length === 1){
143                        board += candidates[square];
144                        givens_idxs.push(i);
145                    } else {
146                        board += sudoku.BLANK_CHAR;
147                    }
148                }
149
150                // If we have more than `difficulty` givens, remove some random
151                // givens until we're down to exactly `difficulty`
152                var nr_givens = givens_idxs.length;
153                if(nr_givens > difficulty){
154                    givens_idxs = sudoku._shuffle(givens_idxs);
155                    for(i = 0; i < nr_givens - difficulty; ++i){
156                        var target = parseInt(givens_idxs[i]);
157                        board = board.substr(0, target) + sudoku.BLANK_CHAR +
158                            board.substr(target + 1);
159                    }
160                }
161
162                // Double check board is solvable
163                // TODO: Make a standalone board checker. Solve is expensive.
164                if(sudoku.solve(board)){
165                    return board;
166                }
167            }
168        }
169
170        // Give up and try a new puzzle
171        return sudoku.generate(difficulty);
172    };
173
174    // Solve
175    // -------------------------------------------------------------------------
176    sudoku.solve = function(board, reverse){
177        /* Solve a sudoku puzzle given a sudoku `board`, i.e., an 81-character
178        string of sudoku.DIGITS, 1-9, and spaces identified by '.', representing the
179        squares. There must be a minimum of 17 givens. If the given board has no
180        solutions, return false.
181
182        Optionally set `reverse` to solve "backwards", i.e., rotate through the
183        possibilities in reverse. Useful for checking if there is more than one
184        solution.
185        */
186
187        // Assure a valid board
188        var report = sudoku.validate_board(board);
189        if(report !== true){
190            throw report;
191        }
192
193        // Check number of givens is at least MIN_GIVENS
194        var nr_givens = 0;
195        for(var i in board){
196            if(board[i] !== sudoku.BLANK_CHAR && sudoku._in(board[i], sudoku.DIGITS)){
197                ++nr_givens;
198            }
199        }
200        if(nr_givens < MIN_GIVENS){
201            // eslint-disable-next-line
202            throw "Too few givens. Minimum givens is " + MIN_GIVENS;
203        }
204
205        // Default reverse to false
206        reverse = reverse || false;
207
208        var candidates = sudoku._get_candidates_map(board);
209        var result = sudoku._search(candidates, reverse);
210
211        if(result){
212            var solution = "";
213            for(var square in result){
214                solution += result[square];
215            }
216            return solution;
217        }
218        return false;
219    };
220
221    sudoku.get_candidates = function(board){
222        /* Return all possible candidatees for each square as a grid of
223        candidates, returnning `false` if a contradiction is encountered.
224
225        Really just a wrapper for sudoku._get_candidates_map for programmer
226        consumption.
227        */
228
229        // Assure a valid board
230        var report = sudoku.validate_board(board);
231        if(report !== true){
232            throw report;
233        }
234
235        // Get a candidates map
236        var candidates_map = sudoku._get_candidates_map(board);
237
238        // If there's an error, return false
239        if(!candidates_map){
240            return false;
241        }
242
243        // Transform candidates map into grid
244        var rows = [];
245        var cur_row = [];
246        var i = 0;
247        for(var square in candidates_map){
248            var candidates = candidates_map[square];
249            cur_row.push(candidates);
250            if(i % 9 === 8){
251                rows.push(cur_row);
252                cur_row = [];
253            }
254            ++i;
255        }
256        return rows;
257    }
258
259    sudoku._get_candidates_map = function(board){
260        /* Get all possible candidates for each square as a map in the form
261        {square: sudoku.DIGITS} using recursive constraint propagation. Return `false`
262        if a contradiction is encountered
263        */
264
265        // Assure a valid board
266        var report = sudoku.validate_board(board);
267        if(report !== true){
268            throw report;
269        }
270
271        var candidate_map = {};
272        var squares_values_map = sudoku._get_square_vals_map(board);
273
274        // Start by assigning every digit as a candidate to every square
275        for(var si in SQUARES){
276            candidate_map[SQUARES[si]] = sudoku.DIGITS;
277        }
278
279        // For each non-blank square, assign its value in the candidate map and
280        // propigate.
281        for(var square in squares_values_map){
282            var val = squares_values_map[square];
283
284            if(sudoku._in(val, sudoku.DIGITS)){
285                var new_candidates = sudoku._assign(candidate_map, square, val);
286
287                // Fail if we can't assign val to square
288                if(!new_candidates){
289                    return false;
290                }
291            }
292        }
293
294        return candidate_map;
295    };
296
297    sudoku._search = function(candidates, reverse){
298        /* Given a map of squares -> candiates, using depth-first search,
299        recursively try all possible values until a solution is found, or false
300        if no solution exists.
301        */
302
303        // Return if error in previous iteration
304        if(!candidates){
305            return false;
306        }
307
308        // Default reverse to false
309        reverse = reverse || false;
310
311        // If only one candidate for every square, we've a solved puzzle!
312        // Return the candidates map.
313        var max_nr_candidates = 0;
314        // eslint-disable-next-line
315        var max_candidates_square = null;
316        for(var si in SQUARES){
317            var square = SQUARES[si];
318
319            var nr_candidates = candidates[square].length;
320
321            if(nr_candidates > max_nr_candidates){
322                max_nr_candidates = nr_candidates;
323                // eslint-disable-next-line no-unused-vars
324                max_candidates_square = square;
325            }
326        }
327        if(max_nr_candidates === 1){
328            return candidates;
329        }
330
331        // Choose the blank square with the fewest possibilities > 1
332        var min_nr_candidates = 10;
333        var min_candidates_square = null;
334        for(si in SQUARES){
335            square = SQUARES[si];
336
337            nr_candidates = candidates[square].length;
338
339            if(nr_candidates < min_nr_candidates && nr_candidates > 1){
340                min_nr_candidates = nr_candidates;
341                min_candidates_square = square;
342            }
343        }
344
345        // Recursively search through each of the candidates of the square
346        // starting with the one with fewest candidates.
347
348        // Rotate through the candidates forwards
349        var min_candidates = candidates[min_candidates_square];
350        if(!reverse){
351            for(var vi in min_candidates){
352                var val = min_candidates[vi];
353
354                // TODO: Implement a non-rediculous deep copy function
355                var candidates_copy = JSON.parse(JSON.stringify(candidates));
356                var candidates_next = sudoku._search(
357                    sudoku._assign(candidates_copy, min_candidates_square, val)
358                );
359
360                if(candidates_next){
361                    return candidates_next;
362                }
363            }
364
365        // Rotate through the candidates backwards
366        } else {
367            for(vi = min_candidates.length - 1; vi >= 0; --vi){
368                val = min_candidates[vi];
369
370                // TODO: Implement a non-rediculous deep copy function
371                candidates_copy = JSON.parse(JSON.stringify(candidates));
372                candidates_next = sudoku._search(
373                    sudoku._assign(candidates_copy, min_candidates_square, val),
374                    reverse
375                );
376
377                if(candidates_next){
378                    return candidates_next;
379                }
380            }
381        }
382
383        // If we get through all combinations of the square with the fewest
384        // candidates without finding an answer, there isn't one. Return false.
385        return false;
386    };
387
388    sudoku._assign = function(candidates, square, val){
389        /* Eliminate all values, *except* for `val`, from `candidates` at
390        `square` (candidates[square]), and propagate. Return the candidates map
391        when finished. If a contradiciton is found, return false.
392
393        WARNING: This will modify the contents of `candidates` directly.
394        */
395
396        // Grab a list of canidates without 'val'
397        var other_vals = candidates[square].replace(val, "");
398
399        // Loop through all other values and eliminate them from the candidates
400        // at the current square, and propigate. If at any point we get a
401        // contradiction, return false.
402        for(var ovi in other_vals){
403            var other_val = other_vals[ovi];
404
405            var candidates_next =
406                sudoku._eliminate(candidates, square, other_val);
407
408            if(!candidates_next){
409                //console.log("Contradiction found by _eliminate.");
410                return false;
411            }
412        }
413
414        return candidates;
415    };
416
417    sudoku._eliminate = function(candidates, square, val){
418        /* Eliminate `val` from `candidates` at `square`, (candidates[square]),
419        and propagate when values or places <= 2. Return updated candidates,
420        unless a contradiction is detected, in which case, return false.
421
422        WARNING: This will modify the contents of `candidates` directly.
423        */
424
425        // If `val` has already been eliminated from candidates[square], return
426        // with candidates.
427        if(!sudoku._in(val, candidates[square])){
428            return candidates;
429        }
430
431        // Remove `val` from candidates[square]
432        candidates[square] = candidates[square].replace(val, '');
433
434        // If the square has only candidate left, eliminate that value from its
435        // peers
436        var nr_candidates = candidates[square].length;
437        if(nr_candidates === 1){
438            var target_val = candidates[square];
439
440            for(var pi in SQUARE_PEERS_MAP[square]){
441                var peer = SQUARE_PEERS_MAP[square][pi];
442
443                var candidates_new =
444                        sudoku._eliminate(candidates, peer, target_val);
445
446                if(!candidates_new){
447                    return false;
448                }
449            }
450
451        // Otherwise, if the square has no candidates, we have a contradiction.
452        // Return false.
453        } if(nr_candidates === 0){
454            return false;
455        }
456
457        // If a unit is reduced to only one place for a value, then assign it
458        for(var ui in SQUARE_UNITS_MAP[square]){
459            var unit = SQUARE_UNITS_MAP[square][ui];
460
461            var val_places = [];
462            for(var si in unit){
463                var unit_square = unit[si];
464                if(sudoku._in(val, candidates[unit_square])){
465                    val_places.push(unit_square);
466                }
467            }
468
469            // If there's no place for this value, we have a contradition!
470            // return false
471            if(val_places.length === 0){
472                return false;
473
474            // Otherwise the value can only be in one place. Assign it there.
475            } else if(val_places.length === 1){
476                candidates_new =
477                    sudoku._assign(candidates, val_places[0], val);
478
479                if(!candidates_new){
480                    return false;
481                }
482            }
483        }
484
485        return candidates;
486    };
487
488
489    // Square relationships
490    // -------------------------------------------------------------------------
491    // Squares, and their relationships with values, units, and peers.
492
493    sudoku._get_square_vals_map = function(board){
494        /* Return a map of squares -> values
495        */
496        var squares_vals_map = {};
497
498        // Make sure `board` is a string of length 81
499        if(board.length !== SQUARES.length){
500            // eslint-disable-next-line
501            throw "Board/squares length mismatch.";
502
503        } else {
504            for(var i in SQUARES){
505                squares_vals_map[SQUARES[i]] = board[i];
506            }
507        }
508
509        return squares_vals_map;
510    };
511
512    sudoku._get_square_units_map = function(squares, units){
513        /* Return a map of `squares` and their associated units (row, col, box)
514        */
515        var square_unit_map = {};
516
517        // For every square...
518        for(var si in squares){
519            var cur_square = squares[si];
520
521            // Maintain a list of the current square's units
522            var cur_square_units = [];
523
524            // Look through the units, and see if the current square is in it,
525            // and if so, add it to the list of of the square's units.
526            for(var ui in units){
527                var cur_unit = units[ui];
528
529                if(cur_unit.indexOf(cur_square) !== -1){
530                    cur_square_units.push(cur_unit);
531                }
532            }
533
534            // Save the current square and its units to the map
535            square_unit_map[cur_square] = cur_square_units;
536        }
537
538        return square_unit_map;
539    };
540
541    sudoku._get_square_peers_map = function(squares, units_map){
542        /* Return a map of `squares` and their associated peers, i.e., a set of
543        other squares in the square's unit.
544        */
545        var square_peers_map = {};
546
547        // For every square...
548        for(var si in squares){
549            var cur_square = squares[si];
550            var cur_square_units = units_map[cur_square];
551
552            // Maintain list of the current square's peers
553            var cur_square_peers = [];
554
555            // Look through the current square's units map...
556            for(var sui in cur_square_units){
557                var cur_unit = cur_square_units[sui];
558
559                for(var ui in cur_unit){
560                    var cur_unit_square = cur_unit[ui];
561
562                    if(cur_square_peers.indexOf(cur_unit_square) === -1 &&
563                            cur_unit_square !== cur_square){
564                        cur_square_peers.push(cur_unit_square);
565                    }
566                }
567            }
568
569            // Save the current square an its associated peers to the map
570            square_peers_map[cur_square] = cur_square_peers;
571        }
572
573        return square_peers_map;
574    };
575
576    sudoku._get_all_units = function(rows, cols){
577        /* Return a list of all units (rows, cols, boxes)
578        */
579        var units = [];
580
581        // Rows
582        for(var ri in rows){
583            units.push(sudoku._cross(rows[ri], cols));
584        }
585
586        // Columns
587        for(var ci in cols){
588           units.push(sudoku._cross(rows, cols[ci]));
589        }
590
591        // Boxes
592        var row_squares = ["ABC", "DEF", "GHI"];
593        var col_squares = ["123", "456", "789"];
594        for(var rsi in row_squares){
595            for(var csi in col_squares){
596                units.push(sudoku._cross(row_squares[rsi], col_squares[csi]));
597            }
598        }
599
600        return units;
601    };
602
603
604    // Conversions
605    // -------------------------------------------------------------------------
606    sudoku.board_string_to_grid = function(board_string){
607        /* Convert a board string to a two-dimensional array
608        */
609        var rows = [];
610        var cur_row = [];
611        for(var i in board_string){
612            cur_row.push(board_string[i]);
613            if(i % 9 === 8){
614                rows.push(cur_row);
615                cur_row = [];
616            }
617        }
618        return rows;
619    };
620
621    sudoku.board_grid_to_string = function(board_grid){
622        /* Convert a board grid to a string
623        */
624        var board_string = "";
625        for(var r = 0; r < 9; ++r){
626            for(var c = 0; c < 9; ++c){
627                board_string += board_grid[r][c];
628            }
629        }
630        return board_string;
631    };
632
633
634    // Utility
635    // -------------------------------------------------------------------------
636
637    sudoku.print_board = function(board){
638        /* Print a sudoku `board` to the console.
639        */
640
641        // Assure a valid board
642        var report = sudoku.validate_board(board);
643        if(report !== true){
644            throw report;
645        }
646
647        var V_PADDING = " ";  // Insert after each square
648        var H_PADDING = '\n'; // Insert after each row
649
650        var V_BOX_PADDING = "  "; // Box vertical padding
651        var H_BOX_PADDING = '\n'; // Box horizontal padding
652
653        var display_string = "";
654
655        for(var i in board){
656            var square = board[i];
657
658            // Add the square and some padding
659            display_string += square + V_PADDING;
660
661            // Vertical edge of a box, insert v. box padding
662            if(i % 3 === 2){
663                display_string += V_BOX_PADDING;
664            }
665
666            // End of a line, insert horiz. padding
667            if(i % 9 === 8){
668                display_string += H_PADDING;
669            }
670
671            // Horizontal edge of a box, insert h. box padding
672            if(i % 27 === 26){
673                display_string += H_BOX_PADDING;
674            }
675        }
676
677        console.log(display_string);
678    };
679
680    sudoku.validate_board = function(board){
681        /* Return if the given `board` is valid or not. If it's valid, return
682        true. If it's not, return a string of the reason why it's not.
683        */
684
685        // Check for empty board
686        if(!board){
687            return "Empty board";
688        }
689
690        // Invalid board length
691        if(board.length !== NR_SQUARES){
692            return "Invalid board size. Board must be exactly " + NR_SQUARES +
693                    " squares.";
694        }
695
696        // Check for invalid characters
697        for(var i in board){
698            if(!sudoku._in(board[i], sudoku.DIGITS) && board[i] !== sudoku.BLANK_CHAR){
699                return "Invalid board character encountered at index " + i +
700                        ": " + board[i];
701            }
702        }
703
704        // Otherwise, we're good. Return true.
705        return true;
706    };
707
708    sudoku._cross = function(a, b){
709        /* Cross product of all elements in `a` and `b`, e.g.,
710        sudoku._cross("abc", "123") ->
711        ["a1", "a2", "a3", "b1", "b2", "b3", "c1", "c2", "c3"]
712        */
713        var result = [];
714        for(var ai in a){
715            for(var bi in b){
716                result.push(a[ai] + b[bi]);
717            }
718        }
719        return result;
720    };
721
722    sudoku._in = function(v, seq){
723        /* Return if a value `v` is in sequence `seq`.
724        */
725        return seq.indexOf(v) !== -1;
726    };
727
728    sudoku._first_true = function(seq){
729        /* Return the first element in `seq` that is true. If no element is
730        true, return false.
731        */
732        for(var i in seq){
733            if(seq[i]){
734                return seq[i];
735            }
736        }
737        return false;
738    };
739
740    sudoku._shuffle = function(seq){
741        /* Return a shuffled version of `seq`
742        */
743
744        // Create an array of the same size as `seq` filled with false
745        var shuffled = [];
746        for(var i = 0; i < seq.length; ++i){
747            shuffled.push(false);
748        }
749
750        for(i in seq){
751            var ti = sudoku._rand_range(seq.length);
752
753            while(shuffled[ti]){
754                ti = (ti + 1) > (seq.length - 1) ? 0 : (ti + 1);
755            }
756
757            shuffled[ti] = seq[i];
758        }
759
760        return shuffled;
761    };
762
763    sudoku._rand_range = function(max, min){
764        /* Get a random integer in the range of `min` to `max` (non inclusive).
765        If `min` not defined, default to 0. If `max` not defined, throw an
766        error.
767        */
768        min = min || 0;
769        if(max){
770            return Math.floor(Math.random() * (max - min)) + min;
771        } else {
772            // eslint-disable-next-line
773            throw "Range undefined";
774        }
775    };
776
777    sudoku._strip_dups = function(seq){
778        /* Strip duplicate values from `seq`
779        */
780        var seq_set = [];
781        var dup_map = {};
782        for(var i in seq){
783            var e = seq[i];
784            if(!dup_map[e]){
785                seq_set.push(e);
786                dup_map[e] = true;
787            }
788        }
789        return seq_set;
790    };
791
792    sudoku._force_range = function(nr, max, min){
793        /* Force `nr` to be within the range from `min` to, but not including,
794        `max`. `min` is optional, and will default to 0. If `nr` is undefined,
795        treat it as zero.
796        */
797        min = min || 0
798        nr = nr || 0
799        if(nr < min){
800            return min;
801        }
802        if(nr > max){
803            return max;
804        }
805        return nr
806    }
807
808    // Initialize library after load
809    initialize();
810
811    export const getSudoku = () => {
812      return sudoku;
813    }
814
815// Pass whatever the root object is, like 'window' in browsers
816//})(this);
817
Full Screen

Accelerate Your Automation Test Cycles With LambdaTest

Leverage LambdaTest’s cloud-based platform to execute your automation tests in parallel and trim down your test execution time significantly. Your first 100 automation testing minutes are on us.

Try LambdaTest

Run JavaScript Tests on LambdaTest Cloud Grid

Execute automation tests with Cypress on a cloud-based Grid of 3000+ real browsers and operating systems for both web and mobile applications.

Test now for Free
LambdaTestX

We use cookies to give you the best experience. Cookies help to provide a more personalized experience and relevant advertising for you, and web analytics for us. Learn More in our Cookies policy, Privacy & Terms of service

Allow Cookie
Sarah

I hope you find the best code examples for your project.

If you want to accelerate automated browser testing, try LambdaTest. Your first 100 automation testing minutes are FREE.

Sarah Elson (Product & Growth Lead)