preciousmouse / Light Up solution

// ==UserScript==
// @namespace       https://openuserjs.org/users/preciousmouse
// @name            Light Up solution
// @description     auto solve Light Up problem
// @author          preciousmouse
// @copyright       2020, preciousmouse (https://openuserjs.org/users/preciousmouse)
// @license         MIT
// @version         1.1
// @updateURL       https://openuserjs.org/meta/preciousmouse/Light_Up_solution.user.js
// @downloadURL     https://openuserjs.org/install/preciousmouse/Light_Up_solution.user.js
// @supportURL      https://github.com/puzzle-resolution/LightUp
// @require         https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.18.2/babel.js
// @require         https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/6.16.0/polyfill.js
// @require         http://requirejs.org/docs/release/2.1.5/comments/require.js
// @include         https://cn.puzzle-light-up.com/
// @include         https://cn.puzzle-light-up.com/?*
// @grant none
// ==/UserScript==

// ==OpenUserJS==
// @author preciousmouse
// ==/OpenUserJS==

console.log("light up solution. set 'window.useRobot = false' to disable auto submiting.")
"use strict";

function submitAnswer() {
    var t;
    null === (t = document.querySelector("#btnReady")) || void 0 === t || t.click();
}

window.useRobot = !0;

class LightUp {
    constructor(t) {
        this.graph = t, this.blackBlocks = this.initBlackBlocks(t);
    }
    initBlackBlocks(t) {
        let i = [];
        for (let s of t.keys()) for (let o of t[s].keys()) -1 !== t[s][o] && -2 !== t[s][o] && i.push({
            x: s,
            y: o,
            count: t[s][o]
        });
        return i;
    }
    initState(t) {
        let i = [], s = [];
        for (let o of t.keys()) {
            i[o] = [], s[o] = [];
            for (let e of t[o].keys()) -1 === t[o][e] ? (i[o][e] = 0, s[o][e] = -1) : (i[o][e] = -2, 
            s[o][e] = 0);
        }
        return [ i, s ];
    }
    cloneState(t) {
        return t.map((t => t.map((t => t))));
    }
    checkBoundary(t) {
        return !(t.x < 0 || t.x >= this.graph.length || t.y < 0 || t.y >= this.graph[0].length);
    }
    clonePosition(t) {
        return Object.assign({}, t);
    }
    leftPosition(t) {
        return {
            x: t.x,
            y: t.y - 1
        };
    }
    rightPosition(t) {
        return {
            x: t.x,
            y: t.y + 1
        };
    }
    topPosition(t) {
        return {
            x: t.x - 1,
            y: t.y
        };
    }
    bottomPosition(t) {
        return {
            x: t.x + 1,
            y: t.y
        };
    }
    listTRBL(t, i, s) {
        let o = [];
        for (let e = this.leftPosition(t); this.checkBoundary(e) && !i(this.clonePosition(e)); e = this.leftPosition(e)) s(this.clonePosition(e)) && o.push(this.clonePosition(e));
        for (let e = this.rightPosition(t); this.checkBoundary(e) && !i(this.clonePosition(e)); e = this.rightPosition(e)) s(this.clonePosition(e)) && o.push(this.clonePosition(e));
        for (let e = this.topPosition(t); this.checkBoundary(e) && !i(this.clonePosition(e)); e = this.topPosition(e)) s(this.clonePosition(e)) && o.push(this.clonePosition(e));
        for (let e = this.bottomPosition(t); this.checkBoundary(e) && !i(this.clonePosition(e)); e = this.bottomPosition(e)) s(this.clonePosition(e)) && o.push(this.clonePosition(e));
        return o;
    }
    listAccessableBlank(t, i) {
        if (!this.checkBoundary(i)) return [];
        const {blankState: s} = t;
        return this.listTRBL(i, (t => -1 !== this.graph[t.x][t.y]), (t => -1 === this.graph[t.x][t.y] && 0 === s[t.x][t.y]));
    }
    listUnlightBlank(t, i) {
        if (!this.checkBoundary(i)) return [];
        const {blankState: s} = t;
        return this.listTRBL(i, (t => -1 !== this.graph[t.x][t.y]), (t => -1 === this.graph[t.x][t.y] && 2 !== s[t.x][t.y] && 1 !== s[t.x][t.y]));
    }
    aroundTRBL(t, i) {
        let s = [ void 0, void 0, void 0, void 0 ];
        const o = this.topPosition(t);
        i(this.clonePosition(o)) && (s[0] = o);
        const e = this.rightPosition(t);
        i(this.clonePosition(e)) && (s[1] = e);
        const n = this.bottomPosition(t);
        i(this.clonePosition(n)) && (s[2] = n);
        const r = this.leftPosition(t);
        return i(this.clonePosition(r)) && (s[3] = r), s;
    }
    restBlankCanChoose(t, i) {
        if (!this.checkBoundary(i)) return [ void 0, void 0, void 0, void 0 ];
        if (-1 === this.graph[i.x][i.y]) return [ void 0, void 0, void 0, void 0 ];
        const {blankState: s} = t;
        return this.aroundTRBL(i, (t => this.checkBoundary(t) && -1 === this.graph[t.x][t.y] && 0 === s[t.x][t.y]));
    }
    restBlackTotal(t) {
        if (!this.checkBoundary(t)) return [ void 0, void 0, void 0, void 0 ];
        if (-1 === this.graph[t.x][t.y]) return [ void 0, void 0, void 0, void 0 ];
        return this.aroundTRBL(t, (t => this.checkBoundary(t) && -1 === this.graph[t.x][t.y]));
    }
    restBlackFinished(t, i) {
        if (!this.checkBoundary(i)) return [ void 0, void 0, void 0, void 0 ];
        if (-1 === this.graph[i.x][i.y]) return [ void 0, void 0, void 0, void 0 ];
        return this.aroundTRBL(i, (i => this.checkBoundary(i) && -1 === this.graph[i.x][i.y] && 1 === t.blankState[i.x][i.y]));
    }
    calcBlackState(t, i) {
        if (!this.checkBoundary(i)) return -1;
        return this.restBlackFinished(t, this.clonePosition(i)).filter((t => t)).length;
    }
    restBlackUnsolved(t, i) {
        if (!this.checkBoundary(i)) return [ void 0, void 0, void 0, void 0 ];
        const {blackState: s} = t;
        return this.aroundTRBL(i, (t => this.checkBoundary(t) && -1 !== this.graph[t.x][t.y] && -2 !== this.graph[t.x][t.y] && 0 !== this.graph[t.x][t.y] && s[t.x][t.y] !== this.graph[t.x][t.y]));
    }
    disablePosition(t, i, s) {
        const {x: o, y: e} = s;
        if (!this.checkBoundary(s)) return !1;
        if (-1 !== this.graph[o][e]) return !1;
        if (0 === t.blankState[o][e]) {
            t.blankState[o][e] = -1, this.appendToUpdateQueue(t, i, [ {
                x: o,
                y: e
            } ]);
            const s = this.restBlackUnsolved(t, {
                x: o,
                y: e
            });
            this.appendToUpdateQueue(t, i, s.filter((t => t)));
            const n = this.listUnlightBlank(t, {
                x: o,
                y: e
            });
            return this.appendToUpdateQueue(t, i, n), !0;
        }
        return !0;
    }
    lightPosition(t, i, s) {
        t.blankState[s.x][s.y] = 2;
        const o = this.restBlackUnsolved(t, s);
        this.appendToUpdateQueue(t, i, o.filter((t => t)));
        const e = this.listUnlightBlank(t, s);
        return this.appendToUpdateQueue(t, i, e), !0;
    }
    choosePosition(t, i, s) {
        const {x: o, y: e} = s;
        if (!this.checkBoundary(s)) return !1;
        if (-1 !== this.graph[o][e]) return !1;
        if (0 === t.blankState[o][e]) {
            t.blankState[o][e] = 1;
            const s = this.restBlackUnsolved(t, {
                x: o,
                y: e
            });
            this.appendToUpdateQueue(t, i, s.filter((t => t)));
            return this.listUnlightBlank(t, {
                x: o,
                y: e
            }).map((s => this.lightPosition(t, i, s))), !0;
        }
        return 1 === t.blankState[o][e];
    }
    updateEqual(t, i, s) {
        if (!this.checkBoundary(s)) return !1;
        const {x: o, y: e} = s;
        let {blankState: n, blackState: r} = t;
        switch (this.graph[o][e]) {
          case -1:
            if (0 === n[o][e]) {
                if (0 === this.listAccessableBlank(t, s).length && !this.choosePosition(t, i, s)) return !1;
            } else if (-1 === n[o][e]) {
                const o = this.listAccessableBlank(t, s);
                if (0 === o.length) return !1;
                if (1 === o.length && !this.choosePosition(t, i, this.clonePosition(o[0]))) return !1;
            }
            break;

          case -2:
            break;

          default:
            const h = this.calcBlackState(t, {
                x: o,
                y: e
            });
            if (r[o][e] = h, this.graph[o][e] < h) return !1;
            const a = this.graph[o][e] - h, l = this.restBlankCanChoose(t, s), c = l.filter((t => void 0 !== t)).length;
            if (c === a) {
                for (const s of l) if (s && !this.choosePosition(t, i, s)) return !1;
            } else {
                if (c < a) return !1;
                if (0 === a) this.disablePosition(t, i, this.leftPosition({
                    x: o,
                    y: e
                })), this.disablePosition(t, i, this.topPosition({
                    x: o,
                    y: e
                })), this.disablePosition(t, i, this.rightPosition({
                    x: o,
                    y: e
                })), this.disablePosition(t, i, this.bottomPosition({
                    x: o,
                    y: e
                })); else if (1 === a && 2 === c) {
                    const [s, n, r, h] = l;
                    if (void 0 !== s && void 0 !== r || void 0 !== n && void 0 !== h) ; else if (void 0 !== s && void 0 !== n) this.disablePosition(t, i, this.topPosition(this.rightPosition({
                        x: o,
                        y: e
                    }))); else if (void 0 !== n && void 0 !== r) this.disablePosition(t, i, this.rightPosition(this.bottomPosition({
                        x: o,
                        y: e
                    }))); else if (void 0 !== r && void 0 !== h) this.disablePosition(t, i, this.bottomPosition(this.leftPosition({
                        x: o,
                        y: e
                    }))); else {
                        if (void 0 === h || void 0 === s) throw new Error("assert 2");
                        this.disablePosition(t, i, this.leftPosition(this.topPosition({
                            x: o,
                            y: e
                        })));
                    }
                } else if (1 === a && 3 === c) ; else if (1 === a && 4 === c) ; else if (2 === a && 3 === c) {
                    const [s, n, r, h] = l;
                    if (void 0 === s) this.disablePosition(t, i, this.rightPosition(this.bottomPosition({
                        x: o,
                        y: e
                    }))), this.disablePosition(t, i, this.bottomPosition(this.leftPosition({
                        x: o,
                        y: e
                    }))); else if (void 0 === n) this.disablePosition(t, i, this.bottomPosition(this.leftPosition({
                        x: o,
                        y: e
                    }))), this.disablePosition(t, i, this.leftPosition(this.topPosition({
                        x: o,
                        y: e
                    }))); else if (void 0 === r) this.disablePosition(t, i, this.leftPosition(this.topPosition({
                        x: o,
                        y: e
                    }))), this.disablePosition(t, i, this.topPosition(this.rightPosition({
                        x: o,
                        y: e
                    }))); else {
                        if (void 0 !== h) throw new Error("assert 3");
                        this.disablePosition(t, i, this.topPosition(this.rightPosition({
                            x: o,
                            y: e
                        }))), this.disablePosition(t, i, this.rightPosition(this.bottomPosition({
                            x: o,
                            y: e
                        })));
                    }
                } else if (2 === a && 4 === c) ; else {
                    if (3 !== a || 4 !== c) throw new Error("assert 4");
                    this.disablePosition(t, i, {
                        x: o - 1,
                        y: e + 1
                    }), this.disablePosition(t, i, {
                        x: o + 1,
                        y: e + 1
                    }), this.disablePosition(t, i, {
                        x: o - 1,
                        y: e - 1
                    }), this.disablePosition(t, i, {
                        x: o + 1,
                        y: e - 1
                    });
                }
            }
        }
        return !0;
    }
    isSolvedPosition(t, i) {
        const {x: s, y: o} = i;
        switch (this.graph[s][o]) {
          case -1:
            return 1 === t.blankState[s][o] || 2 === t.blankState[s][o];

          case 0:
            return !0;

          default:
            return t.blackState[s][o] === this.graph[s][o];
        }
    }
    appendToUpdateQueue(t, i, s) {
        for (let {x: o, y: e} of s) {
            const s = `${o},${e}`;
            i.has(s) || (this.isSolvedPosition(t, {
                x: o,
                y: e
            }) || i.add(s));
        }
        return !0;
    }
    clearUpdateQueue(t, i) {
        try {
            for (;i.size > 0; ) {
                const s = [ ...i.keys() ][0];
                i.delete(s);
                const [o, e] = s.split(",").map((t => +t));
                if (!this.updateEqual(t, i, this.clonePosition({
                    x: o,
                    y: e
                }))) return !1;
            }
        } catch (t) {
            return !1;
        }
        return !0;
    }
    findNext(t) {
        const {blankState: i} = t;
        for (let t of this.graph.keys()) for (let s of this.graph[t].keys()) if (-1 === this.graph[t][s] && 2 !== i[t][s] && 1 !== i[t][s] && -1 !== i[t][s]) return {
            x: t,
            y: s
        };
        return !1;
    }
    verificate(t) {
        const {blackState: i, blankState: s} = t;
        for (let t of this.graph.keys()) for (let o of this.graph[t].keys()) if (-1 === this.graph[t][o]) {
            if (2 !== s[t][o] && 1 !== s[t][o]) return !1;
        } else if (-2 !== this.graph[t][o] && i[t][o] !== this.graph[t][o]) return !1;
        return !0;
    }
    getPositionCases(t, i) {
        const s = this.listAccessableBlank(t, i);
        return 0 === t.blankState[i.x][i.y] ? 0 !== s.length ? [ {
            position: this.clonePosition(i),
            choose: !0
        }, {
            position: this.clonePosition(i),
            choose: !1
        } ] : [ {
            position: this.clonePosition(i),
            choose: !0
        } ] : 0 !== s.length ? [ {
            position: this.clonePosition(i),
            choose: !1
        } ] : [];
    }
    applyPositionCase(t, i, s) {
        const {position: o, choose: e} = s;
        return e ? this.choosePosition(t, i, o) : this.disablePosition(t, i, o);
    }
    recu(t) {
        const {currentRecuIndex: i} = t;
        let s = new Set;
        if (0 == i) {
            for (let {x: i, y: o, count: e} of this.blackBlocks.values()) if (!this.updateEqual(t, s, {
                x: i,
                y: o
            })) return !1;
            if (this.clearUpdateQueue(t, s)) {
                const s = {
                    currentRecuIndex: i,
                    blankState: this.cloneState(t.blankState),
                    blackState: this.cloneState(t.blackState)
                };
                return this.recu(Object.assign(Object.assign({}, s), {
                    currentRecuIndex: s.currentRecuIndex + 1
                }));
            }
            return !1;
        }
        {
            const o = this.findNext(t);
            if (o) {
                const e = this.getPositionCases(t, o);
                if (0 == e.length) return !1;
                for (let o of e) {
                    const e = {
                        currentRecuIndex: i,
                        blankState: this.cloneState(t.blankState),
                        blackState: this.cloneState(t.blackState)
                    }, n = new Set(s);
                    if (this.applyPositionCase(e, n, o) && this.clearUpdateQueue(e, n)) {
                        const t = this.recu(Object.assign(Object.assign({}, e), {
                            currentRecuIndex: e.currentRecuIndex + 1
                        }));
                        if (t) return t;
                    }
                }
                return !1;
            }
            return !!this.verificate(t) && t;
        }
    }
    solve() {
        const [t, i] = this.initState(this.graph), s = this.recu({
            blankState: this.cloneState(t),
            blackState: this.cloneState(i),
            currentRecuIndex: 0
        });
        return this.answer = s ? this.generaterAnawer(s.blankState) : "failed";
    }
    generaterAnawer(t) {
        let i = [];
        for (let s of this.graph[0].keys()) for (let o of this.graph.keys()) 1 === t[o][s] && i.push({
            x: o,
            y: s
        });
        return i.map((t => `${t.y},${t.x}`)).join(";");
    }
}

