| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 'use strict'; |
|
|
| |
| const DIRECTIONS = [ |
| { r: 0, c: 1, name: 'LtoR' }, |
| { r: 0, c: -1, name: 'RtoL' }, |
| { r: 1, c: 0, name: 'UtoD' }, |
| { r: -1, c: 0, name: 'DtoU' }, |
| { r: 1, c: 1, name: 'diagDR'}, |
| { r: -1, c: -1, name: 'diagUL'}, |
| { r: 1, c: -1, name: 'diagDL'}, |
| { r: -1, c: 1, name: 'diagUR'}, |
| ]; |
|
|
| |
| |
| |
| |
| |
| const LOOKALIKES = { |
| 'A': new Set(['4']), |
| 'B': new Set(['8', '3']), |
| 'C': new Set(['G', 'O', 'Q']), |
| 'D': new Set(['O', 'Q', '0']), |
| 'E': new Set(['F']), |
| 'F': new Set(['E']), |
| 'G': new Set(['C', '6', 'Q']), |
| 'H': new Set([]), |
| 'I': new Set(['L', '1', '|', 'J']), |
| 'J': new Set(['I']), |
| 'K': new Set(['X']), |
| 'L': new Set(['I', '1', '|']), |
| 'M': new Set(['N']), |
| 'N': new Set(['M']), |
| 'O': new Set(['0', 'Q', 'D']), |
| 'P': new Set([]), |
| 'Q': new Set(['O', 'G', '0']), |
| 'R': new Set([]), |
| 'S': new Set(['5', '8']), |
| 'T': new Set(['7']), |
| 'U': new Set(['V']), |
| 'V': new Set(['U']), |
| 'W': new Set([]), |
| 'X': new Set(['K']), |
| 'Y': new Set([]), |
| 'Z': new Set(['2', '7']), |
| }; |
|
|
| |
| |
| |
| |
| |
| |
| function charMatch(target, gridChar) { |
| if (!gridChar || gridChar === ' ') return false; |
| if (gridChar === '?') return true; |
| const t = target.toUpperCase(); |
| const g = gridChar.toUpperCase(); |
| if (t === g) return true; |
| const alts = LOOKALIKES[t]; |
| return !!(alts && alts.has(g)); |
| } |
|
|
| function inBounds(grid, r, c) { |
| return r >= 0 && r < grid.length && |
| c >= 0 && c < (grid[r] ? grid[r].length : 0); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| function solve(grid, words) { |
| const results = {}; |
| const rows = grid.length; |
| if (rows === 0) return results; |
|
|
| for (const wordObj of words) { |
| const isExact = wordObj.word && !wordObj.word.includes('-'); |
| const isPattern = !!wordObj.pattern; |
|
|
| if (isExact) { |
| |
| const target = wordObj.word.toUpperCase(); |
| const len = target.length; |
| let found = false; |
|
|
| outer: |
| for (let r = 0; r < rows && !found; r++) { |
| for (let c = 0; c < grid[r].length && !found; c++) { |
| if (!charMatch(target[0], grid[r][c])) continue; |
| for (const dir of DIRECTIONS) { |
| const er = r + dir.r * (len - 1); |
| const ec = c + dir.c * (len - 1); |
| if (!inBounds(grid, er, ec)) continue; |
| let ok = true, candidate = ''; |
| for (let i = 0; i < len; i++) { |
| const nr = r + dir.r * i, nc = c + dir.c * i; |
| if (!inBounds(grid, nr, nc) || !charMatch(target[i], grid[nr][nc])) { |
| ok = false; break; |
| } |
| candidate += grid[nr][nc]; |
| } |
| if (ok) { |
| results[wordObj.word] = { r, c, dir: dir.name, match: candidate }; |
| found = true; break; |
| } |
| } |
| } |
| } |
|
|
| } else if (isPattern) { |
| |
| const pattern = wordObj.pattern.toUpperCase(); |
| const startChar = pattern[0]; |
| const len = pattern.length; |
| const hits = []; |
|
|
| for (let r = 0; r < rows; r++) { |
| for (let c = 0; c < grid[r].length; c++) { |
| if (!charMatch(startChar, grid[r][c])) continue; |
|
|
| for (const dir of DIRECTIONS) { |
| const er = r + dir.r * (len - 1); |
| const ec = c + dir.c * (len - 1); |
| if (!inBounds(grid, er, ec)) continue; |
|
|
| let ok = true, candidate = ''; |
| for (let i = 0; i < len; i++) { |
| const nr = r + dir.r * i, nc = c + dir.c * i; |
| if (!inBounds(grid, nr, nc)) { ok = false; break; } |
| const ch = grid[nr][nc]; |
| if (!ch || ch === ' ') { ok = false; break; } |
| candidate += ch; |
| } |
| if (ok && candidate.length === len) { |
| hits.push({ r, c, dir: dir.name, match: candidate }); |
| } |
| } |
| } |
| } |
|
|
| |
| const seen = new Set(); |
| const unique = hits.filter(h => !seen.has(h.match) && seen.add(h.match)); |
| if (unique.length > 0) results[pattern] = unique; |
| } |
| } |
|
|
| return results; |
| } |
|
|
| |
| let leaderboard = []; |
| const getWordScore = word => word.length * 10; |
| const getLeaderboard = () => leaderboard; |
| function recordScore(userName, score) { |
| leaderboard.push({ name: userName, score, date: new Date().toISOString() }); |
| leaderboard.sort((a, b) => b.score - a.score); |
| leaderboard = leaderboard.slice(0, 10); |
| } |
|
|
| module.exports = { solve, charMatch, getWordScore, recordScore, getLeaderboard }; |
|
|