Solution code
const MATCH = 'match', MATCH_COLOR = 'green';
const MISMATCH = 'mismatch', MISMATCH_COLOR = 'red';
function visMainFunction(inputBoard, inputWord) {
if (!inputBoard.length || !inputBoard[0].length) {
notification(`โ <b>Invalid Input:</b> The board is empty.`);
return false;
}
initClassProperties();
explanationAndColorPaletteNotifications();
const numRows = inputBoard.length;
const numCols = inputBoard[0].length;
startPartialBatch();
const board = new VisArray2D(...(inputBoard.map(row => new VisArray(...row)))).options({ label: 'Board' });
const word = new VisArray(...inputWord);
notification(`๐ <b>Scanning the board</b> โ trying each cell in the board as a potential starting point for forming the word <b>"${inputWord}"</b> through backtracking.`);
endBatch();
for (let rowIndex = 0; rowIndex < numRows; rowIndex++) {
for (let colIndex = 0; colIndex < numCols; colIndex++) {
notification(`โถ๏ธ <b>Attempt:</b> starting backtracking at cell (<b>${rowIndex}</b>, <b>${colIndex}</b>) to match <b>"${inputWord}"</b> from index <b>0</b>.`);
if (backtrack(board, word, rowIndex, colIndex, 0, numRows, numCols)) {
notification(`โ
<b>Success!</b> The word <b>"${inputWord}"</b> was found in the board.`);
return true;
}
}
}
notification(`โ <b>Failed!</b> The word <b>"${inputWord}"</b> does not exist in the board.`);
return false;
}
function backtrack(board, word, rowIndex, colIndex, wordIndex, numRows, numCols) {
if (wordIndex === word.length) {
notification(`โ
<b>Word Complete!</b> Successfully matched all ${word.length} characters.`);
return true;
}
if (
rowIndex < 0 || colIndex < 0 ||
rowIndex >= numRows || colIndex >= numCols
) {
return false;
}
if (word[wordIndex] !== board[rowIndex][colIndex]) {
handleMismatch(board, word, rowIndex, colIndex, wordIndex);
return false;
}
startPartialBatch();
handleMatch(board, word, rowIndex, colIndex, wordIndex);
board[rowIndex][colIndex] = "#";
endBatch();
const found = backtrack(board, word, rowIndex + 1, colIndex, wordIndex + 1, numRows, numCols) ||
backtrack(board, word, rowIndex - 1, colIndex, wordIndex + 1, numRows, numCols) ||
backtrack(board, word, rowIndex, colIndex + 1, wordIndex + 1, numRows, numCols) ||
backtrack(board, word, rowIndex, colIndex - 1, wordIndex + 1, numRows, numCols);
if (!found) {
startPartialBatch();
notification(`๐ <b>Backtracking:</b> Restoring '${word[wordIndex]}' at (${rowIndex}, ${colIndex}).`);
board[rowIndex][colIndex] = word[wordIndex];
word.makeVisManagerForIndex(wordIndex).removeClass(MATCH);
board.makeVisManagerForIndexPair(rowIndex, colIndex).removeClass(MATCH);
endBatch();
}
return found;
}
function initClassProperties() {
setClassProperties(MATCH, { backgroundColor: MATCH_COLOR });
setClassProperties(MISMATCH, { backgroundColor: MISMATCH_COLOR });
}
function handleMismatch(board, word, rowIndex, colIndex, wordIndex) {
startPartialBatch();
const wordCharVisManager = word.makeVisManagerForIndex(wordIndex);
const boardCellVisManager = board.makeVisManagerForIndexPair(rowIndex, colIndex);
wordCharVisManager.addClass(MISMATCH);
boardCellVisManager.addClass(MISMATCH);
notification(`โ <b>${makeColoredSpan('Mismatch', MISMATCH_COLOR)}: </b>
<br> - <b>Position:</b> (${rowIndex}, ${colIndex})
<br> - <b>Expected:</b> '${word[wordIndex]}'
<br> - <b>Found:</b> ${makeColoredSpan("'" + board[rowIndex][colIndex] + "'", MISMATCH_COLOR)}`);
boardCellVisManager.removeClass(MISMATCH);
wordCharVisManager.removeClass(MISMATCH);
endBatch();
}
function handleMatch(board, word, rowIndex, colIndex, wordIndex) {
word.makeVisManagerForIndex(wordIndex).addClass(MATCH);
board.makeVisManagerForIndexPair(rowIndex, colIndex).addClass(MATCH);
notification(`โ
<b>${makeColoredSpan(`Matched '${word[wordIndex]}'`, MATCH_COLOR)}</b> at (<b>${rowIndex}</b>, <b>${colIndex}</b>). <br>
๐งฉ We now <b>mark this cell as visited</b> by temporarily setting
<code>board[${rowIndex}][${colIndex}] = "#"</code> โ this prevents revisiting the same cell in the current path,
avoiding cycles during backtracking. <br>
๐ After marking, the algorithm will <b>explore all four possible directions</b> โ down, up, right, and left โ
to continue matching the remaining characters of the word from this position.`);
}
function explanationAndColorPaletteNotifications() {
explanationNotification();
notification(`
๐จ <b>Color Legend</b>
<ul>
<li>
<b>${makeColoredSpan('Green background', MATCH_COLOR)}</b> (<code>${MATCH}</code>):
Indicates a <b>successful character match</b> between the current board cell and the corresponding letter in the word.
Once matched, the cell is <b>temporarily marked as visited</b> (set to <code>"#"</code>) to prevent revisiting it in the current path.
</li>
<li>
<b>${makeColoredSpan('Red background', MISMATCH_COLOR)}</b> (<code>${MISMATCH}</code>):
Highlights a <b>mismatch</b> โ when the current board cell does not match the expected letter of the word.
</li>
<li>
Together, these colors visualize the <b>decision process of backtracking</b>:
green cells show the active search path, while red flashes mark dead ends that trigger backtracking.
</li>
</ul>
`);
}