(() => {
    const t = `\n\n        ${LightUp.toString()} \n        onmessage=${(t => {
        const i = JSON.parse(t.data), s = new LightUp(i).solve();
        postMessage(s);
    }).toString()}\n    `, i = task, s = Game.task;
    console.info("task", i, s);
    const o = new Date;
    !function registerWorkerWithBlob(t) {
        const {scriptStr: i, postMessageStr: s, onMessage: o} = t, e = new Blob([ i ], {
            type: "text/javascript"
        }), n = URL.createObjectURL(e), r = new Worker(n);
        r.onmessage = o, r.postMessage(s);
    }({
        scriptStr: t,
        postMessageStr: JSON.stringify(s),
        onMessage: t => {
            const i = new Date, s = t.data;
            console.info("answer", s), console.info("耗时", i.valueOf() - o.valueOf(), "ms"), 
            function replaceAnswer(t) {
                document && $ && ($("#puzzleForm").attr("onsubmit", `console.log('customer onsubmit');\n        Game.saveState();\n        Game.tickTimer();\n        this.jstimerPersonal.value = Game.getTimer();\n        this.ansH.value = '${t}';`), 
                $("#btnReady").off("click"), window.useRobot && $("#robot").attr("value", "1"));
            }(s), window.submit = submitAnswer, window.useRobot && submitAnswer();
        }
    });
})();