您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Do trivial deduction.
当前为
// ==UserScript== // @name Puzz.link Assistance // @version 23.11.29.1 // @description Do trivial deduction. // @author Leaving Leaves // @match https://puzz.link/p*/* // @match https://pzplus.tck.mn/p*/* // @match http://pzv.jp/p*/* // @icon https://www.google.com/s2/favicons?sz=64&domain=puzz.link // @grant none // @namespace https://gf.qytechs.cn/users/1192854 // @license GPL // ==/UserScript== 'use strict'; const MAXLOOP = 30; const MAXDFSCELLNUM = 200; let flg = true; let step = false; let board; let GENRENAME; // used for showing pattern // ×·█━┃┓┛┗┏╺╹╸╻●○ // const list const CQNUM = { quesmark: -2, circle: -2, // no number black: -2, none: -1, wcir: 1, bcir: 2, }; const CANUM = { none: -1, // Masyu wcir: 1, bcir: 2, }; const CQANS = { none: 0, black: 1, // Light and Shadow white: 2, // Starbattle star: 1, // Akari light: 1, // Shakashaka triangle bl: 2, br: 3, tr: 4, tl: 5, // Slant rslash: 31, lslash: 32, }; const CQUES = { none: 0, // Castle Wall gray: 0, white: 1, black: 2, // Icebarn ice: 6, // Simpleloop bwall: 7, // Slalom vgate: 21, hgate: 22, // Nurimaze cir: 41, tri: 42, }; const CQSUB = { none: 0, dot: 1, green: 1, // Slitherlink yellow: 2, // All or Nothing gray: 1, }; const QDIR = { none: 0, // arrow up: 1, dn: 2, lt: 3, rt: 4, }; const BQSUB = { none: 0, link: 1, cross: 2, // Icebarn arrow_up: 11, arrow_dn: 12, arrow_lt: 13, arrow_rt: 14, }; // TODO: Moon or Sun, Pencils, Fillomino, Country Road const GENRELIST = [ ["Akari", AkariAssist], ["All or Nothing", AllorNothingAssist], ["Aqre", AqreAssist], ["Aquapelago", AquapelagoAssist], ["Ayeheya", AyeheyaAssist], ["Castle Wall", CastleWallAssist], ["Cave", CaveAssist], ["Choco Banana", ChocoBananaAssist], ["Guide Arrow", GuideArrowAssist], ["Heyawake", HeyawakeAssist], ["Hitori", HitoriAssist], ["Icebarn", IcebarnAssist], ["Koburin", KoburinAssist], ["Kurodoko", KurodokoAssist], ["Light and Shadow", LightandShadowAssist], ["LITS", LitsAssist], ["Masyu", MasyuAssist], ["Norinori", NorinoriAssist], ["No Three", NothreeAssist], ["Nurikabe", NurikabeAssist], ["Nuri-Maze", NuriMazeAssist], ["Nurimisaki", NurimisakiAssist], ["Shakashaka", ShakashakaAssist], ["Shikaku", ShikakuAssist], ["Simple Loop", SimpleloopAssist], ["Slalom", SlalomAssist], ["Slant", SlantAssist], ["Slitherlink", SlitherlinkAssist], ["Square Jam", SquareJamAssist], ["Star Battle", StarbattleAssist], ["Tapa", TapaAssist], ["Tasquare", TasquareAssist], ["Tentaisho", TentaishoAssist], ["Yajilin", YajilinAssist], ["Yin-Yang", YinyangAssist], ]; // main entrance ui.puzzle.on('ready', function () { console.log("Assistance running..."); GENRENAME = ui.puzzle.info.en; if (GENRELIST.some(g => g[0] === GENRENAME)) { let btn = '<button type="button" class="btn" id="assist" style="display: inline;">Assist</button>'; let btn2 = '<button type="button" class="btn" id="assiststep" style="display: inline;">Assist Step</button>'; document.querySelector('#btntrial').insertAdjacentHTML('afterend', btn); document.querySelector("#assist").insertAdjacentHTML('afterend', btn2); document.querySelector("#assist").addEventListener("click", assist, false); document.querySelector("#assiststep").addEventListener("click", assiststep, false); window.addEventListener("keypress", (event) => { if (event.key === 'q' || (event.key === 'Q')) { assist(); } if (event.key === 'w' || (event.key === 'W')) { assiststep(); } }); } }, false); function assiststep() { step = true; assist(); step = false; } function assist() { flg = true; board = ui.puzzle.board; for (let loop = 0; loop < (step ? 1 : MAXLOOP); loop++) { if (!flg) { break; } flg = false; GENRELIST.find(g => g[0] === GENRENAME)[1](); } ui.puzzle.redraw(); console.log('Assisted.'); } // set val let offset = function (c, dx, dy, dir = 0) { dir = (dir % 4 + 4) % 4; if (dir === 0) { return board.getobj(c.bx + dx * 2, c.by + dy * 2); } if (dir === 1) { return board.getobj(c.bx + dy * 2, c.by - dx * 2); } if (dir === 2) { return board.getobj(c.bx - dx * 2, c.by - dy * 2); } if (dir === 3) { return board.getobj(c.bx - dy * 2, c.by + dx * 2); } } let adjlist = function (a) { return [a.top, a.left, a.bottom, a.right]; } let fourside = function (f, a, b = undefined) { if (b === undefined) { f(a.top); f(a.left); f(a.bottom); f(a.right); } else { f(a.top, b.top); f(a.left, b.left); f(a.bottom, b.bottom); f(a.right, b.right); } }; let dir = function (c, d) { d = (d % 4 + 4) % 4; if (d === 0) return c.top; if (d === 1) return c.left; if (d === 2) return c.bottom; if (d === 3) return c.right; } let qdirremap = function (qdir) { return [-1, 0, 2, 1, 3][qdir]; } let add_link = function (b) { if (b === undefined || b.isnull || b.line || b.qans || b.qsub !== BQSUB.none) { return; } if (step && flg) { return; } b.setQsub(BQSUB.link); b.draw(); flg |= b.qsub === BQSUB.link; }; let add_cross = function (b) { if (b === undefined || b.isnull || b.line || b.qsub !== BQSUB.none) { return; } if (step && flg) { return; } b.setQsub(BQSUB.cross); b.draw(); flg |= b.qsub === BQSUB.cross; }; let add_line = function (b) { if (b === undefined || b.isnull || b.line || b.qsub === BQSUB.cross) { return; } if (step && flg) { return; } b.setLine(1); b.draw(); flg |= b.line; }; let add_side = function (b) { if (b === undefined || b.isnull || b.qans || b.qsub === BQSUB.link) { return; } if (step && flg) { return; } b.setQans(1); b.draw(); flg |= b.qans; }; let add_arrow = function (b, dir) { if (b === undefined || b.isnull || b.qsub !== BQSUB.none) { return; } if (step && flg) { return; } flg = true; b.setQsub(dir); b.draw(); }; let add_black = function (c, notOnNum = false) { if (notOnNum && (c.qnum !== CQNUM.none || c.qnums.length > 0)) { return; } if (c === undefined || c.isnull || c.lcnt !== 0 || c.qsub === CQSUB.dot || c.qans !== CQANS.none) { return; } if (step && flg) { return; } flg = true; c.setQans(CQANS.black); c.draw(); }; let add_dot = function (c) { if (c === undefined || c.isnull || c.qnum !== CQNUM.none || c.qnums.length > 0 || c.qans !== CQANS.none || c.qsub === CQSUB.dot) { return; } if (step && flg) { return; } flg = true; c.setQsub(CQSUB.dot); c.draw(); }; let add_green = function (c) { if (c === undefined || c.isnull || c.qans !== CQANS.none || c.qsub === CQSUB.dot) { return; } if (step && flg) { return; } flg = true; c.setQsub(CQSUB.green); c.draw(); }; // single rule deduction function No2x2Cell({ isShaded, add_unshaded } = {}) { for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; let templist = [cell, offset(cell, 1, 0), offset(cell, 0, 1), offset(cell, 1, 1)]; if (templist.some(c => c.isnull)) { continue; } templist = templist.filter(c => !isShaded(c)); if (templist.length === 1) { add_unshaded(templist[0]); } } } function No2x2Black() { No2x2Cell({ isShaded: c => c.qans === CQANS.black, add_unshaded: add_green, }); } function No2x2Green() { No2x2Cell({ isShaded: c => c.qsub === CQSUB.green, add_unshaded: add_black, }); } function CellConnected({ isShaded, isUnshaded, add_shaded, add_unshaded, isLinked = (c, nb, nc) => isShaded(c) && isShaded(nc), isNotPassable = (c, nb, nc) => false, OutsideAsShaded = false } = {}) { // use tarjan to find cut vertex // Maximum call stack size exceeded sometimes for big puzzle let n = 0; let ord = new Map(); let low = new Map(); let shdn = new Map(); let shadelist = []; let dfs = function (c, f = null) { if (!c.isnull && isUnshaded(c) || ord.has(c)) { return; } if (c.isnull && !OutsideAsShaded) { return; } ord.set(c, n); low.set(n, n); shdn.set(n, 0); n++; const cellset = new Set(); if (!c.isnull) { let linkdfs = function (c) { if (c.isnull || cellset.has(c)) { return; } cellset.add(c); fourside((nb, nc) => { if (isLinked(c, nb, nc)) { linkdfs(nc); } }, c.adjborder, c.adjacent); } linkdfs(c); cellset.forEach(cl => { ord.set(cl, ord.get(c)); shdn.set(ord.get(cl), shdn.get(ord.get(cl)) + isShaded(cl)); }); } let fn = function (nc, nb) { if (isNotPassable(c, nb, nc)) { return; } if (nc === f || isUnshaded(nc)) { return; } if (nc.isnull && !OutsideAsShaded) { return; } if (ord.get(c) === ord.get(nc)) { return; } if (ord.has(nc)) { low.set(ord.get(c), Math.min(low.get(ord.get(c)), ord.get(nc))); return; } dfs(nc, c); let ordc = ord.get(c); let ordnc = ord.get(nc); low.set(ordc, Math.min(low.get(ordc), low.get(ordnc))); shdn.set(ordc, shdn.get(ordc) + shdn.get(ordnc)); if (ordc <= low.get(ordnc) && shdn.get(ordnc) > 0) { cellset.forEach(c => shadelist.push(c)); } }; if (!c.isnull) { for (let c of cellset) { // expand Fourside to avoid Maximum call stack size exceeded fn(c.adjacent.top, c.adjborder.top); fn(c.adjacent.bottom, c.adjborder.bottom); fn(c.adjacent.left, c.adjborder.left); fn(c.adjacent.right, c.adjborder.right); }; } else if (c.isnull) { for (let i = 0; i < board.cols; i++) { dfs(board.getc(2 * i + 1, board.minby + 1), c); dfs(board.getc(2 * i + 1, board.maxby - 1), c); } for (let i = 0; i < board.rows; i++) { dfs(board.getc(board.minbx + 1, 2 * i + 1), c); dfs(board.getc(board.maxbx - 1, 2 * i + 1), c); } } }; if (OutsideAsShaded) { dfs(board.getc(0, 0)); } else { for (let i = 0; i < board.cell.length; i++) { if (!isShaded(board.cell[i]) || ord.has(board.cell[i])) { continue; } dfs(board.cell[i]); } } shadelist.forEach(c => add_shaded(c)); if (ord.size > 0) { for (let i = 0; i < board.cell.length; i++) { if (ord.has(board.cell[i]) || isShaded(board.cell[i]) || isUnshaded(board.cell[i])) { continue; } add_unshaded(board.cell[i]); } } } function GreenConnected() { CellConnected({ isShaded: c => c.qsub === CQSUB.green, isUnshaded: c => c.qans === CQANS.black, add_shaded: add_green, add_unshaded: add_black, }); } function BlackConnected() { CellConnected({ isShaded: c => c.qans === CQANS.black, isUnshaded: c => c.qsub === CQSUB.green, add_shaded: add_black, add_unshaded: add_green, }); } function CellNoLoop({ isShaded, isUnshaded, add_unshaded } = {}) { let ord = new Map(); let n = 0; for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; if (!isShaded(cell) || ord.has(cell)) { continue; } let dfs = function (c) { if (c.isnull || !isShaded(c) || ord.has(c)) { return; } ord.set(c, n); fourside(dfs, c.adjacent); } dfs(cell); n++; } for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; if (isShaded(cell) || isUnshaded(cell)) { continue; } let templist = [offset(cell, -1, 0), offset(cell, 0, -1), offset(cell, 0, 1), offset(cell, 1, 0)]; templist = templist.filter(c => !c.isnull && isShaded(c)); templist = templist.map(c => ord.get(c)); for (let i = 0; i < templist.length; i++) { for (let j = i + 1; j < templist.length; j++) { if (templist[i] === templist[j]) { add_unshaded(cell); } } } } } function GreenNoLoopInCell() { CellNoLoop({ isShaded: c => c.qsub === CQSUB.green, isUnshaded: c => c.qans === CQANS.black, add_unshaded: add_black, }); } function BlackNotAdjacent() { for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; if (cell.qans !== CQANS.black) { continue; } fourside(add_green, cell.adjacent); } } function SingleLoopInCell({ isPassable = c => true, isPathable = b => b.qsub !== BQSUB.cross, isPass = c => c.qsub === CQSUB.dot, isPath = b => b.line, add_notpass = c => { }, add_pass = c => { }, add_notpath = add_cross, add_path = add_line } = {}) { for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; if (!isPassable(cell)) { add_notpass(cell); fourside(add_notpath, cell.adjborder); } let emptycnt = 0; let linecnt = 0; let adjcell = cell.adjacent; let adjline = cell.adjborder; fourside((c, b) => { if (!isPassable(c) || !isPathable(b)) { add_notpath(b); } if (!c.isnull && isPassable(c) && isPathable(b)) { emptycnt++; } linecnt += isPath(b); }, adjcell, adjline); if (linecnt > 0) { add_pass(cell); } // no branch if (linecnt === 2 && cell.ques !== CQUES.ice) { fourside(add_notpath, adjline); } // no deadend if (emptycnt <= 1) { fourside(add_notpath, adjline); add_notpass(cell); } // 2 degree path if (emptycnt === 2 && (linecnt === 1 || isPass(cell))) { fourside(add_path, adjline); } // avoid forming multiple loop if (cell.path !== null && cell.ques !== CQUES.ice) { for (let d = 0; d < 4; d++) { let ncell = dir(adjcell, d); if (cell.path === ncell.path && ncell.ques !== CQUES.ice && board.linegraph.components.length > 1) { add_notpath(dir(adjline, d)); } } } // ┏╸ ┏╸ // ┃· -> ┃╺━ // ┗╸ ┗╸ if (cell.lcnt === 0 && isPass(cell)) { let list = []; fourside((c, b) => { if (isPathable(b)) { list.push([c, b]); } }, adjcell, adjline); if (list.length === 3) { let fn = function (a, b, c) { if (a[0].path !== null && a[0].path === b[0].path && board.linegraph.components.length > 1) { add_path(c[1]); } } fn(list[0], list[1], list[2]); fn(list[1], list[2], list[0]); fn(list[2], list[0], list[1]); } } } } function NoCheckerCell({ isShaded, isUnshaded, add_shaded, add_unshaded } = {}) { for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; if (isShaded(cell) || isUnshaded(cell)) { continue; } let fn = function (c, c1, c2, c12) { if (isShaded(c1) && isShaded(c2) && isUnshaded(c12)) { add_shaded(c); } if (isUnshaded(c1) && isUnshaded(c2) && isShaded(c12)) { add_unshaded(c); } }; for (let d = 0; d < 4; d++) { fn(cell, offset(cell, 1, 0, d), offset(cell, 0, 1, d), offset(cell, 1, 1, d)); } } } function SightNumber({ isShaded, isUnshaded, add_shaded, add_unshaded } = {}) { for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; let qnum = cell.qnum; if (cell.qnum !== CQNUM.none) { add_shaded(cell); } if (cell.qnum !== CQNUM.none && cell.qnum !== CQNUM.quesmark) { let seencnt = (isShaded(cell) ? 1 : 0); let farthest = [0, 0, 0, 0]; // count seen green cells for (let d = 0; d < 4; d++) { let pcell = dir(cell.adjacent, d); while (!pcell.isnull && isShaded(pcell)) { farthest[d]++; seencnt++; pcell = dir(pcell.adjacent, d); } while (!pcell.isnull && !isUnshaded(pcell)) { farthest[d]++; pcell = dir(pcell.adjacent, d); } } // not extend too much for (let d = 0; d < 4; d++) { let pcell = dir(cell.adjacent, d); while (!pcell.isnull && isShaded(pcell)) { pcell = dir(pcell.adjacent, d); } if (pcell.isnull || isUnshaded(pcell)) { continue; } let tcell = pcell; pcell = dir(pcell.adjacent, d); let n = 0; while (!pcell.isnull && isShaded(pcell)) { n++; pcell = dir(pcell.adjacent, d); } if (n + seencnt + 1 > qnum) { add_unshaded(tcell); } } // must extend this way let maxn = farthest.reduce((a, b) => a + b) + (isShaded(cell) ? 1 : 0); for (let d = 0; d < 4; d++) { for (let j = 1; j <= qnum - maxn + farthest[d]; j++) { add_green(offset(cell, 0, -j, d)); } } } } } function SizeRegion_Cell({ isShaded, isUnshaded, add_shaded, add_unshaded, OneNumPerRegion = true, NoUnshadedNum = true } = {}) { // maybe rewrite this someday for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; // don't block region exit let templist = [offset(cell, -1, -1), offset(cell, -1, 0), offset(cell, -1, 1), offset(cell, 0, -1), offset(cell, 0, 1), offset(cell, 1, -1), offset(cell, 1, 0), offset(cell, 1, 1)]; if (!isShaded(cell) && !isUnshaded(cell) && templist.filter(c => isUnshaded(c) || c.isnull).length >= 2) { for (let d = 0; d < 4; d++) { let ncell = dir(cell.adjacent, d); if (isUnshaded(ncell)) { continue; } let cellList = []; let dfs = function (c) { if (cellList.length > MAXDFSCELLNUM) { return; } if (c.isnull || isUnshaded(c) || c === cell || cellList.includes(c)) { return; } cellList.push(c); fourside(dfs, c.adjacent); } dfs(ncell); if (cellList.length > MAXDFSCELLNUM) { continue; } let templist = cellList.filter(c => c.qnum !== CQNUM.none && (NoUnshadedNum || isShaded(c))); // extend region without num if (templist.length === 0 && cellList.some(c => isShaded(c)) && OneNumPerRegion) { add_shaded(cell); } // extend region with less cells if (templist.length >= 1 && templist[0].qnum !== CQNUM.quesmark && templist[0].qnum > cellList.length) { add_shaded(cell); } } } // finished region if (cell.qnum > 0 && isShaded(cell)) { let cellList = []; let dfs = function (c) { if (cellList.length > cell.qnum) { return; } if (c.isnull || !isShaded(c) || cellList.includes(c)) { return; } cellList.push(c); fourside(dfs, c.adjacent); } dfs(cell); if (cellList.length === cell.qnum) { cellList.forEach(c => fourside(add_unshaded, c.adjacent)); } } // finished surrounded region if (cell.qnum > 0 && (NoUnshadedNum || isShaded(cell))) { let cellList = []; let dfs = function (c) { if (cellList.length > cell.qnum) { return; } if (c.isnull || isUnshaded(c) || cellList.includes(c)) { return; } cellList.push(c); fourside(dfs, c.adjacent); } dfs(cell); if (cell.qnum !== CQNUM.quesmark && cell.qnum === cellList.length) { cellList.forEach(c => add_shaded(c)); } } // not connect two region for (let d1 = 0; d1 < 4; d1++) { for (let d2 = d1 + 1; d2 < 4; d2++) { if (isShaded(cell) || isUnshaded(cell)) { continue; } let cell1 = dir(cell.adjacent, d1); let cell2 = dir(cell.adjacent, d2); if (cell1.isnull || cell2.isnull || !isShaded(cell1) || !isShaded(cell2)) { continue; } let cellList1 = []; let cellList2 = []; let dfs = function (c, list) { if (c.isnull || !isShaded(c) || list.includes(c)) { return; } list.push(c); dfs(c.adjacent.top, list); dfs(c.adjacent.bottom, list); dfs(c.adjacent.left, list); dfs(c.adjacent.right, list); } dfs(cell1, cellList1); dfs(cell2, cellList2); if (cellList1.includes(cell2)) { continue; } let templist1 = cellList1.filter(c => c.qnum !== CQNUM.none); let templist2 = cellList2.filter(c => c.qnum !== CQNUM.none); if (templist1.length >= 1 && templist2.length >= 1) { if (templist1[0].qnum !== CQNUM.quesmark && templist2[0].qnum !== CQNUM.quesmark && templist1[0].qnum !== templist2[0].qnum || OneNumPerRegion) { add_unshaded(cell); } } if (templist1.length + templist2.length >= 1) { let qnum = (templist1.length >= 1 ? templist1[0] : templist2[0]).qnum; if (qnum !== CQNUM.quesmark && cellList1.length + cellList2.length + 1 > qnum) { add_unshaded(cell); } } if (cell.qnum >= 0 && cellList1.length + cellList2.length + 1 > cell.qnum) { add_unshaded(cell); } } } // cell and region for (let d = 0; d < 4; d++) { if (isShaded(cell) || isUnshaded(cell) || cell.qnum === CQNUM.none) { continue; } let ncell = dir(cell.adjacent, d); if (ncell.isnull || !isShaded(ncell)) { continue; } let cellList = []; let dfs = function (c) { if (c.isnull || !isShaded(c) || cellList.includes(c)) { return; } cellList.push(c); fourside(dfs, c.adjacent); } dfs(ncell, cellList); let templist = cellList.filter(c => c.qnum !== CQNUM.none); if (templist.length >= 1 && (templist[0].qnum !== CQNUM.quesmark && cell.qnum !== CQNUM.quesmark && templist[0].qnum !== cell.qnum || OneNumPerRegion)) { add_unshaded(cell); } if (cell.qnum !== CQNUM.quesmark && cellList.length + 1 > cell.qnum) { add_unshaded(cell); } } } } function RectRegion_Cell({ isShaded, isUnshaded, add_shaded, add_unshaded, isSquare = false }) { for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; if (isShaded(cell) || isUnshaded(cell)) { continue; } // can't be 3 shades in each 2*2 let fn = function (list) { if (list.some(c => c.isnull)) { return; } if (list.filter(c => isShaded(c)).length === 2 && list.filter(c => isUnshaded(c)).length === 1) { add_unshaded(cell); } if (list.filter(c => isShaded(c)).length === 3 && list.filter(c => isUnshaded(c)).length === 0) { add_shaded(cell); } }; for (let d = 0; d < 4; d++) { fn([offset(cell, 1, 0, d), offset(cell, 0, 1, d), offset(cell, 1, 1, d)]); } } if (!isSquare) { return; } let shadelist = []; for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; if (!isShaded(cell)) { continue; } // █ -> █ // · · ··· for (let d = 0; d < 4; d++) { if ([offset(cell, -1, 1, d), offset(cell, 1, 1, d)].every(c => c.isnull || isUnshaded(c))) { add_unshaded(offset(cell, 0, 1, d)); } } if ((c => !c.isnull && isShaded(c))(offset(cell, 0, -1))) { continue; } if ((c => !c.isnull && isShaded(c))(offset(cell, -1, 0))) { continue; } let height = 1, width = 1; while ((c => !c.isnull && isShaded(c))(offset(cell, 0, height - 1))) { height++; } while ((c => !c.isnull && isShaded(c))(offset(cell, width - 1, 0))) { width++; } height--; width--; // finished square if (width === height) { let templist = [offset(cell, -1, 0), offset(cell, 0, -1), offset(cell, width, 0), offset(cell, 0, height)]; if ([templist[0], templist[2]].every(c => c.isnull || isUnshaded(c)) || [templist[1], templist[3]].every(c => c.isnull || isUnshaded(c))) { templist.forEach(c => add_unshaded(c)); } } // extend square if (height > width) { for (let j = 0; j < height; j++) { let c = offset(cell, 0, j); let l = 0, r = 0; while ((c => !c.isnull && !isUnshaded(c))(offset(c, l, 0)) && l > width - height - 1) { l--; } while ((c => !c.isnull && !isUnshaded(c))(offset(c, r, 0)) && r < height) { r++; } for (let k = r - height; k <= l + height; k++) { shadelist.push(offset(c, k, 0)); } } } if (height < width) { for (let j = 0; j < width; j++) { let c = offset(cell, j, 0); let l = 0, r = 0; while ((c => !c.isnull && !isUnshaded(c))(offset(c, 0, l)) && l > height - width - 1) { l--; } while ((c => !c.isnull && !isUnshaded(c))(offset(c, 0, r)) && r < width) { r++; } for (let k = r - width; k <= l + width; k++) { shadelist.push(offset(c, 0, k)); } } } } shadelist.forEach(c => add_shaded(c)); } function RectRegion_Border({ isSquare = false } = {}) { let isLink = b => !b.isnull && b.qsub === BQSUB.link; let isSide = b => b.isnull || b.qans; for (let i = 0; i < board.cross.length; i++) { let cross = board.cross[i]; for (let d = 0; d < 4; d++) { // × × // ×· -> ×·× // × let b1 = dir(cross.adjborder, d); let b2 = dir(cross.adjborder, d + 1); if (isLink(b1) && isLink(b2)) { add_link(dir(cross.adjborder, d + 2)); add_link(dir(cross.adjborder, d + 3)); } // ┃ ┃ // ×╹ -> ×┃ // ┃ if (isSide(b1) && isLink(b2)) { add_side(dir(cross.adjborder, d + 2)); } // × × // ━╸ -> ━━━ // if (isLink(b1) && isSide(b2)) { add_side(dir(cross.adjborder, d + 3)); } // ┃ ┃ // ╹ -> ━┻━ // × × b2 = dir(cross.adjborder, d + 2); if (isSide(b1) && isLink(b2)) { add_side(dir(cross.adjborder, d + 1)); add_side(dir(cross.adjborder, d + 3)); } } } if (!isSquare) { return; } let drawlist = []; for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; if (isLink(cell.adjborder.top)) { continue; } if (isLink(cell.adjborder.left)) { continue; } let height = 1, width = 1; while (isLink(offset(cell, 0, height - .5))) { height++; } while (isLink(offset(cell, width - .5, 0))) { width++; } // finished square if (width === height) { let templist = [offset(cell, -.5, 0), offset(cell, 0, -.5), offset(cell, width - .5, 0), offset(cell, 0, height - .5)]; if ([templist[0], templist[2]].every(c => isSide(c)) || [templist[1], templist[3]].every(c => isSide(c))) { templist.forEach(c => add_side(c)); } } // extend square if (height > width) { for (let j = 0; j < height; j++) { let c = offset(cell, 0, j); let l = 0, r = 0; while (!isSide(offset(c, l - .5, 0)) && l > width - height) { l--; } while (!isSide(offset(c, r + .5, 0)) && r < height - 1) { r++; } for (let k = r - height + 1.5; k <= l + height - 1.5; k++) { drawlist.push(offset(c, k, 0)); } } } if (height < width) { for (let j = 0; j < width; j++) { let c = offset(cell, j, 0); let l = 0, r = 0; while (!isSide(offset(c, 0, l - .5)) && l > height - width) { l--; } while (!isSide(offset(c, 0, r + .5)) && r < width - 1) { r++; } for (let k = r - width + 1.5; k <= l + width - 1.5; k++) { drawlist.push(offset(c, 0, k)); } } } } drawlist.forEach(b => add_link(b)); } // assist for certain genre function ShikakuAssist() { RectRegion_Border(); let n = 0; let id = new Map(); // map every cell to unique clue id let id_info = new Map(); // get region info // assign id for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; if (cell.qnum === CQNUM.none) { continue; } n++; let x1 = cell.bx, x2 = cell.bx, y1 = cell.by, y2 = cell.by; let dfs = function (c) { if (id.has(c)) { return; } id.set(c, n); x1 = Math.min(x1, c.bx); x2 = Math.max(x2, c.bx); y1 = Math.min(y1, c.by); y2 = Math.max(y2, c.by); fourside((nb, nc) => { if (nb.qsub === BQSUB.link) { dfs(nc); } }, c.adjborder, c.adjacent) } dfs(cell); if ((y2 - y1 + 2) * (x2 - x1 + 2) === 4 * cell.qnum) { for (let i = x1; i <= x2; i += 2) { add_side(board.getb(i, y1 - 1)); add_side(board.getb(i, y2 + 1)); } for (let i = y1; i <= y2; i += 2) { add_side(board.getb(x1 - 1, i)); add_side(board.getb(x2 + 1, i)); } } id_info.set(n, { cell: cell, x1: x1, x2: x2, y1: y1, y2: y2 }); } // check available rectangle let add_n = function (c, idn) { let obj = []; if (id.has(c)) { obj = id.get(c); } if (typeof (obj) !== "number" && !obj.includes(idn)) { obj.push(idn); } id.set(c, obj); } let fn = function (sx, sy, h, w, idn) { for (let i = 0; i < w; i++) for (let j = 0; j < h; j++) { let cell = board.getc(sx + i * 2, sy + j * 2); if (cell.isnull) { return 0; } let obj = []; if (id.has(cell)) { obj = id.get(cell); } if (typeof (obj) === "number" && obj !== idn) { return 0; } } let list = []; for (let i = 0; i < w; i++) for (let j = 0; j < h; j++) { let cell = board.getc(sx + i * 2, sy + j * 2); add_n(cell, idn); if (!list.includes(cell)) { list.push(cell); } } return list; } // try every possible rectangle for each clue for (let i = 1; i <= n; i++) { let info = id_info.get(i); let cell = info.cell; let x1 = info.x1, x2 = info.x2, y1 = info.y1, y2 = info.y2; let isExtendable = c => !c.isnull && (!id.has(c) || typeof (id.get(c)) !== "number") || id.get(c) === i; if (cell.qnum === CQNUM.none) { continue; } if (cell.qnum === CQNUM.quesmark) { for (let d = 0; d < 4; d++) { let c = cell; let maxn = Math.max(board.cols, board.rows); while (isExtendable(offset(c, 1, 0, d))) { c = offset(c, 1, 0, d); add_n(c, i); let c2 = c; let n = 0; while (isExtendable(offset(c2, 0, 1, d)) && n < maxn) { c2 = offset(c2, 0, 1, d); add_n(c2, i); n++; } maxn = Math.min(maxn, n); } } continue; } let list = null; for (let h = Math.ceil(cell.qnum / board.cols); h <= Math.min(cell.qnum, board.rows); h++) if (cell.qnum % h === 0) { let w = cell.qnum / h; for (let sx = Math.max(board.minbx + 1, x2 - (w - 1) * 2); sx <= Math.min(board.maxbx - 1, x1); sx += 2) for (let sy = Math.max(board.minby + 1, y2 - (h - 1) * 2); sy <= Math.min(board.maxby - 1, y1); sy += 2) { let tmp = fn(sx, sy, h, w, i); if (tmp === 0) { continue; } if (list === null) { list = tmp; } else { list = list.filter(c => tmp.includes(c)); } } } list.forEach(c => id.set(c, i)); } // apply cell with only one possibility for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; if (!id.has(cell)) { continue; } let obj = id.get(cell); if (typeof (obj) === "number" || obj.length !== 1) { continue; } let info = id_info.get(obj[0]); for (let x = Math.min(cell.bx, info.x1); x <= Math.max(cell.bx, info.x2); x += 2) for (let y = Math.min(cell.by, info.y1); y <= Math.max(cell.by, info.y2); y += 2) { id.set(board.getc(x, y), obj[0]); } } // add link and side for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; if (!id.has(cell) || typeof (id.get(cell)) !== "number") { continue; } fourside((nb, nc) => { if (nc.isnull) { return; } if (!id.has(nc) || typeof (id.get(nc)) !== "number") { return; } if (id.get(cell) === id.get(nc)) { add_link(nb); } if (id.get(cell) !== id.get(nc)) { add_side(nb); } }, cell.adjborder, cell.adjacent); } } function SquareJamAssist() { // TODO: make it more useful RectRegion_Border({ isSquare: true }); for (let i = 0; i < board.cross.length; i++) { let cross = board.cross[i]; let list = adjlist(cross.adjborder); if (list.filter(b => !b.isnull && b.qans).length === 3) { list.forEach(b => add_link(b)); } } for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; if (cell.qnum < 0) { continue; } let h1 = -.5, h2 = .5; while ((b => !b.isnull && b.qsub === BQSUB.link)(offset(cell, 0, h1))) { h1--; } while ((b => !b.isnull && b.qsub === BQSUB.link)(offset(cell, 0, h2))) { h2++; } if (cell.qnum === h2 - h1) { add_side(offset(cell, 0, h1)); add_side(offset(cell, 0, h2)); } let w1 = -.5, w2 = .5; while ((b => !b.isnull && b.qsub === BQSUB.link)(offset(cell, w1, 0))) { w1--; } while ((b => !b.isnull && b.qsub === BQSUB.link)(offset(cell, w2, 0))) { w2++; } if (cell.qnum === w2 - w1) { add_side(offset(cell, w1, 0)); add_side(offset(cell, w2, 0)); } } } function TasquareAssist() { RectRegion_Cell({ isShaded: c => c.qans === CQANS.black, isUnshaded: c => c.qsub === CQSUB.green || c.qnum !== CQNUM.none, add_shaded: add_black, add_unshaded: add_dot, isSquare: true, }); CellConnected({ isShaded: c => c.qsub === CQSUB.dot || c.qnum !== CQNUM.none, isUnshaded: c => c.qans === CQANS.black, add_shaded: add_dot, add_unshaded: add_black, }); let is2x2able = function (c) { for (let d = 0; d < 4; d++) { let list = [c, offset(c, 0, 1, d), offset(c, 1, 0, d), offset(c, 1, 1, d)]; if (list.every(c => !c.isnull && c.qsub !== CQSUB.dot && c.qnum === CQNUM.none)) { return true; } } return false; } let isNotBlack = c => c.isnull || c.qsub === CQSUB.dot || c.qnum !== CQNUM.none; for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; if (cell.qnum === CQNUM.none) { continue; } add_dot(cell); let templist = adjlist(cell.adjacent); if (templist.filter(c => !isNotBlack(c)).length === 1) { templist.forEach(c => add_black(c, true)); } if (cell.qnum === CQNUM.quesmark) { continue; } // black cells: n around 0~3, n-3 around 4~7, 2 around 8 if (cell.qnum <= 8) { let list = adjlist(cell.adjacent).filter(c => !isNotBlack(c)); if ([0, 1, 2, 3].includes(cell.qnum) && list.length === cell.qnum || [4, 5, 6].includes(cell.qnum) && list.length === cell.qnum - 3 || cell.qnum === 8 && list.length === 2) { list.forEach(c => add_black(c)); } } // 1*2^2 around 4~6, 2*2^2 around 8 if (cell.qnum >= 4 && cell.qnum <= 8) { let list = []; for (let d = 0; d < 4; d++) { list.push([offset(cell, 2, 0, d), offset(cell, 1, 0, d)]); } list = list.filter(l => is2x2able(l[1])); if (list.length === Math.floor(cell.qnum / 4)) { list.forEach(l => { add_black(l[0]), add_black(l[1]) }); } } // · · // 3 -> 3 // · · if (cell.qnum === 3) { add_dot(offset(cell, -1, -1)); add_dot(offset(cell, -1, 1)); add_dot(offset(cell, 1, -1)); add_dot(offset(cell, 1, 1)); } for (let d = 0; d < 4; d++) { // ? █ -> ?·█ (?<=3) if (cell.qnum <= 3 && offset(cell, 2, 0, d).qans === CQANS.black) { add_dot(offset(cell, 1, 0, d)); } // 4 · -> 4·· if (cell.qnum === 4 && isNotBlack(offset(cell, 2, 0, d))) { add_dot(offset(cell, 1, 0, d)); } // · · // ·1 -> ·1 // · if (cell.qnum === 1 && isNotBlack(offset(cell, 0, -1, d)) && isNotBlack(offset(cell, -1, 0, d))) { add_dot(offset(cell, 1, 1, d)); } // · · // 2 -> 2 // · · if (cell.qnum === 2 && isNotBlack(offset(cell, 0, -1, d))) { add_dot(offset(cell, -1, 1, d)); add_dot(offset(cell, 1, 1, d)); } } let blist = []; let dfs = function (c) { if (blist.includes(c) || c.isnull || c.qans !== CQANS.black) { return; } blist.push(c); fourside(dfs, c.adjacent); } adjlist(cell.adjacent).forEach(c => dfs(c)); if (blist.length === cell.qnum) { fourside(add_dot, cell.adjacent); blist.forEach(c => fourside(add_dot, c.adjacent)); } } } function TentaishoAssist() { let isDot = obj => obj.qnum > 0; let isEmpty = c => !c.isnull && c.ques !== CQUES.bwall; for (let i = 0; i < board.cross.length; i++) { let cross = board.cross[i]; let list = adjlist(cross.adjborder); if (list.filter(b => !b.isnull && b.qsub === BQSUB.link).length === 2 && cross.lcnt === 1) { list.forEach(b => add_side(b)); } if (list.filter(b => !b.isnull && b.qsub === BQSUB.link).length === 3) { list.forEach(b => add_link(b)); } } let n = 0; let id = new Map(); // map every cell to unique dot id let dotmap = new Map(); // get dot obj let bfs_id = function (clist, n) { while (clist.length > 0) { let c = clist.pop(); let x = dotmap.get(n).bx; let y = dotmap.get(n).by; if (!isEmpty(c) || id.has(c)) { continue; } id.set(c, n); let fn = function (bbx, bby, cbx, cby) { let nb = board.getb(bbx, bby); let nc = board.getc(cbx, cby); if (!isEmpty(nc) || nb.qans || id.has(nc) && id.get(nc) !== id.get(c)) { add_side(nb); add_side(board.getb(2 * x - bbx, 2 * y - bby)); } if (id.has(nc) && id.get(nc) === id.get(c)) { add_link(nb); } if (isEmpty(nc) && nb.qsub === BQSUB.link) { add_link(board.getb(2 * x - bbx, 2 * y - bby)); clist.push(nc); clist.push(board.getc(2 * x - cbx, 2 * y - cby)); } }; fn(c.bx - 1, c.by, c.bx - 2, c.by); fn(c.bx + 1, c.by, c.bx + 2, c.by); fn(c.bx, c.by - 1, c.bx, c.by - 2); fn(c.bx, c.by + 1, c.bx, c.by + 2); } }; let id_choice = new Map(); let dfs_idc = function (c, n) { let x = dotmap.get(n).bx; let y = dotmap.get(n).by; let oc = board.getc(2 * x - c.bx, 2 * y - c.by); if (!isEmpty(c) || id.has(c) && id.get(c) !== n) { return; } if (!isEmpty(oc) || id.has(oc) && id.get(oc) !== n) { return; } if (id_choice.has(c) && id_choice.get(c).includes(n)) { return; } if (id_choice.has(oc) && id_choice.get(oc).includes(n)) { return; } if (!id_choice.has(c)) { id_choice.set(c, []); } if (!id_choice.has(oc)) { id_choice.set(oc, []); } id_choice.set(c, id_choice.get(c).concat([n])); id_choice.set(oc, id_choice.get(oc).concat([n])); fourside((nb, nc) => { if (nb.qans) { return; } dfs_idc(nc, n); }, c.adjborder, c.adjacent); }; // assign cells to dots and deduce for (let x = board.minbx + 1; x <= board.maxbx - 1; x++) { for (let y = board.minby + 1; y <= board.maxby - 1; y++) { if (isDot(board.getobj(x, y))) { n++; dotmap.set(n, board.getobj(x, y)); let clist = []; if (x % 2 === 1 && y % 2 === 1) { clist.push(board.getc(x, y)); } if (x % 2 === 1 && y % 2 === 0) { clist.push(board.getc(x, y - 1)); clist.push(board.getc(x, y + 1)); } if (x % 2 === 0 && y % 2 === 1) { clist.push(board.getc(x - 1, y)); clist.push(board.getc(x + 1, y)); } if (x % 2 === 0 && y % 2 === 0) { clist.push(board.getc(x - 1, y - 1)); clist.push(board.getc(x - 1, y + 1)); clist.push(board.getc(x + 1, y - 1)); clist.push(board.getc(x + 1, y + 1)); } bfs_id(clist, n); } } } // assign cells to possible dots n = 0; for (let x = board.minbx + 1; x <= board.maxbx - 1; x++) { for (let y = board.minby + 1; y <= board.maxby - 1; y++) { if (isDot(board.getobj(x, y))) { n++; let clist = []; if (x % 2 === 1 && y % 2 === 1) { clist.push(board.getc(x, y)); } if (x % 2 === 1 && y % 2 === 0) { clist.push(board.getc(x, y - 1)); clist.push(board.getc(x, y + 1)); } if (x % 2 === 0 && y % 2 === 1) { clist.push(board.getc(x - 1, y)); clist.push(board.getc(x + 1, y)); } if (x % 2 === 0 && y % 2 === 0) { clist.push(board.getc(x - 1, y - 1)); clist.push(board.getc(x - 1, y + 1)); clist.push(board.getc(x + 1, y - 1)); clist.push(board.getc(x + 1, y + 1)); } dfs_idc(clist[0], n); } } } // check not assigned cells for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; if (!isDot(cell) && adjlist(cell.adjborder).filter(b => !b.isnull && !b.qans).length === 1) { add_link(adjlist(cell.adjborder).filter(b => !b.isnull && !b.qans)[0]); } if (!isEmpty(cell) || id.has(cell)) { continue; } if (id_choice.has(cell) && id_choice.get(cell).length === 1) { bfs_id([cell], id_choice.get(cell)[0]); } } document.querySelector('#btncolor').click(); } function NorinoriAssist() { for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; let list = adjlist(cell.adjacent); // surrounded by dot if (!list.some(c => !c.isnull && c.qsub !== CQSUB.dot)) { add_dot(cell); } // extend domino if (cell.qans === CQANS.black && list.filter(c => !c.isnull && c.qsub !== CQSUB.dot).length === 1) { let ncell = list.find(c => !c.isnull && c.qsub !== CQSUB.dot); add_black(ncell); } // finished domino if (cell.qans === CQANS.black && list.some(c => !c.isnull && c.qans === CQANS.black)) { let ncell = list.find(c => !c.isnull && c.qans === CQANS.black); fourside(add_dot, cell.adjacent); fourside(add_dot, ncell.adjacent); } // not making triomino if (list.filter(c => !c.isnull && c.qans === CQANS.black).length >= 2) { add_dot(cell); } // · · // ·█ -> ·█ // · for (let d = 0; d < 4; d++) { if (cell.qans === CQANS.black && (offset(cell, -1, 0, d).isnull || offset(cell, -1, 0, d).qsub === CQSUB.green) && (offset(cell, 0, -1, d).isnull || offset(cell, 0, -1, d).qsub === CQSUB.green)) { add_dot(offset(cell, 1, 1, d)); } } } for (let i = 0; i < board.roommgr.components.length; i++) { let room = board.roommgr.components[i]; let list = []; for (let j = 0; j < room.clist.length; j++) { list.push(room.clist[j]); } // finish region if (list.filter(c => c.qans === CQANS.black).length === 2) { list.forEach(c => add_dot(c)); } if (list.filter(c => c.qsub !== CQSUB.dot).length === 2) { list.forEach(c => add_black(c)); } if (list.filter(c => c.qans === CQANS.black).length === 1) { list.forEach(c => { if (c.qans === CQANS.black || c.qsub === CQSUB.dot) { return; } if (!adjlist(c.adjacent).some(nc => !nc.isnull && (c.room !== nc.room && nc.qsub !== CQSUB.dot || c.room === nc.room && nc.qans === CQANS.black))) { add_dot(c); } }); } } } function AllorNothingAssist() { let add_color = function (c, color) { if (c.isnull || c.qsub !== CQSUB.none) { return; } if (step && flg) { return; } flg = 1; c.setQsub(color); c.draw(); }; let add_gray = function (c) { add_color(c, CQSUB.gray); }; let add_yellow = function (c) { add_color(c, CQSUB.yellow); }; let add_border_cross = function (b) { if (b.ques) { add_cross(b); } }; SingleLoopInCell({ isPassable: c => c.qsub !== CQSUB.gray, isPass: c => c.qsub === CQSUB.yellow, add_notpass: add_gray, add_pass: add_yellow, }); for (let i = 0; i < board.roommgr.components.length; i++) { let room = board.roommgr.components[i]; let list = []; for (let j = 0; j < room.clist.length; j++) { list.push(room.clist[j]); } let nbcnt = function (c) { let templist = adjlist(c.adjacent); return templist.filter(nc => !nc.isnull && c.room === nc.room).length; }; let list2 = list.filter(c => nbcnt(c) !== 1); let listodd = list.filter(c => (c.bx + c.by) % 4 === 2); let listeven = list.filter(c => (c.bx + c.by) % 4 === 0); if (list.length - list2.length === 2) { list2.forEach(c => fourside(add_border_cross, c.adjborder)); } if (listeven.length === listodd.length + 1) { listodd.forEach(c => fourside(add_border_cross, c.adjborder)); } if (listodd.length === listeven.length + 1) { listeven.forEach(c => fourside(add_border_cross, c.adjborder)); } if (list.some(c => c.lcnt > 0 || c.qsub === CQSUB.yellow)) { list.forEach(c => add_yellow(c)); let templist = list.filter(c => nbcnt(c) === 1); templist.forEach(c => fourside((nb, nc) => { if (!nc.isnull && room === nc.room) { add_line(nb); } }, c.adjborder, c.adjacent)); } if (list.some(c => c.qsub === CQSUB.gray) || list.length - list2.length > 2 || Math.abs(listodd.length - listeven.length) > 1) { list.forEach(c => add_gray(c)); list.forEach(c => fourside(add_cross, c.adjborder)); list.forEach(c => fourside(add_yellow, c.adjacent)); } } } function AqreAssist() { BlackConnected(); for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; let fn = function (c1, c2, c3) { if (c1.isnull || c2.isnull || c3.isnull) { return; } if (c1.qans === CQANS.black && c2.qans === CQANS.black && c3.qans === CQANS.black) { add_green(cell); } if (c1.qsub === CQSUB.green && c2.qsub === CQSUB.green && c3.qsub === CQSUB.green) { add_black(cell); } }; if (cell.qsub === CQSUB.none && cell.qans === CQANS.none) { fn(offset(cell, -3, 0), offset(cell, -2, 0), offset(cell, -1, 0),); fn(offset(cell, -2, 0), offset(cell, -1, 0), offset(cell, 1, 0),); fn(offset(cell, -1, 0), offset(cell, 1, 0), offset(cell, 2, 0),); fn(offset(cell, 1, 0), offset(cell, 2, 0), offset(cell, 3, 0),); fn(offset(cell, 0, -3), offset(cell, 0, -2), offset(cell, 0, -1),); fn(offset(cell, 0, -2), offset(cell, 0, -1), offset(cell, 0, 1),); fn(offset(cell, 0, -1), offset(cell, 0, 1), offset(cell, 0, 2),); fn(offset(cell, 0, 1), offset(cell, 0, 2), offset(cell, 0, 3),); } } for (let i = 0; i < board.roommgr.components.length; i++) { let room = board.roommgr.components[i]; let qnum = room.top.qnum; if (qnum === CQNUM.none || qnum === CQNUM.quesmark) { continue; } let list = []; for (let j = 0; j < room.clist.length; j++) { list.push(room.clist[j]); } if (list.filter(c => c.qans === CQANS.black).length === qnum) { list.forEach(c => add_green(c)); } if (qnum - list.filter(c => c.qans === CQANS.black).length === list.filter(c => c.qans === CQANS.none && c.qsub === CQSUB.none).length) { list.forEach(c => add_black(c)); } } } function KurodokoAssist() { GreenConnected(); BlackNotAdjacent(); SightNumber({ isShaded: c => c.qsub === CQSUB.green, isUnshaded: c => c.qans === CQANS.black, add_shaded: add_green, add_unshaded: add_black, }); for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; if (cell.qnum !== CQNUM.none) { add_green(cell); } } } function HitoriAssist() { GreenConnected(); BlackNotAdjacent(); let uniq = new Map(); for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; uniq.set(cell, true); } let fn = function (a) { let vis = new Map(); for (let cell of a) { if (cell.qnum === CQNUM.none) continue; if (cell.qsub === CQSUB.green) vis.set(cell.qnum, cell); } for (let cell of a) { if (cell.qnum === CQNUM.none) continue; if (vis.has(cell.qnum)) add_black(cell); } let cnt = new Map(); for (let cell of a) { if (cell.qnum === CQNUM.none || cell.qans === CQANS.black) continue; let c = cnt.has(cell.qnum) ? cnt.get(cell.qnum) : 0; c++; cnt.set(cell.qnum, c); } for (let cell of a) { if (cell.qnum === CQNUM.none || cell.qans === CQANS.black) continue; if (cnt.get(cell.qnum) >= 2) uniq.set(cell, false); } // aba for (let i = 0; i < a.length - 2; i++) { if (a[i].qnum === CQNUM.none || a[i].qnum !== a[i + 2].qnum) continue; add_green(a[i + 1]); } // a..aa for (let i = 0; i < a.length - 1; i++) { if (a[i].qnum === CQNUM.none || a[i].qnum !== a[i + 1].qnum) continue; for (let j = 0; j < a.length; j++) { if (j !== i && j !== i + 1 && a[j].qnum === a[i].qnum) add_black(a[j]); } } }; for (let i = 0; i < board.rows; i++) { let a = []; for (let j = 0; j < board.cols; j++) { a.push(board.getc(2 * j + 1, 2 * i + 1)); } fn(a); } for (let j = 0; j < board.cols; j++) { let a = []; for (let i = 0; i < board.rows; i++) { a.push(board.getc(2 * j + 1, 2 * i + 1)); } fn(a); } for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; if (uniq.get(cell)) add_green(cell); } } function CaveAssist() { GreenConnected(); CellConnected({ isShaded: c => c.qans === CQANS.black, isUnshaded: c => c.qsub === CQSUB.green, add_shaded: add_black, add_unshaded: add_green, OutsideAsShaded: true, }); SightNumber({ isShaded: c => c.qsub === CQSUB.green, isUnshaded: c => c.qans === CQANS.black, add_shaded: add_green, add_unshaded: add_black, }); NoCheckerCell({ isShaded: c => c.qans === CQANS.black, isUnshaded: c => c.qans === CQANS.green, add_shaded: add_black, add_unshaded: add_green, }); for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; if (cell.qnum !== CQNUM.none) { add_green(cell); } } } function TapaAssist() { No2x2Cell({ isShaded: c => c.qans === CQANS.black, add_unshaded: add_dot, }); CellConnected({ isShaded: c => c.qans === CQANS.black, isUnshaded: c => c.qsub === CQSUB.dot || c.qnums.length > 0, add_shaded: add_black, add_unshaded: add_dot, }); let check = function (qnums, s) { if (s === "11111111") { return qnums.length === 1 && qnums[0] === 8 || qnums[0] === CQNUM.quesmark; } while (s[0] !== '0') { s = s.slice(1) + s[0]; } s = s.split('0').filter(s => s.length > 0).map(s => s.length); if (s.length === 0) { s = [0]; } if (s.length !== qnums.length) { return false; } for (let i = 0; i < qnums.length; i++) { if (!s.includes(qnums[i])) { continue; } s.splice(s.indexOf(qnums[i]), 1); } return s.length === qnums.filter(n => n === CQNUM.quesmark).length; }; let isEmpty = function (c) { return !c.isnull && c.qans === CQANS.none && c.qsub === CQSUB.none && c.qnums.length === 0; }; for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; if (cell.qnums.length === 0) { continue; } let list = [offset(cell, -1, -1), offset(cell, 0, -1), offset(cell, 1, -1), offset(cell, 1, 0), offset(cell, 1, 1), offset(cell, 0, 1), offset(cell, -1, 1), offset(cell, -1, 0)]; let mask = parseInt(list.map(c => isEmpty(c) ? "1" : "0").join(""), 2); let blk = parseInt(list.map(c => (!c.isnull && c.qans === CQANS.black ? "1" : "0")).join(""), 2); let setb = 0b11111111, setd = 0b00000000, n = 0; for (let j = mask; j >= 0; j--) { j &= mask; if (check(cell.qnums, (j | blk).toString(2).padStart(8, '0'))) { setb &= (j | blk); setd |= (j | blk); n++; } } if (n === 0) { add_black(cell); continue; } setb = setb.toString(2).padStart(8, '0'); setd = setd.toString(2).padStart(8, '0'); for (let j = 0; j < 8; j++) { if (setb[j] === '1') { add_black(list[j], true); } if (setd[j] === '0') { add_dot(list[j]); } } } } function LightandShadowAssist() { let add_black = function (c) { if (c.isnull || c.qans !== CQANS.none) { return; } if (step && flg) { return; } flg = 1; c.setQans(CQANS.black); c.draw(); }; let add_white = function (c) { if (c.isnull || c.qans !== CQANS.none) { return; } if (step && flg) { return; } flg = 1; c.setQans(CQANS.white); c.draw(); }; SizeRegion_Cell({ isShaded: c => c.qans === CQANS.black, isUnshaded: c => c.qans === CQANS.white, add_shaded: add_black, add_unshaded: add_white, NoUnshadedNum: false, }); SizeRegion_Cell({ isShaded: c => c.qans === CQANS.white, isUnshaded: c => c.qans === CQANS.black, add_shaded: add_white, add_unshaded: add_black, NoUnshadedNum: false, }); for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; if (cell.qnum !== CQNUM.none && cell.ques === 1) { add_black(cell); } if (cell.qnum !== CQNUM.none && cell.ques === 0) { add_white(cell); } } } function SlalomAssist() { SingleLoopInCell({ isPassable: c => c.ques !== 1, isPass: c => c.bx === board.startpos.bx && c.by === board.startpos.by, }); for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; if (cell.ques === 1) { fourside(add_cross, cell.adjborder); } if (cell.ques === CQUES.vgate && (cell.adjacent.top.isnull || cell.adjacent.top.ques !== CQUES.vgate)) { let list = []; let pcell = cell; while (!pcell.isnull && pcell.ques === CQUES.vgate) { add_cross(pcell.adjborder.top); add_cross(pcell.adjborder.bottom); list.push(pcell); pcell = pcell.adjacent.bottom; } if (list.filter(c => c.adjborder.left.line).length === 1) { list.forEach(c => add_cross(c.adjborder.left)); } if (list.filter(c => c.adjborder.left.qsub !== BQSUB.cross).length === 1) { list.forEach(c => add_line(c.adjborder.left)); } } if (cell.ques === CQUES.hgate && (cell.adjacent.left.isnull || cell.adjacent.left.ques !== CQUES.hgate)) { let list = []; let pcell = cell; while (!pcell.isnull && pcell.ques === CQUES.hgate) { add_cross(pcell.adjborder.left); add_cross(pcell.adjborder.right); list.push(pcell); pcell = pcell.adjacent.right; } if (list.filter(c => c.adjborder.top.line).length === 1) { list.forEach(c => add_cross(c.adjborder.top)); } if (list.filter(c => c.adjborder.top.qsub !== BQSUB.cross).length === 1) { list.forEach(c => add_line(c.adjborder.top)); } } } } function StarbattleAssist() { let add_cir = function (b) { if (b === undefined || b.isnull || b.line || b.qsub !== BQSUB.none) { return; } if (step && flg) { return; } b.setQsub(1); b.draw(); flg |= b.qsub === BQSUB.cross; }; let starcount = board.starCount.count; let add_star = add_black; for (let i = 0; i < board.roommgr.components.length; i++) { let room = board.roommgr.components[i]; let cellList = []; for (let j = 0; j < room.clist.length; j++) { cellList.push(room.clist[j]); } // finish room if (cellList.filter(c => c.qans === CQANS.star).length === starcount) { cellList.forEach(c => add_dot(c)); } if (cellList.filter(c => c.qsub !== CQSUB.dot).length === starcount) { cellList.forEach(c => add_star(c)); } } for (let i = 0; i < board.rows; i++) { let hcellList = []; let vcellList = []; for (let j = 0; j < board.cols; j++) { hcellList.push(board.getc(2 * i + 1, 2 * j + 1)); vcellList.push(board.getc(2 * j + 1, 2 * i + 1)); } // finish row/col if (hcellList.filter(c => c.qans === CQANS.star).length === starcount) { hcellList.forEach(c => add_dot(c)); } if (hcellList.filter(c => c.qsub !== CQSUB.dot).length === starcount) { hcellList.forEach(c => add_star(c)); } if (vcellList.filter(c => c.qans === CQANS.star).length === starcount) { vcellList.forEach(c => add_dot(c)); } if (vcellList.filter(c => c.qsub !== CQSUB.dot).length === starcount) { vcellList.forEach(c => add_star(c)); } } for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; if (cell.qans === CQANS.star) { for (let dx = -1; dx <= 1; dx++) { for (let dy = -1; dy <= 1; dy++) { add_dot(offset(cell, dx, dy)); } } } } for (let i = 0; i < board.cross.length; i++) { let cross = board.cross[i]; if (cross.qsub !== 1) { continue; } for (let d = 0; d < 4; d++) { if (offset(cross, .5, .5, d).qsub === CQSUB.dot && offset(cross, -.5, .5, d).qsub === CQSUB.dot) { add_cir(offset(cross, 0, -.5, d)); cross.setQsub(0); cross.draw(); } } } for (let i = 0; i < board.border.length; i++) { let border = board.border[i]; if (border.qsub !== 1) { continue; } if (border.isvert) { add_dot(offset(border, -.5, -1)); add_dot(offset(border, +.5, -1)); add_dot(offset(border, -.5, +1)); add_dot(offset(border, +.5, +1)); } else { add_dot(offset(border, -1, -.5)); add_dot(offset(border, -1, +.5)); add_dot(offset(border, +1, -.5)); add_dot(offset(border, +1, +.5)); } for (let j = 0; j <= 1; j++) { if (border.sidecell[j].qsub === CQSUB.dot) { add_star(border.sidecell[1 - j]); } } } } function CastleWallAssist() { SingleLoopInCell({ isPassable: c => c.qnum === CQNUM.none, }); // add invisible qsub at cross const INLOOP = 1, OUTLOOP = 2; let add_inout = function (cr, qsub) { if (cr.isnull || cr.qsub !== 0) { return; } cr.setQsub(qsub); } for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; if (cell.qnum !== CQNUM.none) { // add qsub around b/w clue if (cell.ques === CQUES.black || cell.ques === CQUES.white) { for (let d = 0; d < 4; d++) { add_inout(offset(cell, .5, .5, d), (cell.ques === CQUES.black ? OUTLOOP : INLOOP)); } } // finish clue if (cell.qnum !== CQNUM.quesmark) { let d = qdirremap(cell.qdir); let borderlist = []; let pcell = dir(cell.adjacent, d); let qnum = cell.qnum; while (!pcell.isnull && (pcell.qnum < 0 || pcell.qdir !== cell.qdir)) { let b = dir(pcell.adjborder, d); if (!b.isnull && b.sidecell[0].qnum === CQNUM.none && b.sidecell[1].qnum === CQNUM.none) { borderlist.push(b); } pcell = dir(pcell.adjacent, d); } if (!pcell.isnull) { qnum -= pcell.qnum; } if (borderlist.filter(b => b.line).length === qnum) { borderlist.forEach(b => add_cross(b)); } if (borderlist.filter(b => b.qsub === BQSUB.none).length === qnum) { borderlist.forEach(b => add_line(b)); } } } } for (let i = 0; i < board.cross.length; i++) { let cross = board.cross[i]; // outloop at side if (cross.bx === board.minbx || cross.bx === board.maxbx || cross.by === board.minby || cross.by === board.maxby) { add_inout(cross, OUTLOOP); } // no checker if (cross.qsub === 0) { let fn = function (cr, cr1, cr2, cr12) { if (cr1.isnull || cr2.isnull || cr12.isnull) { return; } if (cr1.qsub === 0 || cr2.qsub === 0 || cr12.qsub === 0) { return; } if (cr1.qsub === cr2.qsub && cr1.qsub !== cr12.qsub) { add_inout(cr, cr1.qsub); } }; for (let d = 0; d < 4; d++) { fn(cross, offset(cross, 1, 0, d), offset(cross, 0, 1, d), offset(cross, 1, 1, d)); } } // add line between different i/o if (cross.qsub !== 0) { let fn = function (cr1, cr2, b) { if (cr1.isnull || cr2.isnull) { return; } if (cr1.qsub === 0 || cr2.qsub === 0) { return; } if (cr1.qsub === cr2.qsub) { add_cross(b); } if (cr1.qsub !== cr2.qsub) { add_line(b); } }; for (let d = 0; d < 4; d++) { fn(cross, offset(cross, 1, 0, d), offset(cross, .5, 0, d)); } } // extend i/o through cross/line if (cross.qsub !== 0) { let fn = function (cr1, cr2, b) { if (cr2.isnull) { return; } if (b.isnull || b.qsub === BQSUB.cross || b.sidecell[0].qnum !== CQNUM.none || b.sidecell[1].qnum !== CQNUM.none) { add_inout(cr2, cr1.qsub); } if (!b.isnull && b.line) { add_inout(cr2, INLOOP + OUTLOOP - cr1.qsub); } }; for (let d = 0; d < 4; d++) { fn(cross, offset(cross, 1, 0, d), offset(cross, .5, 0, d)); } } } } function NurimisakiAssist() { let isEmpty = c => !c.isnull && c.qnum === CQNUM.none && c.qsub === CQSUB.none && c.qans === CQANS.none; let isDot = c => !c.isnull && c.qsub === CQSUB.dot && c.qnum === CQNUM.none; let isDotEmpty = c => isEmpty(c) || isDot(c); let isBlack = c => !c.isnull && c.qans === CQANS.black; let isBorderBlack = c => c.isnull || c.qans === CQANS.black; let isConnectBlack = c => isBorderBlack(c) || c.qnum !== CQNUM.none; let isCircle = c => !c.isnull && c.qnum !== CQNUM.none; let isDotCircle = c => isDot(c) || isCircle(c); for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; let blackcnt = 0; let dotcnt = 0; fourside(c => { if (isBorderBlack(c)) { blackcnt++; } if (isDot(c)) { dotcnt++; } }, cell.adjacent); // no clue pattern // add dot if (isEmpty(cell)) { for (let d = 0; d < 4; d++) { // cannot place black with 2x2 black rule if (isEmpty(offset(cell, 0, -1, d)) && isBlack(offset(cell, 1, 0, d)) && isBlack(offset(cell, 1, -1, d)) && (isBorderBlack(offset(cell, 0, -2, d)) || isBorderBlack(offset(cell, -1, -1, d)))) { add_dot(cell); } else if (isEmpty(offset(cell, 0, -1, d)) && isBlack(offset(cell, -1, 0, d)) && isBlack(offset(cell, -1, -1, d)) && (isBorderBlack(offset(cell, 0, -2, d)) || isBorderBlack(offset(cell, 1, -1, d)))) { add_dot(cell); } else if (isEmpty(offset(cell, 1, 0, d)) && isEmpty(offset(cell, 0, -1, d)) && isBlack(offset(cell, 1, -1, d)) && (isBorderBlack(offset(cell, 2, 0, d)) || isBorderBlack(offset(cell, 1, 1, d))) && (isBorderBlack(offset(cell, 0, -2, d)) || isBorderBlack(offset(cell, -1, -1, d)))) { add_dot(cell); } else if (isEmpty(offset(cell, 0, -1, d)) && isEmpty(offset(cell, 1, -1, d)) && isBlack(offset(cell, 1, 0, d)) && isBorderBlack(offset(cell, -1, -1, d)) && isBorderBlack(offset(cell, 0, -2, d)) && offset(cell, 1, -2, d).isnull) { add_dot(cell); } else if (isEmpty(offset(cell, 0, -1, d)) && isEmpty(offset(cell, -1, -1, d)) && isBlack(offset(cell, -1, 0, d)) && isBorderBlack(offset(cell, 1, -1, d)) && isBorderBlack(offset(cell, 0, -2, d)) && offset(cell, -1, -2, d).isnull) { add_dot(cell); } // cannot place black with 2x2 dot rule else if (isBlack(offset(cell, 1, 0, d)) && isBlack(offset(cell, 1, 1, d)) && isDot(offset(cell, -1, 2, d)) && isEmpty(offset(cell, 0, 1, d)) && isDotEmpty(offset(cell, -1, 1, d)) && isDotEmpty(offset(cell, 0, 2, d))) { add_dot(cell); } else if (isBlack(offset(cell, -1, 0, d)) && isBlack(offset(cell, -1, 1, d)) && isDot(offset(cell, 1, 2, d)) && isEmpty(offset(cell, 0, 1, d)) && isDotEmpty(offset(cell, 1, 1, d)) && isDotEmpty(offset(cell, 0, 2, d))) { add_dot(cell); } // cannot place black with 2x2 black rule and 2x2 dot rule else if (isDotEmpty(offset(cell, 0, -1, d)) && isEmpty(offset(cell, 0, -2, d)) && isBlack(offset(cell, 1, -1, d)) && isBlack(offset(cell, 1, -2, d)) && isBorderBlack(offset(cell, 0, -3, d))) { add_dot(cell); } else if (isDotEmpty(offset(cell, 0, -1, d)) && isEmpty(offset(cell, 0, -2, d)) && isBlack(offset(cell, -1, -1, d)) && isBlack(offset(cell, -1, -2, d)) && isBorderBlack(offset(cell, 0, -3, d))) { add_dot(cell); } else if (isDotEmpty(offset(cell, -1, 0, d)) && isEmpty(offset(cell, -1, -1, d)) && isBlack(offset(cell, 0, -1, d)) && isBorderBlack(offset(cell, -1, 1, d)) && isBorderBlack(offset(cell, -1, -2, d))) { add_dot(cell); } else if (isDotEmpty(offset(cell, 1, 0, d)) && isEmpty(offset(cell, 1, -1, d)) && isBlack(offset(cell, 0, -1, d)) && isBorderBlack(offset(cell, 1, 1, d)) && isBorderBlack(offset(cell, 1, -2, d))) { add_dot(cell); } // cannot place black with 2x3 border pattern else if (isEmpty(offset(cell, 0, -1, d)) && isEmpty(offset(cell, 1, -1, d)) && isEmpty(offset(cell, 2, -1, d)) && isDotEmpty(offset(cell, 1, 0, d)) && isDotEmpty(offset(cell, 2, 0, d)) && isBorderBlack(offset(cell, -1, -1, d)) && isBorderBlack(offset(cell, 3, -1, d)) && isBorderBlack(offset(cell, 3, 0, d)) && offset(cell, 0, -2, d).isnull) { add_dot(cell); } else if (isEmpty(offset(cell, 0, -1, d)) && isEmpty(offset(cell, -1, -1, d)) && isEmpty(offset(cell, -2, -1, d)) && isDotEmpty(offset(cell, -1, 0, d)) && isDotEmpty(offset(cell, -2, 0, d)) && isBorderBlack(offset(cell, 1, -1, d)) && isBorderBlack(offset(cell, -3, -1, d)) && isBorderBlack(offset(cell, -3, 0, d)) && offset(cell, 0, -2, d).isnull) { add_dot(cell); } } } if (isDot(cell)) { // dot cannot be deadend if (blackcnt === 2) { fourside(add_dot, cell.adjacent); } for (let d = 0; d < 4; d++) { // avoid 2x2 dot if (isBorderBlack(offset(cell, 0, -1, d)) && isEmpty(offset(cell, 1, 0, d)) && isEmpty(offset(cell, 0, 1, d)) && isEmpty(offset(cell, -1, 0, d)) && isDot(offset(cell, 1, 1, d))) { add_dot(offset(cell, -1, 0, d)); } else if (isBorderBlack(offset(cell, 0, -1, d)) && isEmpty(offset(cell, -1, 0, d)) && isEmpty(offset(cell, 0, 1, d)) && isEmpty(offset(cell, 1, 0, d)) && isDot(offset(cell, -1, 1, d))) { add_dot(offset(cell, 1, 0, d)); } // dot cannot be deadend with 2x2 dot rule else if (isBorderBlack(offset(cell, 0, -1, d)) && isEmpty(offset(cell, 1, 0, d)) && isBorderBlack(offset(cell, 2, 0, d)) && isBorderBlack(offset(cell, 1, -1, d)) && isEmpty(offset(cell, -1, 0, d))) { add_dot(offset(cell, -1, 0, d)); } else if (isBorderBlack(offset(cell, 0, -1, d)) && isEmpty(offset(cell, -1, 0, d)) && isBorderBlack(offset(cell, -2, 0, d)) && isBorderBlack(offset(cell, -1, -1, d)) && isEmpty(offset(cell, 1, 0, d))) { add_dot(offset(cell, 1, 0, d)); } } } // add black if (isEmpty(cell)) { // black deadend if (blackcnt >= 3) { add_black(cell); } for (let d = 0; d < 4; d++) { // cannot dot with 2x2 dot rule if (isBorderBlack(offset(cell, -1, 0, d)) && isBorderBlack(offset(cell, 2, 0, d)) && isEmpty(offset(cell, 1, 0, d)) && offset(cell, 0, -1, d).isnull && offset(cell, 1, -1, d).isnull) { add_black(cell); } else if (isBorderBlack(offset(cell, 1, 0, d)) && isBorderBlack(offset(cell, 0, -1, d)) && isDot(offset(cell, -1, 1, d)) && isDotEmpty(offset(cell, -1, 0, d)) && isDotEmpty(offset(cell, 0, 1, d))) { add_black(cell); } } } // clue pattern // any circle clue if (cell.qnum !== CQNUM.none) { // clue deadend check if (blackcnt === 3) { fourside(add_dot, cell.adjacent); } else if (dotcnt === 1) { fourside(add_black, cell.adjacent); } for (let d = 0; d < 4; d++) { // avoid 2x2 black pattern if (isEmpty(offset(cell, 0, -1, d)) && isEmpty(offset(cell, 1, 0, d)) && isEmpty(offset(cell, 1, -1, d)) && isBlack(offset(cell, 0, -2, d)) && isBlack(offset(cell, 1, -2, d))) { add_black(offset(cell, 0, 1, d)); add_black(offset(cell, -1, 0, d)); } else if (isEmpty(offset(cell, 0, -1, d)) && isEmpty(offset(cell, 1, 0, d)) && isEmpty(offset(cell, 1, -1, d)) && isBlack(offset(cell, 2, 0, d)) && isBlack(offset(cell, 2, -1, d))) { add_black(offset(cell, 0, 1, d)); add_black(offset(cell, -1, 0, d)); } else if (isEmpty(offset(cell, 0, -1, d)) && isEmpty(offset(cell, 1, 0, d)) && isEmpty(offset(cell, 1, -1, d)) && isEmpty(offset(cell, 0, -2, d)) && isBlack(offset(cell, 1, -2, d)) && (isBorderBlack(offset(cell, 0, -3, d)) || isBorderBlack(offset(cell, -1, -2, d)))) { add_black(offset(cell, 0, 1, d)); add_black(offset(cell, -1, 0, d)); } else if (isEmpty(offset(cell, 0, -1, d)) && isEmpty(offset(cell, 1, 0, d)) && isEmpty(offset(cell, 1, -1, d)) && isEmpty(offset(cell, 2, 0, d)) && isBlack(offset(cell, 2, -1, d)) && (isBorderBlack(offset(cell, 3, 0, d)) || isBorderBlack(offset(cell, 2, 1, d)))) { add_black(offset(cell, 0, 1, d)); add_black(offset(cell, -1, 0, d)); } // avoid 2x2 black and 2x2 dot apttern else if (isEmpty(offset(cell, 0, -1, d)) && isEmpty(offset(cell, 1, 0, d)) && isDotEmpty(offset(cell, 1, -1, d)) && isEmpty(offset(cell, 1, -2, d)) && isBlack(offset(cell, 0, -2, d)) && isBorderBlack(offset(cell, 1, -3, d))) { add_black(offset(cell, 0, 1, d)); add_black(offset(cell, -1, 0, d)); } else if (isEmpty(offset(cell, 0, -1, d)) && isEmpty(offset(cell, 1, 0, d)) && isDotEmpty(offset(cell, 1, -1, d)) && isEmpty(offset(cell, 2, -1, d)) && isBlack(offset(cell, 2, 0, d)) && isBorderBlack(offset(cell, 3, -1, d))) { add_black(offset(cell, 0, 1, d)); add_black(offset(cell, -1, 0, d)); } // avoid border 2x2 black else if (isEmpty(offset(cell, 0, -1, d)) && isEmpty(offset(cell, 1, 0, d)) && isEmpty(offset(cell, 1, -1, d)) && isDotEmpty(offset(cell, 2, 0, d)) && isEmpty(offset(cell, 2, -1, d)) && isBorderBlack(offset(cell, 3, 0, d)) && isBorderBlack(offset(cell, 3, -1, d)) && offset(cell, 0, -2, d).isnull) { add_black(offset(cell, 0, 1, d)); add_black(offset(cell, -1, 0, d)); } else if (isEmpty(offset(cell, 0, -1, d)) && isEmpty(offset(cell, 1, 0, d)) && isEmpty(offset(cell, 1, -1, d)) && isDotEmpty(offset(cell, 0, -2, d)) && isEmpty(offset(cell, 1, -2, d)) && isBorderBlack(offset(cell, 0, -3, d)) && isBorderBlack(offset(cell, 1, -3, d)) && offset(cell, 2, 0, d).isnull) { add_black(offset(cell, 0, 1, d)); add_black(offset(cell, -1, 0, d)); } // avoid border 2x3 pattern else if (isEmpty(offset(cell, 0, -1, d)) && isEmpty(offset(cell, 1, 0, d)) && isEmpty(offset(cell, 1, -1, d)) && isDotEmpty(offset(cell, 2, 0, d)) && isDotEmpty(offset(cell, 2, -1, d)) && isDotEmpty(offset(cell, 3, 0, d)) && isEmpty(offset(cell, 3, -1, d)) && isBorderBlack(offset(cell, 4, 0, d)) && isBorderBlack(offset(cell, 4, -1, d)) && offset(cell, 0, -2, d).isnull) { add_black(offset(cell, 0, 1, d)); add_black(offset(cell, -1, 0, d)); } else if (isEmpty(offset(cell, 0, -1, d)) && isEmpty(offset(cell, 1, 0, d)) && isEmpty(offset(cell, 1, -1, d)) && isDotEmpty(offset(cell, 0, -2, d)) && isDotEmpty(offset(cell, 1, -2, d)) && isDotEmpty(offset(cell, 0, -3, d)) && isEmpty(offset(cell, 1, -3, d)) && isBorderBlack(offset(cell, 0, -4, d)) && isBorderBlack(offset(cell, 1, -4, d)) && offset(cell, 2, 0, d).isnull) { add_black(offset(cell, 0, 1, d)); add_black(offset(cell, -1, 0, d)); } } } if (isEmpty(cell)) { for (let d = 0; d < 4; d++) { // cannot place black with 2x2 white if (isBlack(offset(cell, 1, 0, d)) && isBlack(offset(cell, 1, 1, d)) && isCircle(offset(cell, -1, 2, d)) && isEmpty(offset(cell, 0, 1, d)) && isEmpty(offset(cell, -1, 1, d)) && isEmpty(offset(cell, 0, 2, d))) { add_dot(cell); } else if (isBlack(offset(cell, -1, 0, d)) && isBlack(offset(cell, -1, 1, d)) && isCircle(offset(cell, 1, 2, d)) && isEmpty(offset(cell, 0, 1, d)) && isEmpty(offset(cell, 1, 1, d)) && isEmpty(offset(cell, 0, 2, d))) { add_dot(cell); } // cannot place dot with 2x2 white else if (isBorderBlack(offset(cell, 1, 0, d)) && isBorderBlack(offset(cell, 0, -1, d)) && isCircle(offset(cell, -1, 1, d)) && isEmpty(offset(cell, -1, 0, d)) && isEmpty(offset(cell, 0, 1, d))) { add_black(cell); } } } if (isDot(cell)) { for (let d = 0; d < 4; d++) { // avoid 2x2 white if (isBorderBlack(offset(cell, 0, -1, d)) && isEmpty(offset(cell, 1, 0, d)) && isEmpty(offset(cell, 0, 1, d)) && isDotEmpty(offset(cell, -1, 0, d)) && isCircle(offset(cell, 1, 1, d))) { add_dot(offset(cell, -1, 0, d)); add_black(offset(cell, 2, 1, d)); add_black(offset(cell, 1, 2, d)); } else if (isBorderBlack(offset(cell, 0, -1, d)) && isEmpty(offset(cell, -1, 0, d)) && isEmpty(offset(cell, 0, 1, d)) && isDotEmpty(offset(cell, 1, 0, d)) && isCircle(offset(cell, -1, 1, d))) { add_dot(offset(cell, 1, 0, d)); add_black(offset(cell, -2, 1, d)); add_black(offset(cell, -1, 2, d)); } } } // circle clue with number if (cell.qnum >= 2) { for (let d = 0; d < 4; d++) { if (isEmpty(offset(cell, 0, -1, d))) { // avoid eyesight too long if (isDotCircle(offset(cell, 0, -cell.qnum, d))) { add_black(offset(cell, 0, -1, d)); } // situation for clue at the end else if (isCircle(offset(cell, 0, -cell.qnum + 1, d)) && offset(cell, 0, -cell.qnum + 1, d).qnum !== CQNUM.circle && offset(cell, 0, -cell.qnum + 1, d).qnum !== cell.qnum) { add_black(offset(cell, 0, -1, d)); } } if (isEmpty(offset(cell, 0, -1, d))) { for (let j = 2; j < cell.qnum; j++) { // eyesight not enough long if (j !== cell.qnum - 1 && isConnectBlack(offset(cell, 0, -j, d))) { add_black(offset(cell, 0, -1, d)); break; } if (isBorderBlack(offset(cell, 0, -j, d))) { add_black(offset(cell, 0, -1, d)); break; } // avoid 2x2 dot if (isDot(offset(cell, 1, -j + 1, d)) && isDot(offset(cell, 1, -j, d))) { add_black(offset(cell, 0, -1, d)); break; } if (isDot(offset(cell, -1, -j + 1, d)) && isDot(offset(cell, -1, -j, d))) { add_black(offset(cell, 0, -1, d)); break; } } } // extend eyesight if (isDot(offset(cell, 0, -1, d))) { for (let j = 2; j < cell.qnum; j++) { add_dot(offset(cell, 0, -j, d)); } add_black(offset(cell, 0, -cell.qnum, d)); } } } } // 2x2 rules No2x2Cell({ isShaded: c => c.qans === CQANS.black, add_unshaded: add_dot, }); No2x2Cell({ isShaded: c => c.qsub === CQSUB.dot, add_unshaded: add_black, }); CellConnected({ isShaded: isDotCircle, isUnshaded: isBlack, add_shaded: add_dot, add_unshaded: add_black, }); CellConnected({ isShaded: isDot, isUnshaded: isConnectBlack, add_shaded: add_dot, add_unshaded: add_black, }); } function ChocoBananaAssist() { SizeRegion_Cell({ isShaded: c => c.qans === CQANS.black, isUnshaded: c => c.qsub === CQSUB.green, add_shaded: add_black, add_unshaded: add_green, OneNumPerRegion: false, NoUnshadedNum: false, }); SizeRegion_Cell({ isShaded: c => c.qsub === CQSUB.green, isUnshaded: c => c.qans === CQANS.black, add_shaded: add_green, add_unshaded: add_black, OneNumPerRegion: false, NoUnshadedNum: false, }); RectRegion_Cell({ isShaded: c => c.qans === CQANS.black, isUnshaded: c => c.qsub === CQSUB.green, add_shaded: add_black, add_unshaded: add_green, }); for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; if (cell.qnum === 1 || cell.qnum === 2) { add_black(cell); } // non-rect if (cell.qsub === CQSUB.green) { let templist = [offset(cell, 1, 0), offset(cell, 0, 1), offset(cell, -1, 0), offset(cell, 0, -1)]; templist = templist.filter(c => !c.isnull && c.qans !== CQANS.black); if (templist.length === 1) { let ncell = templist[0]; add_green(ncell); let templist2 = [offset(ncell, 1, 0), offset(ncell, 0, 1), offset(ncell, -1, 0), offset(ncell, 0, -1)]; templist2 = templist2.filter(c => !c.isnull && c.qans !== CQANS.black && c !== cell); if (templist2.length === 1) { add_green(templist2[0]); } } } } } function SlantAssist() { let add_slash = function (c, qans) { if (c === undefined || c.isnull || c.qans !== CQANS.none) { return; } if (step && flg) { return; } flg = true; c.setQans(qans % 2 === 0 ? CQANS.lslash : CQANS.rslash); c.draw(); }; let isNotSide = function (c) { return c.bx > board.minbx && c.bx < board.maxbx && c.by > board.minby && c.by < board.maxby; } for (let i = 0; i < board.cross.length; i++) { let cross = board.cross[i]; let adjcellList = [[board.getc(cross.bx - 1, cross.by - 1), CQANS.rslash, CQANS.lslash], [board.getc(cross.bx - 1, cross.by + 1), CQANS.lslash, CQANS.rslash], [board.getc(cross.bx + 1, cross.by - 1), CQANS.lslash, CQANS.rslash], [board.getc(cross.bx + 1, cross.by + 1), CQANS.rslash, CQANS.lslash]]; adjcellList = adjcellList.filter(c => !c[0].isnull); // finish clue if (cross.qnum >= 0) { if (adjcellList.filter(c => c[0].qans === c[1]).length === cross.qnum) { adjcellList.forEach(c => add_slash(c[0], c[2])); } if (adjcellList.filter(c => c[0].qans !== c[2]).length === cross.qnum) { adjcellList.forEach(c => add_slash(c[0], c[1])); } } // diagonal 1 & 1 if (cross.qnum === 1 && isNotSide(cross)) { for (let d = 0; d < 4; d++) { let cross2 = offset(cross, 1, 1, d); if (cross2.qnum === 1 && isNotSide(cross2)) { add_slash(offset(cross, .5, .5, d), CQANS.lslash + d); } } } // 1 & 1 if (cross.qnum === 1) { for (let d = 0; d < 4; d++) { if (offset(cross, 0, 1, d).isnull || offset(cross, 0, -1, d).isnull) { continue; } if (!offset(cross, 1, 0, d).isnull && offset(cross, 1, 0, d).qnum === 1) { add_slash(offset(cross, -0.5, -.5, d), CQANS.lslash + d); add_slash(offset(cross, -0.5, +.5, d), CQANS.rslash + d); add_slash(offset(cross, +1.5, -.5, d), CQANS.rslash + d); add_slash(offset(cross, +1.5, +.5, d), CQANS.lslash + d); } } } // 3 & 3 if (cross.qnum === 3) { for (let d = 0; d < 4; d++) { if (!offset(cross, 1, 0, d).isnull && offset(cross, 1, 0, d).qnum === 3 && isNotSide(offset(cross, 1, 0, d))) { add_slash(offset(cross, -0.5, -.5, d), CQANS.rslash + d); add_slash(offset(cross, -0.5, +.5, d), CQANS.lslash + d); add_slash(offset(cross, +1.5, -.5, d), CQANS.lslash + d); add_slash(offset(cross, +1.5, +.5, d), CQANS.rslash + d); } } } } // no loop for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; if (cell.qans !== CQANS.none) { continue; } let cross1, cross2; cross1 = board.getobj(cell.bx - 1, cell.by - 1); cross2 = board.getobj(cell.bx + 1, cell.by + 1); if (cross1.path !== null && cross1.path === cross2.path) { add_slash(cell, CQANS.lslash); } cross1 = board.getobj(cell.bx - 1, cell.by + 1); cross2 = board.getobj(cell.bx + 1, cell.by - 1); if (cross1.path !== null && cross1.path === cross2.path) { add_slash(cell, CQANS.rslash); } } } function NurikabeAssist() { for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; // surrounded white cell let templist = [offset(cell, 1, 0, 0), offset(cell, 1, 0, 1), offset(cell, 1, 0, 2), offset(cell, 1, 0, 3)]; if (cell.qnum === CQNUM.none && templist.filter(c => c.isnull || c.qans === CQANS.black).length === 4) { add_black(cell); } } flg = 0; CellConnected({ isShaded: c => c.qans === CQANS.black, isUnshaded: c => c.qsub === CQSUB.dot || c.qnum !== CQNUM.none, add_shaded: c => add_black(c, true), add_unshaded: add_dot, }); No2x2Cell({ isShaded: c => c.qans === CQANS.black, add_unshaded: add_dot, }); SizeRegion_Cell({ isShaded: c => c.qsub === CQSUB.dot || c.qnum !== CQNUM.none, isUnshaded: c => c.qans === CQANS.black, add_shaded: add_dot, add_unshaded: c => add_black(c, true), }); // unreachable cell { let list = []; for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; if (cell.qnum !== CQNUM.none) { list.push(cell); if (cell.qnum === CQNUM.quesmark) { continue; } for (let dx = -cell.qnum + 1; dx <= cell.qnum - 1; dx++) { for (let dy = -cell.qnum + Math.abs(dx) + 1; dy <= cell.qnum - Math.abs(dx) - 1; dy++) { let c = offset(cell, dx, dy); if (c.isnull || list.includes(c)) { continue; } list.push(c); } } } } if (!list.some(c => c.qnum === CQNUM.quesmark)) { for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; if (!list.includes(cell)) { add_black(cell); } } } } } function GuideArrowAssist() { BlackNotAdjacent(); GreenConnected(); GreenNoLoopInCell(); let goalcell = board.getc(board.goalpos.bx, board.goalpos.by); for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; if (cell === goalcell) { add_green(cell); continue; } if (cell.qnum !== CQNUM.none) { add_green(cell); if (cell.qnum !== CQNUM.quesmark) { let d = qdirremap(cell.qnum); add_green(dir(cell.adjacent, d)); } continue; } } // direction consistency { let vis = new Map(); let dfs = function (c, d) { if (vis.has(c)) return; vis.set(c, d); for (let d1 = 0; d1 < 4; d1++) { if (d1 === d) continue; let c1 = dir(c.adjacent, d1); if (c1 === undefined || c1.isnull || c1.qsub !== CQSUB.green) continue; dfs(c1, (d1 + 2) % 4); } }; dfs(goalcell, -1); for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; if (cell.qnum === CQNUM.none || cell.qnum === CQNUM.quesmark) continue; dfs(cell, qdirremap(cell.qnum)); } for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; let adjcell = cell.adjacent; if (cell.qsub !== CQSUB.none || cell.qans !== CQANS.none) continue; let cnt = 0; fourside(c => { if (!c.isnull && c.qsub === CQSUB.green && vis.has(c)) { cnt++; } }, adjcell); if (cnt >= 2) add_black(cell); } } // single out { let vis = new Map(); vis.set(goalcell, -1); for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; let d = (function () { if (cell.qnum === CQNUM.none || cell.qnum === CQNUM.quesmark) { let cnt = 0; let dd = -1; for (let d1 = 0; d1 < 4; d1++) { let c1 = dir(cell.adjacent, d1); if (c1 === undefined || c1.isnull || c1.qans === CQANS.black) continue; cnt++; dd = d1; } if (cnt === 1) return dd; return -1; } return qdirremap(cell.qnum);; })(); if (d === -1) continue; while (true) { if (vis.has(cell)) break; vis.set(cell, d); cell = dir(cell.adjacent, d); add_green(cell); let cnt = 0; let dd = -1; for (let d1 = 0; d1 < 4; d1++) { let c1 = dir(cell.adjacent, d1); if (c1 === undefined || c1.isnull || c1.qans === CQANS.black) continue; if (vis.has(c1) && vis.get(c1) === (d1 + 2) % 4) continue; cnt++; dd = d1; } if (cnt !== 1) break; d = dd; } } } } function YinyangAssist() { let add_color = function (c, color) { if (c === undefined || c.isnull || c.anum !== CANUM.none || color !== CANUM.wcir && color !== CANUM.bcir) { return; } if (step && flg) { return; } flg = true; c.setAnum(color); c.draw(); }; let add_black = function (c) { add_color(c, CANUM.bcir); }; let add_white = function (c) { add_color(c, CANUM.wcir); }; CellConnected({ isShaded: c => c.anum === CANUM.wcir, isUnshaded: c => c.anum === CANUM.bcir, add_shaded: add_white, add_unshaded: add_black, }); CellConnected({ isShaded: c => c.anum === CANUM.bcir, isUnshaded: c => c.anum === CANUM.wcir, add_shaded: add_black, add_unshaded: add_white, }); No2x2Cell({ isShaded: c => c.anum === CANUM.wcir, add_unshaded: add_black, }); No2x2Cell({ isShaded: c => c.anum === CANUM.bcir, add_unshaded: add_white, }); NoCheckerCell({ isShaded: c => c.anum === CANUM.wcir, isUnshaded: c => c.anum === CANUM.bcir, add_shaded: add_white, add_unshaded: add_black, }); // cell at side is grouped when both sides are even if (board.rows % 2 === 0 && board.cols % 2 === 0) { for (let i = 1; i + 1 < board.rows; i += 2) { let cell1 = board.getc(board.minbx + 1, 2 * i + 1); let cell2 = board.getc(board.minbx + 1, 2 * i + 3); if (cell1.anum !== CANUM.none || cell2.anum !== CANUM.none) { add_color(cell1, cell2.anum); add_color(cell2, cell1.anum); } cell1 = board.getc(board.maxbx - 1, 2 * i + 1); cell2 = board.getc(board.maxbx - 1, 2 * i + 3); if (cell1.anum !== CANUM.none || cell2.anum !== CANUM.none) { add_color(cell1, cell2.anum); add_color(cell2, cell1.anum); } } for (let i = 1; i + 1 < board.cols; i += 2) { let cell1 = board.getc(2 * i + 1, board.minby + 1); let cell2 = board.getc(2 * i + 3, board.minby + 1); if (cell1.anum !== CANUM.none || cell2.anum !== CANUM.none) { add_color(cell1, cell2.anum); add_color(cell2, cell1.anum); } cell1 = board.getc(2 * i + 1, board.maxby - 1); cell2 = board.getc(2 * i + 3, board.maxby - 1); if (cell1.anum !== CANUM.none || cell2.anum !== CANUM.none) { add_color(cell1, cell2.anum); add_color(cell2, cell1.anum); } } } for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; if (cell.qnum !== CQNUM.none) { add_color(cell, cell.qnum); } // ○ ● ○●● // ○ ○ -> ○ ○ if (cell.anum === CANUM.none) { for (let d = 0; d < 4; d++) { let templist = [offset(cell, 1, -1, d), offset(cell, 1, 1, d), offset(cell, 0, -1, d), offset(cell, 0, 1, d)]; if (!templist.some(c => c.isnull || c.anum === CANUM.none) && templist[0].anum === templist[1].anum && templist[2].anum !== templist[3].anum) { add_color(cell, CANUM.bcir + CANUM.wcir - templist[0].anum); } } } } // outside { let firstcell = board.cell[0]; let cellList = []; for (let j = 0; j < board.rows; j++) { cellList.push(offset(firstcell, 0, j)); } for (let i = 1; i < board.cols - 1; i++) { cellList.push(offset(firstcell, i, board.rows - 1)); } for (let j = board.rows - 1; j >= 0; j--) { cellList.push(offset(firstcell, board.cols - 1, j)); } for (let i = board.cols - 2; i > 0; i--) { cellList.push(offset(firstcell, i, 0)); } let len = cellList.length; if (cellList.some(c => c.anum === CANUM.bcir) && cellList.some(c => c.anum === CANUM.wcir)) { for (let i = 0; i < len; i++) { if (cellList[i].anum === CANUM.none || cellList[(i + 1) % len].anum !== CANUM.none) { continue; } for (let j = (i + 1) % len; j != i; j = (j + 1) % len) { if (cellList[j].anum === CANUM.bcir + CANUM.wcir - cellList[i].anum) { break; } if (cellList[j].anum === CANUM.none) { continue; } if (cellList[j].anum === cellList[i].anum) { for (let k = i; k != j; k = (k + 1) % len) { add_color(cellList[k], cellList[i].anum); } } } } } } } function NuriMazeAssist() { No2x2Black(); No2x2Green(); CellConnected({ isShaded: c => c.qsub === CQSUB.green, isUnshaded: c => c.qans === CQANS.black, add_shaded: add_green, add_unshaded: add_black, isLinked: (c, nb, nc) => c.room === nc.room, }); CellConnected({ isShaded: function (c) { let startcell = board.getc(board.startpos.bx, board.startpos.by); let goalcell = board.getc(board.goalpos.bx, board.goalpos.by); return c === startcell || c === goalcell || c.ques === CQUES.cir; }, isUnshaded: c => c.qans === CQANS.black || c.ques === CQUES.tri, add_shaded: add_green, add_unshaded: () => { }, isLinked: (c, nb, nc) => c.room === nc.room, }); let startcell = board.getc(board.startpos.bx, board.startpos.by); let goalcell = board.getc(board.goalpos.bx, board.goalpos.by); let circnt = 0; for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; circnt += cell.ques === CQUES.cir; } for (let i = 0; i < board.roommgr.components.length; i++) { let room = board.roommgr.components[i]; let cellList = []; for (let j = 0; j < room.clist.length; j++) { cellList.push(room.clist[j]); } if (cellList.some(c => c.qsub === CQSUB.green || c.ques === CQUES.cir || c.ques === CQUES.tri || c.lcnt > 0) || room === startcell.room || room === goalcell.room) { cellList.forEach(c => add_green(c)); continue; } if (cellList.some(c => c.qans === CQANS.black)) { cellList.forEach(c => add_black(c)); continue; } let circnt1 = circnt, circnt2 = circnt; let templist = []; cellList.forEach(c => { let list = [offset(c, -1, 0), offset(c, 0, -1), offset(c, 0, 1), offset(c, 1, 0)]; list.forEach(c => { if (c.isnull || templist.includes(c)) { return; } templist.push(c); }); }); templist = templist.filter(c => !c.isnull && c.qsub === CQSUB.green); if (templist.length < 2) { continue; } // no loop let templist2 = templist.map(c => { let dfslist = []; let dfs = function (c) { if (c.isnull || c.qsub !== CQSUB.green || dfslist.includes(c)) { return; } dfslist.push(c); fourside(dfs, c.adjacent); }; dfs(c); if (dfslist.some(c => c === startcell)) { circnt1 = dfslist.filter(c => c.ques === CQUES.cir).length; } if (dfslist.some(c => c === goalcell)) { circnt2 = dfslist.filter(c => c.ques === CQUES.cir).length; } return dfslist.filter(c => templist.includes(c)).length; }); if (templist2.some(n => n > 1)) { cellList.forEach(c => add_black(c)); continue; } // not enough cir if (circnt1 + circnt2 < circnt) { cellList.forEach(c => add_black(c)); continue; } // no branch for line templist2 = templist.map(c => { let res = 0; let dfslist = []; let dfs = function (c) { if (c.isnull || c.qsub !== CQSUB.green || dfslist.includes(c)) { return; } if (c === startcell || c === goalcell || c.ques === CQUES.cir || c.lcnt > 0) { res += c === startcell; res += c === goalcell; res += (c.ques === CQUES.cir && c.lcnt === 0); res += c.lcnt; res += (dfslist.some(c => c.ques === CQUES.tri) ? 2 : 0); return; } dfslist.push(c); fourside(dfs, c.adjacent); dfslist.pop(); } dfs(c); return Math.min(res, 2); }); if (templist2.reduce((a, b) => a + b) > 2) { cellList.forEach(c => add_black(c)); continue; } } for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; if (cell.ques === CQUES.cir) { let templist = [offset(cell, -1, 0), offset(cell, 1, 0), offset(cell, 0, -1), offset(cell, 0, 1)]; templist = templist.filter(c => !c.isnull && c.qans !== CQANS.black); if (templist.length === 2) { templist.forEach(c => add_green(c)); } } // surrounded by black { let templist = [offset(cell, -1, 0), offset(cell, 1, 0), offset(cell, 0, -1), offset(cell, 0, 1)]; if (templist.filter(c => c.isnull || c.qans === CQANS.black).length === 4) { add_black(cell); } } // no 2*2 { let templist = [cell, offset(cell, 1, 0), offset(cell, 0, 1), offset(cell, 1, 1)]; if (!templist.some(c => c.isnull) && !templist.some(c => c.qsub === CQSUB.green)) { let templist2 = templist.filter(c => !c.qans); if (templist2.length > 0 && !templist2.some(c => c.room !== templist2[0].room)) { add_green(templist2[0]); } } if (!templist.some(c => c.qans)) { let templist2 = templist.filter(c => c.qsub !== CQSUB.green); if (templist2.length > 0 && !templist2.some(c => c.room !== templist2[0].room)) { add_black(templist2[0]); } } } } // line for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; let adjcell = cell.adjacent; let adjline = cell.adjborder; if (cell.qans === CQANS.black || cell.ques === CQUES.tri) { fourside(add_cross, adjline); } if (cell.qans !== CQANS.black) { let emptycnt = 0; let linecnt = 0; fourside((c, b) => { if (!c.isnull && b.qsub !== BQSUB.cross) { emptycnt++; } linecnt += b.line; }, adjcell, adjline); if (linecnt > 0) { add_green(cell); } // no branch if (linecnt === 2 || linecnt === 1 && (cell === startcell || cell === goalcell)) { fourside(add_cross, adjline); } // no deadend if (emptycnt === 1) { if (cell !== startcell && cell !== goalcell) { fourside(add_cross, adjline); } else { fourside((c, b) => { if (!c.isnull && b.qsub !== BQSUB.cross) { add_line(b); } }, adjcell, adjline); } } // 2 degree path if (emptycnt === 2 && cell !== startcell && cell !== goalcell && (linecnt === 1 || cell.ques === CQUES.cir)) { fourside((c, b) => { add_line(b); if (!b.isnull && b.line) { add_green(c); } }, adjcell, adjline); } // extend line if (linecnt === 1 && cell !== startcell && cell !== goalcell || linecnt === 0 && (cell === startcell || cell === goalcell || cell.ques === CQUES.cir)) { let fn = function (c, b, list) { if (c.isnull || c.qsub !== CQSUB.green || list.includes(c)) { return; } if (b !== null && b.line) { return; } list.push(c); if (c.lcnt === 1 || c.ques === CQUES.cir || c === startcell || c === goalcell) { for (let j = 1; j < list.length; j++) { let cell1 = list[j - 1]; let cell2 = list[j]; let border = board.getb((cell1.bx + cell2.bx) / 2, (cell1.by + cell2.by) / 2); add_line(border); add_green(cell1); add_green(cell2); } } fn(c.adjacent.top, c.adjborder.top, list); fn(c.adjacent.bottom, c.adjborder.bottom, list); fn(c.adjacent.left, c.adjborder.left, list); fn(c.adjacent.right, c.adjborder.right, list); list.pop(); } fn(cell, null, []); } } } } function AquapelagoAssist() { No2x2Green(); BlackNotAdjacent(); GreenConnected(); for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; if (cell.qnum !== CQNUM.none) { add_black(cell); } if (cell.qnum > 0) { let templist = []; let fn = function (c) { if (c.qans !== CQANS.black) { return; } if (templist.includes(c)) { return; } templist.push(c); fn(offset(c, -1, -1)); fn(offset(c, -1, +1)); fn(offset(c, +1, -1)); fn(offset(c, +1, +1)); } fn(cell); if (templist.length === cell.qnum) { templist.forEach(c => { add_green(offset(c, -1, -1)); add_green(offset(c, -1, +1)); add_green(offset(c, +1, -1)); add_green(offset(c, +1, +1)); }); } } } } function IcebarnAssist() { SingleLoopInCell(); // add cross outside except IN and OUT { let inp = [board.arrowin.bx, board.arrowin.by]; let outp = [board.arrowout.bx, board.arrowout.by]; add_line(board.getb(inp[0], inp[1])); add_line(board.getb(outp[0], outp[1])); let minbx = board.minbx + 2; let minby = board.minby + 2; let maxbx = board.maxbx - 2; let maxby = board.maxby - 2; for (let j = minbx + 1; j < maxbx; j += 2) { add_cross(board.getb(j, minby)); add_cross(board.getb(j, maxby)); } for (let j = minby + 1; j < maxby; j += 2) { add_cross(board.getb(minbx, j)); add_cross(board.getb(maxbx, j)); } } for (let i = 0; i < board.border.length; i++) { let border = board.border[i]; if (border.qdir != QDIR.none) { add_arrow(border, border.qdir + 10); // from qdir to bqsub add_line(border); } } for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; let adjline = cell.adjborder; if (cell.ques === CQUES.ice) { for (let d = 0; d < 4; d++) { if (dir(adjline, d).qsub === BQSUB.cross) { add_cross(dir(adjline, d + 2)); } if (dir(adjline, d).line) { add_line(dir(adjline, d + 2)); if (dir(adjline, d).qsub !== BQSUB.none) { add_arrow(dir(adjline, d + 2), dir(adjline, d).qsub); } } } } if (cell.lcnt === 2 && cell.ques !== CQUES.ice) { let templist = [[adjline.top, BQSUB.arrow_up, BQSUB.arrow_dn], [adjline.bottom, BQSUB.arrow_dn, BQSUB.arrow_up], [adjline.left, BQSUB.arrow_lt, BQSUB.arrow_rt], [adjline.right, BQSUB.arrow_rt, BQSUB.arrow_lt]]; templist = templist.filter(b => b[0].line); if (templist.filter(b => b[0].qsub === BQSUB.none).length === 1) { if (templist[0][0].qsub !== BQSUB.none) { templist = [templist[1], templist[0]]; } if (templist[1][0].qsub === templist[1][1]) { add_arrow(templist[0][0], templist[0][2]); } if (templist[1][0].qsub === templist[1][2]) { add_arrow(templist[0][0], templist[0][1]); } } } if (cell.lcnt === 1 && cell.ques !== CQUES.ice) { for (let d = 0; d < 4; d++) { let ncell = dir(cell.adjacent, d); while (!ncell.isnull && ncell.ques === CQUES.ice) { ncell = dir(ncell.adjacent, d); } if (ncell.isnull || ncell.lcnt !== 1 || dir(ncell.adjborder, d + 2).line) { continue; } let fn = function (c) { let adjline = c.adjborder; let templist = [[adjline.top, BQSUB.arrow_up, BQSUB.arrow_dn], [adjline.bottom, BQSUB.arrow_dn, BQSUB.arrow_up], [adjline.left, BQSUB.arrow_lt, BQSUB.arrow_rt], [adjline.right, BQSUB.arrow_rt, BQSUB.arrow_lt]]; templist = templist.filter(b => b[0].line); if (templist.length !== 1) { return 0; } if (templist[0][0].qsub === templist[0][1]) { return 1; } if (templist[0][0].qsub === templist[0][2]) { return 2; } return 0; } if (fn(cell) && fn(cell) === fn(ncell)) { add_cross(dir(adjline, d)); } } } } } function LitsAssist() { BlackConnected(); No2x2Black(); for (let i = 0; i < board.roommgr.components.length; i++) { let room = board.roommgr.components[i]; let templist = []; for (let j = 0; j < room.clist.length; j++) { templist.push(room.clist[j]); } if (templist.filter(c => c.qsub !== CQSUB.dot).length === 4) { templist.forEach(c => add_black(c)); } if (templist.filter(c => c.qans === CQANS.black).length === 4) { templist.forEach(c => add_dot(c)); } for (let j = 0; j < room.clist.length; j++) { if (room.clist[j].qsub === CQSUB.dot) { continue; } let templist2 = []; let fn = function (c) { if (c.room !== room || c.qsub === CQSUB.dot || templist2.includes(c)) { return; } templist2.push(c); fourside(fn, c.adjacent); } fn(room.clist[j]); if (templist2.length < 4) { templist2.forEach(c => add_dot(c)); } if (room.clist[j].qans !== CQANS.black) { continue; } templist2 = []; let fn2 = function (c, step = 3) { if (step < 0 || c.room !== room) { return; } templist2.push(c); fn2(c.adjacent.top, step - 1); fn2(c.adjacent.bottom, step - 1); fn2(c.adjacent.left, step - 1); fn2(c.adjacent.right, step - 1); } fn2(room.clist[j]); templist.forEach(c => { if (!templist2.includes(c)) { add_dot(c); } }); } } } function NothreeAssist() { BlackNotAdjacent(); GreenConnected(); for (let i = 0; i < board.dots.length; i++) { let dot = board.dots[i].piece; if (dot.qnum !== 1) { continue; } let cellList = []; if (dot.bx % 2 === 1 && dot.by % 2 === 1) { cellList.push(board.getc(dot.bx, dot.by)); } if (dot.bx % 2 === 0 && dot.by % 2 === 1) { cellList.push(board.getc(dot.bx - 1, dot.by)); cellList.push(board.getc(dot.bx + 1, dot.by)); } if (dot.bx % 2 === 1 && dot.by % 2 === 0) { cellList.push(board.getc(dot.bx, dot.by - 1)); cellList.push(board.getc(dot.bx, dot.by + 1)); } if (dot.bx % 2 === 0 && dot.by % 2 === 0) { cellList.push(board.getc(dot.bx - 1, dot.by - 1)); cellList.push(board.getc(dot.bx + 1, dot.by - 1)); cellList.push(board.getc(dot.bx - 1, dot.by + 1)); cellList.push(board.getc(dot.bx + 1, dot.by + 1)); } let blackcnt = 0; let emptycnt = 0; cellList.forEach(c => { blackcnt += c.qans === CQANS.black; emptycnt += c.qans !== CQANS.black && c.qsub !== CQSUB.dot; }); if (blackcnt === 0 && emptycnt === 1) { cellList.forEach(c => add_black(c)); } if (blackcnt === 1) { cellList.forEach(c => add_green(c)); } } for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; for (let d = 0; d < 4; d++) { let fn = function (list) { if (!list.some(c => c.isnull) && list.filter(c => c.qans === CQANS.black).length === 2) { list.forEach(c => add_green(c)); } } // O.O.O fn([cell, offset(cell, 2, 0, d), offset(cell, 4, 0, d)]); // O..O..O fn([cell, offset(cell, 3, 0, d), offset(cell, 6, 0, d)]); // O...O...O fn([cell, offset(cell, 4, 0, d), offset(cell, 8, 0, d)]); // OXXXXOX?XXO for (let l = 5; l * 2 < Math.max(board.cols, board.rows); l++) { let templist1 = [cell, offset(cell, l, 0, d), offset(cell, 2 * l, 0, d)]; if (templist1.some(c => c.isnull)) { continue; } templist1 = templist1.filter(c => c.qans !== CQANS.black); let templist2 = []; for (let j = 1; j < 2 * l; j++) { if (j === l) { continue; } templist2.push(offset(cell, j, 0, d)); } if (templist2.some(c => c.qans === CQANS.black)) { continue; } templist2 = templist2.filter(c => c.qsub !== CQSUB.dot); if (templist1.length === 0 && templist2.length === 1) { add_black(templist2[0]); } if (templist1.length === 1 && templist2.length === 0) { add_green(templist1[0]); } } } } } function AyeheyaAssist() { for (let i = 0; i < board.roommgr.components.length; i++) { let room = board.roommgr.components[i]; let qnum = room.top.qnum; let rows = room.clist.getRectSize().rows; let cols = room.clist.getRectSize().cols; let tx = room.clist.getRectSize().x1 + room.clist.getRectSize().x2; let ty = room.clist.getRectSize().y1 + room.clist.getRectSize().y2; if (rows % 2 === 1 && cols % 2 === 0) { let c1 = board.getc(tx / 2 - 1, ty / 2); let c2 = board.getc(tx / 2 + 1, ty / 2); if (c1.room === room) { add_green(c1); } if (c2.room === room) { add_green(c2); } } if (rows % 2 === 0 && cols % 2 === 1) { let c1 = board.getc(tx / 2, ty / 2 - 1); let c2 = board.getc(tx / 2, ty / 2 + 1); if (c1.room === room) { add_green(c1); } if (c2.room === room) { add_green(c2); } } if (rows % 2 === 1 && cols % 2 === 1) { let c = board.getc(tx / 2, ty / 2); if (qnum >= 0 && qnum % 2 === 0 && c.room === room) { add_green(c); } if (qnum >= 0 && qnum % 2 === 1 && c.room === room) { add_black(c); } } for (let j = 0; j < room.clist.length; j++) { let cell = room.clist[j]; if (cell.qsub === CQSUB.green) { add_green(board.getc(tx - cell.bx, ty - cell.by)); } if (cell.qans === CQANS.black) { add_black(board.getc(tx - cell.bx, ty - cell.by)); } } } HeyawakeAssist(); } function ShakashakaAssist() { let isEmpty = function (c) { return !c.isnull && c.qnum === CQNUM.none && c.qsub === CQSUB.none && c.qans === CQANS.none; }; // draw triangle let add_triangle = function (c, ndir) { // 0 = bl, 1 = br, 2 = tr, 3 = tl if (c === undefined || c.isnull || !isEmpty(c)) { return; } if (step && flg) { return; } flg = true; ndir = (ndir % 4 + 4) % 4; c.setQans(ndir + 2); c.draw(); }; // check blacking edge let isEdge = function (c, ndir) { // 0 = left, 1 = bottom, 2 = right, 3 = top ndir = (ndir % 4 + 4) % 4; let tdir = (ndir + 3) % 4; return c.isnull || c.qnum !== CQNUM.none || c.qans === tdir + 2 || c.qans === ndir + 2; }; // check dot area if stick edge let isDotEdge = function (c) { if (c.qsub !== CQSUB.dot) { return false; } let temp = false; let dfslist = [c]; let dfs = function (c, ndir) { if (isEmpty(c) || dfslist.includes(c)) { return; } if (isEdge(c, ndir + 2)) { temp = true; return; } if (c.qsub === CQSUB.dot) { dfslist.push(c); dfs(offset(c, -1, 0), 0); dfs(offset(c, 0, 1), 1); dfs(offset(c, 1, 0), 2); dfs(offset(c, 0, -1), 3); } }; for (let d = 0; d < 4; d++) { dfs(dir(c.adjacent, d + 1), d); } return temp; }; // check blacking edge including dot let isEdgeEx = function (c, ndir) { // 0 = left, 1 = bottom, 2 = right, 3 = top return isEdge(c, ndir) || isDotEdge(c); }; // check blacking corner including dot let isCorner = function (c, ndir) { // 0 = bl, 1 = br, 2 = tr, 3 = tl ndir = (ndir % 4 + 4) % 4; return c.isnull || c.qnum !== CQNUM.none || c.qans === ndir + 2 || isDotEdge(c); }; // check blacking sharp including dot let isSharp = function (c, ndir) { // 0 = bl, 1 = br, 2 = tr, 3 = tl ndir = (ndir % 4 + 4) % 4; return isEdgeEx(c, ndir) || c.qans === (ndir + 1) % 4 + 2; }; // if can place triangle of specific direction let isntTri = function (c, ndir) { // 0 = bl, 1 = br, 2 = tr, 3 = tl ndir = (ndir % 4 + 4) % 4; if (!isEmpty(c) && c.qans !== ndir + 2) { return true; } if (c.qans === ndir + 2) { return false; } let temp = offset(c, 1, 0, ndir); if (isEdgeEx(temp, ndir) || temp.qans === (ndir + 2) % 4 + 2) { return true; } temp = offset(c, 0, -1, ndir); if (isEdgeEx(temp, ndir + 1) || temp.qans === (ndir + 2) % 4 + 2) { return true; } temp = offset(c, -1, 0, ndir); if (!temp.isnull && (temp.qans === (ndir + 3) % 4 + 2 || temp.qans === ndir + 2)) { return true; } temp = offset(c, 0, 1, ndir); if (!temp.isnull && (temp.qans === ndir + 2 || temp.qans === (ndir + 1) % 4 + 2)) { return true; } temp = offset(c, 1, -1, ndir); if (!temp.isnull && isSharp(temp, ndir)) { return true; } temp = offset(c, -1, -1, ndir); if (!temp.isnull && temp.qans === (ndir + 3) % 4 + 2) { return true; } temp = offset(c, -1, 1, ndir); if (!temp.isnull && temp.qans === ndir + 2) { return true; } temp = offset(c, 1, 1, ndir); if (!temp.isnull && temp.qans === (ndir + 1) % 4 + 2) { return true; } temp = offset(c, 2, 0, ndir); if (!temp.isnull && temp.qans === (ndir + 1) % 4 + 2) { return true; } temp = offset(c, 0, -2, ndir); if (!temp.isnull && temp.qans === (ndir + 3) % 4 + 2) { return true; } temp = offset(c, 2, -1, ndir); if (!temp.isnull && temp.qans === (ndir + 2) % 4 + 2) { return true; } temp = offset(c, 1, -2, ndir); if (!temp.isnull && temp.qans === (ndir + 2) % 4 + 2) { return true; } return false; }; // extend of isntTri including some complex logic let isntTriEx = function (c, ndir) { // 0 = bl, 1 = br, 2 = tr, 3 = tl if (isntTri(c, ndir)) { return true; } let templist = [offset(c, -1, 0, ndir), offset(c, 0, 1, ndir), offset(c, -2, 0, ndir), offset(c, 0, 2, ndir), offset(c, -1, 1, ndir)]; if (isEmpty(templist[0]) && isEmpty(templist[1]) && isEdgeEx(templist[2], ndir + 2) && isEdgeEx(templist[3], ndir + 3) && isEmpty(templist[4]) && isntTri(templist[4], ndir + 2)) { return true; } return false; } // start assist for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; let adjcell = cell.adjacent; let tricnt = 0; let emptycnt = 0; fourside(c => { if (!c.isnull && c.qans >= 2) { tricnt++; } if (isEmpty(c)) { emptycnt++; } }, adjcell); // add dot // cannot place any triangle { let temp = true; for (let d = 0; d < 4; d++) { temp &= isntTriEx(cell, d); } if (temp) { add_dot(cell); } } // fill rectangle { let templist = [cell, offset(cell, 1, 0), offset(cell, 0, 1), offset(cell, 1, 1)]; if (!templist.some(c => c.isnull)) { templist = templist.filter(c => c.qsub !== CQSUB.dot); if (templist.length === 1) { add_dot(templist[0]); } } } // dot by clue if (tricnt === cell.qnum) { fourside(add_dot, adjcell); } // pattern with clue 1 if (cell.qnum === 1) { for (let d = 0; d < 4; d++) { let tempcell = offset(cell, -1, 0, d); let tempstate = !tempcell.isnull && (isEmpty(tempcell) || tempcell.qsub === CQSUB.dot); tempcell = offset(cell, 0, 1, d); tempstate &= !tempcell.isnull && (isEmpty(tempcell) || tempcell.qsub === CQSUB.dot); tempcell = offset(cell, -1, 1, d); tempstate &= tempcell.qnum === CQNUM.none && isntTriEx(tempcell, d + 2); if (tempstate) { add_dot(offset(cell, 1, 0, d)); add_dot(offset(cell, 0, -1, d)); } } } // add triangle // cannot form non-rectangle if (isEmpty(cell)) { for (let d = 0; d < 4; d++) { let templist = [offset(cell, -1, 0, d), offset(cell, 0, 1, d), offset(cell, -1, 1, d)]; let templist_dot = templist.filter(c => !c.isnull && !isEmpty(c) && c.qsub === CQSUB.dot); let templist_ndot = templist.filter(c => !c.isnull && !isEmpty(c) && c.qsub !== CQSUB.dot); if (templist_dot.length === 2 && templist_ndot.length === 1) { let temp = templist_ndot[0]; if (templist.indexOf(temp) === 0 && isCorner(temp, d + 1)) { add_triangle(cell, d); break; } else if (templist.indexOf(temp) === 1 && isCorner(temp, d + 3)) { add_triangle(cell, d); break; } else if (templist.indexOf(temp) === 2 && isCorner(temp, d + 2)) { add_triangle(cell, d); break; } } } } // triangle by clue if (emptycnt === cell.qnum - tricnt) { for (let d = 0; d < 4; d++) { let adj = dir(adjcell, d); if (isEmpty(adj)) { if (isntTriEx(adj, d)) { add_triangle(adj, d + 1); } else if (isntTriEx(adj, d + 1)) { add_triangle(adj, d); } } } } // side extend if (cell.qans >= 2) { let ndir = cell.qans - 2; // rectangle needs turn or cannot turn if (isntTriEx(offset(cell, -1, -1, ndir), ndir)) { add_triangle(offset(cell, 0, -1, ndir), ndir + 3); } else if (isntTriEx(offset(cell, 0, -1, ndir), ndir + 3)) { add_triangle(offset(cell, -1, -1, ndir), ndir); } if (isntTriEx(offset(cell, 1, 1, ndir), ndir)) { add_triangle(offset(cell, 1, 0, ndir), ndir + 1); } else if (isntTriEx(offset(cell, 1, 0, ndir), ndir + 1)) { add_triangle(offset(cell, 1, 1, ndir), ndir); } // only one opposite side position if (isEdgeEx(offset(cell, 2, -1, ndir), ndir)) { add_triangle(offset(cell, 1, -1, ndir), ndir + 2); } else if (isEdgeEx(offset(cell, 1, -2, ndir), ndir + 1)) { add_triangle(offset(cell, 1, -1, ndir), ndir + 2); } else if (isSharp(offset(cell, 2, -2, ndir), ndir)) { add_triangle(offset(cell, 1, -1, ndir), ndir + 2); } // rectangle opposite side extend let temp = cell; while (!offset(temp, -1, -1, ndir).isnull && offset(temp, -1, -1, ndir).qans === cell.qans) { temp = offset(temp, -1, -1, ndir); } let turn1 = offset(temp, 0, -1, ndir); if (turn1.qans === (ndir + 3) % 4 + 2) { let temp = cell; while (!offset(temp, 1, 1, ndir).isnull && offset(temp, 1, 1, ndir).qans === cell.qans) { temp = offset(temp, 1, 1, ndir); } let turn2 = offset(temp, 1, 0, ndir); if (turn2.qans === (ndir + 1) % 4 + 2) { turn1 = offset(turn1, 1, -1, ndir); turn2 = offset(turn2, 1, -1, ndir); while ((!turn1.isnull && turn1.qans === (ndir + 3) % 4 + 2) || (!turn2.isnull && turn2.qans === (ndir + 1) % 4 + 2)) { add_triangle(turn1, ndir + 3); add_triangle(turn2, ndir + 1); turn1 = offset(turn1, 1, -1, ndir); turn2 = offset(turn2, 1, -1, ndir); } } } } // 2x2 pattern if (isEmpty(cell)) { for (let d = 0; d < 4; d++) { let templist = [offset(cell, -1, 0, d), offset(cell, 0, -1, d), offset(cell, 0, 1, d), offset(cell, -1, -1, d), offset(cell, 0, -2, d)]; if (!templist[0].isnull && templist[0].qsub === CQSUB.dot && isEmpty(templist[1]) && isEdge(templist[2], d + 3) && isEdge(templist[3], d + 1) && isEdgeEx(templist[4], d + 1)) { add_triangle(cell, d); break; } templist = [offset(cell, 0, 1, d), offset(cell, 1, 0, d), offset(cell, -1, 0, d), offset(cell, 1, 1, d), offset(cell, 2, 0, d)]; if (!templist[0].isnull && templist[0].qsub === CQSUB.dot && isEmpty(templist[1]) && isEdge(templist[2], d + 2) && isEdge(templist[3], d) && isEdgeEx(templist[4], d)) { add_triangle(cell, d); break; } } } } } function HeyawakeAssist() { GreenConnected(); BlackNotAdjacent(); for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; let adjcell = cell.adjacent; let blackcnt = 0; fourside(c => { blackcnt += c.isnull || c.qans === CQANS.black; }, adjcell); // no two facing doors for (let d = 0; d < 4; d++) { if (cell.qsub !== CQSUB.green) { break; } let pcell = dir(cell.adjacent, d); let bordercnt = 0; let emptycellList = [cell]; while (!pcell.isnull && pcell.qans !== CQANS.black && bordercnt < 2) { if (dir(pcell.adjborder, d + 2).ques) { bordercnt++; } emptycellList.push(pcell); pcell = dir(pcell.adjacent, d); } emptycellList = emptycellList.filter(c => c.qsub !== CQSUB.green); if (bordercnt === 2 && emptycellList.length === 1) { add_black(emptycellList[0]); } } } const MAXSIT = 50000; const MAXAREA = 50; for (let i = 0; i < board.roommgr.components.length; i++) { let room = board.roommgr.components[i]; let qnum = room.top.qnum; if (qnum === CQNUM.none || qnum === CQNUM.quesmark) { continue; } let list = []; let surlist = []; let sitcnt = 0; let cst = new Map(); let apl = new Map(); for (let j = 0; j < room.clist.length; j++) { let cell = room.clist[j]; list.push(cell); cst.set(cell, (cell.qans === CQANS.black ? "BLK" : (cell.qsub === CQSUB.green ? "GRN" : "UNK"))); apl.set(cell, (cell.qans === CQANS.black ? "BLK" : (cell.qsub === CQSUB.green ? "GRN" : "UNK"))); } if (qnum === list.filter(c => c.qans === CQANS.black).length) { list.forEach(c => add_green(c)); continue; } // randomly chosen approximate formula if (list.filter(c => c.qans === CQANS.none && c.qsub === CQSUB.none).length > MAXAREA && (qnum - list.filter(c => c.qans === CQANS.black).length + 1) < list.filter(c => c.qans === CQANS.none && c.qsub === CQSUB.none).length) { continue; } if ((qnum - list.filter(c => c.qans === CQANS.black).length) * 2 + 5 < list.filter(c => c.qans === CQANS.none && c.qsub === CQSUB.none).length) { continue; } list.forEach(c => { adjlist(c.adjacent).forEach(c => { if (c.isnull || c.room === room || surlist.includes(c)) { return; } if (c.qsub === CQSUB.green || c.qans === CQANS.black) { return; } surlist.push(c); apl.set(c, "GRN"); }); }); let dfs = function (i, blkcnt) { if (sitcnt > MAXSIT) { return; } if (i === list.length) { if (blkcnt !== qnum) { return; } let templist = []; let templist2 = []; if (list.some(c => { if (cst.get(c) === "BLK") { return false; } if (templist.includes(c)) { return false; } let n = 0; let olist = []; let dfs = function (c) { if (c.isnull || templist.includes(c)) { return false; } if (c.room !== room) { if (c.qans === CQANS.black) { return false; } olist.push(c); return true; } if (cst.get(c) === "BLK") { return false; } templist.push(c); n++; let res = 0; res |= dfs(offset(c, -1, 0)); res |= dfs(offset(c, 0, -1)); res |= dfs(offset(c, 1, 0)); res |= dfs(offset(c, 0, 1)); return res; }; let res = dfs(c); if (olist.length === 1) { templist2.push(olist[0]); } if (!res && n + qnum < list.length) { return true; } return false; })) { return; }; list.forEach(c => { if (apl.get(c) !== "UNK" && apl.get(c) !== cst.get(c)) { apl.set(c, "AMB"); } if (apl.get(c) === "UNK") { apl.set(c, cst.get(c)); } }); surlist.forEach(c => { if (templist2.includes(c)) { return; } let templist = [offset(c, -1, 0), offset(c, 0, -1), offset(c, 1, 0), offset(c, 0, 1)]; if (templist.some(c => !c.isnull && c.room === room && cst.get(c) === "BLK")) { return; } apl.set(c, "AMB"); }); return; } if (cst.get(list[i]) !== "UNK") { dfs(i + 1, blkcnt); return; } sitcnt++; let templist = [offset(list[i], -1, 0), offset(list[i], 0, -1), offset(list[i], 1, 0), offset(list[i], 0, 1)]; if (blkcnt < qnum && !templist.some(c => !c.isnull && c.qans === CQANS.black || cst.has(c) && cst.get(c) === "BLK")) { cst.set(list[i], "BLK"); dfs(i + 1, blkcnt + 1); cst.set(list[i], "UNK"); } cst.set(list[i], "GRN"); dfs(i + 1, blkcnt); cst.set(list[i], "UNK"); }; dfs(0, list.filter(c => c.qans === CQANS.black).length); if (sitcnt > MAXSIT) { continue; } list.forEach(c => { if (apl.get(c) === "BLK") { add_black(c); } if (apl.get(c) === "GRN") { add_green(c); } }); surlist.forEach(c => { if (apl.get(c) === "GRN") { add_green(c); } }); } } function AkariAssist() { let isEmpty = c => !c.isnull && c.qnum === CQNUM.none && c.qans !== CQANS.light && c.qsub !== CQSUB.dot; let isNotLight = c => c.isnull || c.qnum !== CQNUM.none || c.qsub === CQSUB.dot let add_light = function (c) { add_black(c, true); }; for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; let adjcell = cell.adjacent; let emptycnt = 0; let lightcnt = 0; // add dot where lighted if (cell.qlight && cell.qans !== CQANS.light) { add_dot(cell); } // only one place can light if (cell.qnum === CQNUM.none && !cell.qlight) { let emptycellList = []; if (cell.qsub !== CQSUB.dot) { emptycellList.push(cell); } for (let d = 0; d < 4; d++) { let pcell = dir(cell.adjacent, d); while (!pcell.isnull && pcell.qnum === CQNUM.none) { emptycellList.push(pcell); pcell = dir(pcell.adjacent, d); } } emptycellList = emptycellList.filter(c => c.qsub !== CQSUB.dot); if (emptycellList.length === 1) { add_light(emptycellList[0]); } } fourside(c => { if (!c.isnull && c.qnum === CQNUM.none && c.qsub !== CQSUB.dot && c.qans !== CQANS.light) { emptycnt++; } lightcnt += (c.qans === CQANS.light); }, adjcell); if (cell.qnum >= 0) { // finished clue if (cell.qnum === lightcnt) { fourside(add_dot, adjcell); } // finish clue if (cell.qnum === emptycnt + lightcnt) { fourside(add_light, adjcell); } // dot at corner if (cell.qnum - lightcnt + 1 === emptycnt) { for (let d = 0; d < 4; d++) { if (isEmpty(offset(cell, 0, 1, d)) && isEmpty(offset(cell, 1, 0, d)) && isEmpty(offset(cell, 1, 1, d))) { add_dot(offset(cell, 1, 1, d)); } } } } // 3 & 1 if (cell.qnum === 3) { for (let d = 0; d < 4; d++) { if (!offset(cell, 1, 1, d).isnull && offset(cell, 1, 1, d).qnum === 1) { add_light(offset(cell, -1, 0, d)); add_light(offset(cell, 0, -1, d)); add_dot(offset(cell, 1, 2, d)); add_dot(offset(cell, 2, 1, d)); } } } // 2 & 1 if (cell.qnum === 2) { for (let d = 0; d < 4; d++) { if (!offset(cell, 1, 1, d).isnull && offset(cell, 1, 1, d).qnum === 1) { add_dot(offset(cell, -1, -1, d)); } } } // 1 & 1 if (cell.qnum === 1) { for (let d = 0; d < 4; d++) { if (!offset(cell, 1, 1, d).isnull && offset(cell, 1, 1, d).qnum === 1 && isNotLight(offset(cell, 1, 2, d)) && isNotLight(offset(cell, 2, 1, d))) { add_dot(offset(cell, -1, 0, d)); add_dot(offset(cell, 0, -1, d)); } } } } } function MasyuAssist() { SingleLoopInCell({ isPass: c => c.qnum !== CQNUM.none, }); let isBlack = c => !c.isnull && c.qnum === CQNUM.bcir; let isWhite = c => !c.isnull && c.qnum === CQNUM.wcir; let isPathable = b => !b.isnull && b.qsub !== BQSUB.cross; for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; for (let d = 0; d < 4; d++) { // +×+ +×+ // ● ● -> ● ● // + + +×+ if (isBlack(offset(cell, -1, 0, d)) && isBlack(offset(cell, 1, 0, d)) && !isPathable(offset(cell, 0, -.5, d))) { add_cross(offset(cell, 0, .5, d)); } // + + +×+ // ━○ -> ━○━ // + + +×+ if (isWhite(cell) && (offset(cell, -.5, 0, d).line || !isPathable(offset(cell, 0, -.5, d)))) { add_line(offset(cell, -.5, 0, d)); add_line(offset(cell, +.5, 0, d)); add_cross(offset(cell, 0, -.5, d)); add_cross(offset(cell, 0, +.5, d)); } // + + + + + + + + // ━━━○━╸ -> ━━━○━╸× // + + + + + + + + if (isWhite(cell) && offset(cell, -.5, 0, d).line && offset(cell, -1.5, 0, d).line) { add_cross(offset(cell, 1.5, 0, d)); } // + + + + + +┃+ + // ━╸ ○ ○ -> ━╸×○×○ // + + + + + +┃+ + if (isWhite(cell) && (offset(cell, -1.5, 0, d).line || isWhite(offset(cell, -1, 0, d))) && (offset(cell, +1.5, 0, d).line || isWhite(offset(cell, +1, 0, d)))) { add_cross(offset(cell, -.5, 0, d)); add_cross(offset(cell, +.5, 0, d)); add_line(offset(cell, 0, -.5, d)); add_line(offset(cell, 0, +.5, d)); } // + + + : + + + : + +┃+ : + + + : + + + + + + // ━● : ●× : ● ╹ : ● ● : ● × -> ━●× // + + + ; + + + ; + + + ; + + + ; + + + + + + if (isBlack(cell) && (offset(cell, -.5, 0, d).line || !isPathable(offset(cell, .5, 0, d)) || offset(cell, 1, -.5, d).line || offset(cell, 1, .5, d).line || isBlack(offset(cell, 1, 0, d)) || !isPathable(offset(cell, 1.5, 0, d)))) { add_cross(offset(cell, .5, 0, d)); add_line(offset(cell, -.5, 0, d)); } // + + + + + + // ●━╸ -> ●━━━ // + + + + + + if (isBlack(cell) && offset(cell, .5, 0, d).line) { add_line(offset(cell, 1.5, 0, d)); } // + + + + + + + + + + // ● ○ ○ -> ━● ○ ○ // + + + + + + + + + + if (isBlack(cell) && isWhite(offset(cell, 2, 0, d)) && isWhite(offset(cell, 3, 0, d))) { add_line(offset(cell, -.5, 0, d)); } // + + + + + + + + // ○ ○ ○ ○ // + + + + -> + + + + // ● ● // + + + + + +┃+ + if (isBlack(cell) && isWhite(offset(cell, -1, -1, d)) && isWhite(offset(cell, 1, -1, d))) { add_line(offset(cell, 0, .5, d)); } } } } function SimpleloopAssist() { SingleLoopInCell({ isPassable: c => c.ques !== CQUES.bwall, isPass: c => c.ques !== CQUES.bwall, }); } function KoburinAssist() { SingleLoopInCell({ isPassable: c => c.qnum === CQNUM.none, isPass: c => c.qsub === CQSUB.dot, add_notpass: c => add_black(c, true), add_pass: add_dot, }); let isPathable = c => !c.isnull && c.qnum === CQNUM.none && c.qans !== CQANS.black; let isEmpty = c => !c.isnull && c.qnum === CQNUM.none && c.qans !== CQANS.black && c.qsub !== CQSUB.dot && c.lcnt === 0; for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; let adjcell = cell.adjacent; let adjline = cell.adjborder; // check clue if (cell.qnum >= 0) { let list = adjlist(cell.adjacent); if (list.filter(c => c.qans === CQANS.black).length === cell.qnum) { list.forEach(c => add_dot(c)); } if (list.filter(c => !c.isnull && c.qnum === CQNUM.none && c.qsub !== CQSUB.dot).length === cell.qnum) { list.forEach(c => add_black(c, true)); } } // add cross if (cell.qnum !== CQNUM.none) { fourside(add_cross, adjline); for (let d = 0; d < 4; d++) { // █ // 3 -> █3 // █ █ if (cell.qnum === 3 && ((b => b.isnull || b.qsub === BQSUB.cross)(offset(cell, 1.5, 1, d)) || (c => c.isnull || c.qans === CQANS.black || c.qnum !== CQNUM.none)(offset(cell, 2, 1, d)))) { add_black(offset(cell, -1, 0, d), true); add_black(offset(cell, 0, -1, d), true); } if (cell.qnum === 3 && ((b => b.isnull || b.qsub === BQSUB.cross)(offset(cell, 1.5, -1, d)) || (c => c.isnull || c.qans === CQANS.black || c.qnum !== CQNUM.none)(offset(cell, 2, -1, d)))) { add_black(offset(cell, -1, 0, d), true); add_black(offset(cell, 0, 1, d), true); } // · // 2 -> 2 // █ █ if (cell.qnum === 2 && ((b => b.isnull || b.qsub === BQSUB.cross)(offset(cell, 1.5, 1, d)) || (c => c.isnull || c.qans === CQANS.black || c.qnum !== CQNUM.none)(offset(cell, 2, 1, d)))) { add_dot(offset(cell, -1, -1, d)); } if (cell.qnum === 2 && ((b => b.isnull || b.qsub === BQSUB.cross)(offset(cell, 1.5, -1, d)) || (c => c.isnull || c.qans === CQANS.black || c.qnum !== CQNUM.none)(offset(cell, 2, -1, d)))) { add_dot(offset(cell, -1, 1, d)); } } continue; } // add dot around black if (cell.qans === CQANS.black) { fourside(add_cross, adjline); fourside(add_dot, adjcell); continue; } let emptycnt = 0; let linecnt = 0; fourside((b, c) => { if (isPathable(c) && b.qsub !== BQSUB.cross) { emptycnt++; } linecnt += b.line; }, adjline, adjcell); // no branch if (linecnt === 2) { fourside(add_cross, adjline); } // no deadend if (emptycnt <= 1) { add_black(cell, true); fourside(add_cross, adjline); fourside(add_dot, adjcell); } // 2 degree cell no deadend if (emptycnt === 2) { fourside((b, c) => { if (!isPathable(c) || b.qsub === BQSUB.cross) { return; } add_dot(c); }, adjline, adjcell); } } } function YajilinAssist() { SingleLoopInCell({ isPassable: c => c.qnum === CQNUM.none, isPass: c => c.qsub === CQSUB.dot, add_notpass: c => add_black(c, true), add_pass: add_dot, }); let isPathable = c => !c.isnull && c.qnum === CQNUM.none && c.qans !== CQANS.black; let isEmpty = c => !c.isnull && c.qnum === CQNUM.none && c.qans !== CQANS.black && c.qsub !== CQSUB.dot && c.lcnt === 0; for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; let adjcell = cell.adjacent; let adjline = cell.adjborder; // TODO: rewrite this // check clue if (cell.qnum >= 0 && cell.qdir !== QDIR.none) { let d = qdirremap(cell.qdir); let emptycnt = 0; let blackcnt = 0; let lastcell = cell; let pcell = dir(cell.adjacent, d); let emptycellList = []; let addcellList = []; while (!pcell.isnull && (pcell.qdir !== cell.qdir || pcell.qnum < 0)) { if (isEmpty(pcell)) { emptycnt++; emptycellList.push(pcell); } blackcnt += pcell.qans === CQANS.black; if (isEmpty(lastcell) && isEmpty(pcell)) { lastcell = cell; emptycnt--; } else { if (isEmpty(lastcell) && !isEmpty(pcell)) { addcellList.push(lastcell); } lastcell = pcell; } pcell = dir(pcell.adjacent, d); } if (isEmpty(lastcell)) { addcellList.push(lastcell); } if (!pcell.isnull) { blackcnt += pcell.qnum; } // finish clue if (emptycnt + blackcnt === cell.qnum) { addcellList.forEach(cell => add_black(cell, true)); } // finished clue if (blackcnt === cell.qnum) { emptycellList.forEach(cell => add_dot(cell)); } } // add cross if (cell.qnum !== CQNUM.none) { fourside(add_cross, adjline); continue; } // add dot around black if (cell.qans === CQANS.black) { fourside(add_cross, adjline); fourside(add_dot, adjcell); continue; } let emptycnt = 0; let linecnt = 0; fourside((b, c) => { if (isPathable(c) && b.qsub !== BQSUB.cross) { emptycnt++; } linecnt += b.line; }, adjline, adjcell); // no branch if (linecnt === 2) { fourside(add_cross, adjline); } // no deadend if (emptycnt <= 1) { add_black(cell); fourside(add_cross, adjline); fourside(add_dot, adjcell); } // 2 degree cell no deadend if (emptycnt === 2) { fourside((b, c) => { if (!isPathable(c) || b.qsub === BQSUB.cross) { return; } add_dot(c); }, adjline, adjcell); } } } function SlitherlinkAssist() { let add_bg_color = function (c, color) { if (c === undefined || c.isnull || c.qsub !== CQSUB.none || c.qsub === color) { return; } if (step && flg) { return; } flg = true; c.setQsub(color); c.draw(); } let add_bg_inner_color = function (c) { add_bg_color(c, CQSUB.green); } let add_bg_outer_color = function (c) { add_bg_color(c, CQSUB.yellow); } let isCross = b => b.isnull || b.qsub === BQSUB.cross let isLine = b => b.line let add_oneline = function (b1, b2) { if (b1.qsub === BQSUB.cross || b1.isnull || b2.line) { add_cross(b1); add_line(b2); } if (b2.qsub === BQSUB.cross || b2.isnull || b1.line) { add_cross(b2); add_line(b1); } } CellConnected({ isShaded: c => c.qsub === CQSUB.green, isUnshaded: c => c.qsub === CQSUB.yellow || c.qsub === CQSUB.none && c.qnum === 3, add_shaded: add_bg_inner_color, add_unshaded: add_bg_outer_color, isLinked: (c, nb, nc) => nb.qsub === BQSUB.cross, isNotPassable: (c, nb, nc) => nb.line, }); CellConnected({ isShaded: c => c.qsub === CQSUB.yellow, isUnshaded: c => c.qsub === CQSUB.green || c.qsub === CQSUB.none && c.qnum === 3, add_shaded: add_bg_outer_color, add_unshaded: add_bg_inner_color, isLinked: (c, nb, nc) => nb.qsub === BQSUB.cross, isNotPassable: (c, nb, nc) => nb.line, OutsideAsShaded: true, }); NoCheckerCell({ isShaded: c => !c.isnull && c.qsub === CQSUB.green, isUnshaded: c => c.isnull || c.qsub === CQSUB.yellow, add_shaded: add_bg_inner_color, add_unshaded: add_bg_outer_color, }); // counting this due to some small loop joke let twocnt = 0; let threecnt = 0; for (let i = 0; i < board.cell.length; i++) { twocnt += board.cell[i].qnum === 2; threecnt += board.cell[i].qnum === 3; } for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; let blist = adjlist(cell.adjborder); if (blist.filter(b => b.line).length === cell.qnum) { blist.forEach(b => add_cross(b)); } if (blist.filter(b => b.qsub !== BQSUB.cross).length === cell.qnum) { blist.forEach(b => add_line(b)); } // deduce single clue // 1 // 2·3· // 4c5 // ·6· let fn = function (c, b1, b2, b3, b4, b5, b6) { // × × // ×· · -> ×·×· // 1 ×1 // · · · · if (c.qnum === 1 && isCross(b1) && isCross(b2)) { add_cross(b3); add_cross(b4); } // × × // ×· · -> ×┏━╸ // 3 ┃3 // · · ╹ · if (c.qnum === 3 && isCross(b1) && isCross(b2)) { add_line(b3); add_line(b4); } // × × // ━╸ · ━╸ · // 1 -> 1× // · · ·×· if (c.qnum === 1 && (isCross(b1) && isLine(b2) || isLine(b1) && isCross(b2))) { add_cross(b5); add_cross(b6); } // × ┃ // · · ━╸ · ×╹ · // 1× -> 1× or 1× // ·×· ·×· ·×· if (c.qnum === 1 && isCross(b5) && isCross(b6)) { add_oneline(b1, b2); } // × // ━╸ · ━╸ ╻ // 3 -> 3┃ // · · ╺━┛ if (c.qnum === 3 && (isLine(b1) || isLine(b2))) { add_cross(b1); add_cross(b2); add_line(b5); add_line(b6); } // × ┃ // · ╻ ━╸ ╻ ×╹ ╻ // 3┃ -> 3┃ or 3┃ // ╺━┛ ╺━┛ ╺━┛ if (c.qnum === 3 && isLine(b5) && isLine(b6)) { add_oneline(b1, b2); } // × × // ×· ╻ ×·×╻ // 2┃ -> ×2┃ // · ╹ ╺━┛ if (c.qnum === 2 && isCross(b1) && isCross(b2) && (isLine(b5) || isLine(b6))) { add_cross(b3); add_cross(b4); add_line(b5); add_line(b6); } // × × // ×· · ×┏━╸ // 2× -> ┃2× // · · ╹×· if (c.qnum === 2 && isCross(b1) && isCross(b2) && (isCross(b5) || isCross(b6))) { add_line(b3); add_line(b4); add_cross(b5); add_cross(b6); } // × // ━╸ · ━╸ · // 2× -> 2× // · · ╺━╸ if (c.qnum === 2 && (isLine(b1) || isLine(b2)) && (isCross(b5) || isCross(b6))) { add_cross(b1); add_cross(b2); add_line(b5); add_line(b6); } // × ┃ // · · ━╸ · ×╹ · // 2× -> 2× or 2× // ╺━╸ ╺━╸ ╺━╸ if (c.qnum === 2 && (isLine(b5) && isCross(b6) || isCross(b5) && isLine(b6))) { add_oneline(b1, b2); } }; for (let d = 0; d < 4; d++) { fn(cell, offset(cell, -1, -.5, d), offset(cell, -.5, -1, d), offset(cell, 0, -.5, d), offset(cell, -.5, 0, d), offset(cell, .5, 0, d), offset(cell, 0, .5, d),); // · · · ┏━╸ · // 3 ┃3 // · · · -> ╹ · ╻ // 3 3┃ // · · · · ╺━┛ if (cell.qnum === 3 && offset(cell, 1, 1, d).qnum === 3) { add_line(offset(cell, 0, -.5, d)); add_line(offset(cell, -.5, 0, d)); add_line(offset(cell, 1.5, 1, d)); add_line(offset(cell, 1, 1.5, d)); } // × × × × // ×· · ×· ╺━ // 2 -> 2 // ━╸ · ━╸ · // × if (cell.qnum === 2 && isCross(offset(cell, -.5, -1, d)) && isCross(offset(cell, -1, -.5, d))) { add_oneline(offset(cell, .5, -1, d), offset(cell, 1, -.5, d)); add_oneline(offset(cell, -1, .5, d), offset(cell, -.5, 1, d)); } // × × // ×· · ×· · // 2 -> 2 // · ·× · ·× // × if (cell.qnum === 2 && isCross(offset(cell, -.5, -1, d)) && isCross(offset(cell, -1, -.5, d)) && (isCross(offset(cell, 1, .5, d)) || isCross(offset(cell, .5, 1, d)))) { add_cross(offset(cell, 1, .5, d)); add_cross(offset(cell, .5, 1, d)); } // × × // ×· · ×┏━╸ // 2 -> ┃2× // · ╺━ ╹×┏━ // ┃ if (cell.qnum === 2 && isCross(offset(cell, -.5, -1, d)) && isCross(offset(cell, -1, -.5, d)) && (isLine(offset(cell, 1, .5, d)) || isLine(offset(cell, .5, 1, d)))) { add_line(offset(cell, 0, -.5, d)); add_line(offset(cell, -.5, 0, d)); add_cross(offset(cell, .5, 0, d)); add_cross(offset(cell, 0, .5, d)); add_line(offset(cell, 1, .5, d)); add_line(offset(cell, .5, 1, d)); } // × // · · · ╻ ╻ ╻ // 3 3 -> ┃3┃3┃ // · · · ╹ ╹ ╹ // × if (cell.qnum === 3 && (threecnt > 2 || twocnt > 0) && offset(cell, 1, 0, d).qnum === 3) { add_line(offset(cell, -.5, 0, d)); add_line(offset(cell, .5, 0, d)); add_line(offset(cell, 1.5, 0, d)); add_cross(offset(cell, .5, -1, d)); add_cross(offset(cell, .5, 1, d)); } // × // · · · · · ╻ // ×2 3 -> ×2 3┃ // · · · · · ╹ // × if (cell.qnum === 2 && offset(cell, 1, 0, d).qnum === 3 && isCross(offset(cell, -.5, 0, d))) { add_line(offset(cell, 1.5, 0, d)); add_cross(offset(cell, .5, -1, d)); add_cross(offset(cell, .5, 1, d)); } // ·×· · ·×· · // ×1 ×1 // · · · -> · · · // 1 1× // · · · · ·×· if (cell.qnum === 1 && offset(cell, 1, 1, d).qnum === 1 && isCross(offset(cell, 0, -.5, d)) && isCross(offset(cell, -.5, 0, d))) { add_cross(offset(cell, 1.5, 1, d)); add_cross(offset(cell, 1, 1.5, d)); } } } // connectivity at cross for (let i = 0; i < board.cross.length; i++) { let cross = board.cross[i]; let blist = adjlist(cross.adjborder); let linecnt = blist.filter(b => b.line).length; let crosscnt = blist.filter(b => b.qsub === BQSUB.cross).length; if (linecnt === 2 || crosscnt === 3) { blist.forEach(b => add_cross(b)); } if (linecnt === 1 && crosscnt === 2) { blist.forEach(b => add_line(b)); } } // avoid forming multiple loop for (let i = 0; i < board.border.length; i++) { let border = board.border[i]; if (border.qsub === BQSUB.cross) { continue; } if (border.line) { continue; } let cr1 = border.sidecross[0]; let cr2 = border.sidecross[1]; if (cr1.path !== null && cr1.path === cr2.path && board.linegraph.components.length > 1) { add_cross(border); } } // deduce color for (let i = 0; i < board.cell.length; i++) { let cell = board.cell[i]; let adjline = cell.adjborder; let adjcell = cell.adjacent; // neighbor color { fourside((b, c) => { if (!c.isnull && cell.qsub !== CQSUB.none && cell.qsub === c.qsub) { add_cross(b); } if (cell.qsub === CQSUB.yellow && c.isnull) { add_cross(b); } if (!c.isnull && cell.qsub !== CQSUB.none && c.qsub !== CQSUB.none && cell.qsub !== c.qsub) { add_line(b); } if (cell.qsub === CQSUB.green && c.isnull) { add_line(b); } }, adjline, adjcell); } // deduce neighbor color if (cell.qsub === CQSUB.none) { fourside((b, c) => { if (b.line && c.isnull) { add_bg_inner_color(cell); } if (b.qsub === BQSUB.cross && c.isnull) { add_bg_outer_color(cell); } if (b.line && !c.isnull && c.qsub !== CQSUB.none) { add_bg_color(cell, CQSUB.green + CQSUB.yellow - c.qsub); } if (b.qsub === BQSUB.cross && !c.isnull && c.qsub !== CQSUB.none) { add_bg_color(cell, c.qsub); } }, adjline, adjcell); } { let innercnt = 0; let outercnt = 0; fourside(c => { if (!c.isnull && c.qsub === CQSUB.green) { innercnt++; } if (c.isnull || c.qsub === CQSUB.yellow) { outercnt++; } }, adjcell); // surrounded by green if (innercnt === 4) { add_bg_inner_color(cell); } // number and color deduce if (cell.qnum >= 0) { if (cell.qnum < innercnt || 4 - cell.qnum < outercnt) { add_bg_inner_color(cell); } if (cell.qnum < outercnt || 4 - cell.qnum < innercnt) { add_bg_outer_color(cell); } if (cell.qsub === CQSUB.green && cell.qnum === outercnt) { fourside(add_bg_inner_color, adjcell); } if (cell.qsub === CQSUB.yellow && cell.qnum === innercnt) { fourside(add_bg_outer_color, adjcell); } if (cell.qsub === CQSUB.yellow && cell.qnum === 4 - outercnt) { fourside(add_bg_inner_color, adjcell); } if (cell.qsub === CQSUB.green && cell.qnum === 4 - innercnt) { fourside(add_bg_outer_color, adjcell); } if (cell.qnum === 2 && outercnt === 2) { fourside(add_bg_inner_color, adjcell); } if (cell.qnum === 2 && innercnt === 2) { fourside(add_bg_outer_color, adjcell); } // 2 different color around 1 or 3 if ((cell.qnum === 1 || cell.qnum === 3) && innercnt === 1 && outercnt === 1) { fourside((c, d) => { if (!c.isnull && c.qsub === CQSUB.none) { if (cell.qnum === 1) { add_cross(d); } if (cell.qnum === 3) { add_line(d); } } }, adjcell, adjline); } // same diagonal color as 3 if (cell.qnum === 3 && cell.qsub !== CQSUB.none) { for (let d = 0; d < 4; d++) { if (!dir(adjcell, d).isnull && !dir(adjcell, d + 1).isnull && dir(dir(adjcell, d).adjacent, d + 1).qsub === cell.qsub) { add_line(dir(adjline, d + 2)); add_line(dir(adjline, d + 3)); } } } if (cell.qnum === 2) { // × // ×· · // 2 A // · ·a // Bb for (let d = 0; d < 4; d++) { let b1 = offset(cell, -.5, -1, d); let b2 = offset(cell, -1, -.5, d); if (!(b1.isnull || b1.qsub === BQSUB.cross)) { continue; } if (!(b2.isnull || b2.qsub === BQSUB.cross)) { continue; } let c1 = dir(adjcell, d + 2); let c2 = dir(adjcell, d + 3); // A=B add_bg_color(c1, (c2.isnull ? CQSUB.yellow : c2.qsub)); add_bg_color(c2, (c1.isnull ? CQSUB.yellow : c1.qsub)); } } } } } }
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址