NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @namespace https://openuserjs.org/users/preciousmouse // @name Pipe solution // @description auto solve Pipe problem // @author preciousmouse // @copyright 2020, preciousmouse (https://openuserjs.org/users/preciousmouse) // @license MIT // @version 1.4 // @updateURL https://openuserjs.org/meta/preciousmouse/Pipe_solution.user.js // @downloadURL https://openuserjs.org/install/preciousmouse/Pipe_solution.user.js // @supportURL https://github.com/puzzle-resolution/Pipe // @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-pipes.com/ // @include https://cn.puzzle-pipes.com/?* // @grant none // ==/UserScript== // ==OpenUserJS== // @author preciousmouse // ==/OpenUserJS== "use strict"; function submitAnswer() { var t; null === (t = document.querySelector("#btnReady")) || void 0 === t || t.click(); } try { window.useRobot = !0; } catch (t) {} class Pipe { constructor(t, i) { this.initState = t => ({ blockState: t.map((t => t.map((t => ({ sharp: this.getBlockType(t), status: {}, locked: !1 }))))) }), this.getBlockType = t => { const i = !!(1 & t), o = !!(2 & t), s = !!(4 & t), e = !!(8 & t), n = [ o, s, e, i ].filter((t => t)).length; if (n < 1 || n > 3) throw new Error(`BlockType assert: ${t}`); return { 1: "Sharp1", 2: o && e || s && i ? "Sharp2" : "Sharp3", 3: "Sharp4" }[n]; }, this.clonePosition = t => Object.assign({}, t), this.checkPositionEqual = (t, i) => t.x === i.x && t.y === i.y, this.topPosition = t => this.clonePosition({ x: 0 == t.x ? this.graph.length - 1 : t.x - 1, y: t.y }), this.bottomPosition = t => this.clonePosition({ x: t.x == this.graph.length - 1 ? 0 : t.x + 1, y: t.y }), this.leftPosition = t => this.clonePosition({ x: t.x, y: 0 == t.y ? this.graph[0].length - 1 : t.y - 1 }), this.rightPosition = t => this.clonePosition({ x: t.x, y: t.y == this.graph[0].length - 1 ? 0 : t.y + 1 }), this.oppositeDirection = t => { const i = [ "up", "left", "down", "right" ]; return i[(i.indexOf(t) + 2) % 4]; }, this.directionPositionReducer = t => { switch (t) { case "up": return this.topPosition; case "down": return this.bottomPosition; case "left": return this.leftPosition; case "right": return this.rightPosition; default: throw new Error("direction param illegal assert"); } }, this.cloneState = function(t) { return t.map((t => t.map((t => JSON.parse(JSON.stringify(t)))))); }, this.checkBoundary = t => !(t.x < 0 || t.x >= this.graph.length || t.y < 0 || t.y >= this.graph[0].length), this.lockPointDirectionStatus = (t, i, o, s, e) => { const n = this.directionPositionReducer(t)(s), r = this.oppositeDirection(t), h = i.blockState[s.x][s.y].status[t], a = i.blockState[n.x][n.y].status[r]; return (void 0 === h || void 0 === a || h === a) && (!(void 0 !== h && h !== e || void 0 !== a && a !== e) && (void 0 === h && (i.blockState[s.x][s.y].status[t] = e, this.appendToUpdateQueue(i, o, [ this.clonePosition(s) ])), void 0 === a && (i.blockState[n.x][n.y].status[r] = e, this.appendToUpdateQueue(i, o, [ this.clonePosition(n) ])), !0)); }, this.lockPointUpStatus = this.lockPointDirectionStatus.bind(null, "up"), this.lockPointDownStatus = this.lockPointDirectionStatus.bind(null, "down"), this.lockPointLeftStatus = this.lockPointDirectionStatus.bind(null, "left"), this.lockPointRightStatus = this.lockPointDirectionStatus.bind(null, "right"), this.applyNeighborLink = (t, i, o, s, e) => { if (this.checkPositionEqual(this.topPosition(i), o)) return this.lockPointUpStatus(s, e, i, t); if (this.checkPositionEqual(this.bottomPosition(i), o)) return this.lockPointDownStatus(s, e, i, t); if (this.checkPositionEqual(this.leftPosition(i), o)) return this.lockPointLeftStatus(s, e, i, t); if (this.checkPositionEqual(this.rightPosition(i), o)) return this.lockPointRightStatus(s, e, i, t); throw new Error("Nodes are not adjacent assert"); }, this.checkNeighborLink = this.applyNeighborLink.bind(null, !0), this.disableNeighborLink = this.applyNeighborLink.bind(null, !1), this.isSolvedPosition = (t, i) => t.blockState[i.x][i.y].locked, this.getDirectionLink = (t, i, o) => { const s = t.blockState, e = s[i.x][i.y].status; if (s[o.x][o.y].status, this.checkPositionEqual(this.topPosition(i), o)) return e.up; if (this.checkPositionEqual(this.bottomPosition(i), o)) return e.down; if (this.checkPositionEqual(this.leftPosition(i), o)) return e.left; if (this.checkPositionEqual(this.rightPosition(i), o)) return e.right; throw new Error("Nodes are not adjacent assert"); }, this.checkDirectionLink = (t, i, o) => { const check = (t, i) => void 0 === t || void 0 === i || t === i, s = t.blockState, e = s[i.x][i.y].status, n = s[o.x][o.y].status; if (this.checkPositionEqual(this.topPosition(i), o)) return check(e.up, n.down); if (this.checkPositionEqual(this.bottomPosition(i), o)) return check(e.down, n.up); if (this.checkPositionEqual(this.leftPosition(i), o)) return check(e.left, n.right); if (this.checkPositionEqual(this.rightPosition(i), o)) return check(e.right, n.left); throw new Error("Nodes are not adjacent assert"); }, this.asynDirectionLink = (t, i, o) => { const {status: {up: s, down: e, left: n, right: r}} = t.blockState[o.x][o.y], h = this.topPosition(o), a = t.blockState[h.x][h.y], l = this.bottomPosition(o), c = t.blockState[l.x][l.y], u = this.leftPosition(o), f = t.blockState[u.x][u.y], p = this.rightPosition(o), d = t.blockState[p.x][p.y]; if (void 0 !== a.status.down) if (void 0 === s) { if (!this.lockPointUpStatus(t, i, o, a.status.down)) return !1; } else if (s !== a.status.down) return !1; if (void 0 !== c.status.up) if (void 0 === e) { if (!this.lockPointDownStatus(t, i, o, c.status.up)) return !1; } else if (e !== c.status.up) return !1; if (void 0 !== f.status.right) if (void 0 === n) { if (!this.lockPointLeftStatus(t, i, o, f.status.right)) return !1; } else if (n !== f.status.right) return !1; if (void 0 !== d.status.left) if (void 0 === r) { if (!this.lockPointRightStatus(t, i, o, d.status.left)) return !1; } else if (r !== d.status.left) return !1; return !0; }, this.updatePoint = (t, i, o) => { if (this.isSolvedPosition(t, o)) return !0; const {blockState: s} = t, {x: e, y: n} = o, r = s[e][n], {sharp: h, status: {up: a, down: l, left: c, right: u}} = r, f = { Sharp1: 1, Sharp2: 2, Sharp3: 2, Sharp4: 3 }[h], p = [ a, c, l, u ].filter((t => !0 === t)).length, d = [ a, c, l, u ].filter((t => !1 === t)).length; if (p > f || d > 4 - f) return !1; if (p === f) { if ("Sharp2" === h && (void 0 !== a && void 0 !== l && a !== l || void 0 !== c && void 0 !== u && c !== u)) return !1; if ("Sharp3" === h && (a && l || c && u)) return !1; if (void 0 === a && !this.lockPointUpStatus(t, i, o, !1)) return !1; if (void 0 === l && !this.lockPointDownStatus(t, i, o, !1)) return !1; if (void 0 === c && !this.lockPointLeftStatus(t, i, o, !1)) return !1; if (void 0 === u && !this.lockPointRightStatus(t, i, o, !1)) return !1; s[e][n].locked = !0; } else if (d === 4 - f) { if ("Sharp2" === h && (!1 === a != (!1 === l) || !1 === c != (!1 === u))) return !1; if ("Sharp3" === h && (!1 === a && !1 === l || !1 === c && !1 === u)) return !1; if (void 0 === a && !this.lockPointUpStatus(t, i, o, !0)) return !1; if (void 0 === l && !this.lockPointDownStatus(t, i, o, !0)) return !1; if (void 0 === c && !this.lockPointLeftStatus(t, i, o, !0)) return !1; if (void 0 === u && !this.lockPointRightStatus(t, i, o, !0)) return !1; s[e][n].locked = !0; } else { const r = [ "up", "left", "down", "right" ]; if ("Sharp1" === h) ; else if ("Sharp2" === h) { if (1 == p) { const h = [ a, c, l, u ].findIndex((t => !0 === t)); if (!this.lockPointDirectionStatus(r[(h + 2) % 4], t, i, o, !0)) return !1; if (!this.lockPointDirectionStatus(r[(h + 1) % 4], t, i, o, !1)) return !1; if (!this.lockPointDirectionStatus(r[(h + 3) % 4], t, i, o, !1)) return !1; s[e][n].locked = !0; } else if (1 == d) { const h = [ a, c, l, u ].findIndex((t => !1 === t)); if (!this.lockPointDirectionStatus(r[(h + 2) % 4], t, i, o, !1)) return !1; if (!this.lockPointDirectionStatus(r[(h + 1) % 4], t, i, o, !0)) return !1; if (!this.lockPointDirectionStatus(r[(h + 3) % 4], t, i, o, !0)) return !1; s[e][n].locked = !0; } } else if ("Sharp3" === h) { if (1 == d) { const s = [ a, c, l, u ].findIndex((t => !1 === t)); if (!this.lockPointDirectionStatus(r[(s + 2) % 4], t, i, o, !0)) return !1; } if (1 == p) { const s = [ a, c, l, u ].findIndex((t => !0 === t)); if (!this.lockPointDirectionStatus(r[(s + 2) % 4], t, i, o, !1)) return !1; } } } return !0; }, this.updateAroudState = (t, i) => { for (let o of this.graph.keys()) this.lockPointLeftStatus(t, i, { x: o, y: 0 }, !1), this.lockPointRightStatus(t, i, { x: o, y: this.graph[0].length - 1 }, !1); for (let o of this.graph[0].keys()) this.lockPointUpStatus(t, i, { x: 0, y: o }, !1), this.lockPointDownStatus(t, i, { x: this.graph.length - 1, y: o }, !1); return !0; }, this.updateCutInCases = (t, i) => { const {blockState: o} = t; for (let s of this.graph.keys()) for (let e of this.graph[0].keys()) if ("Sharp1" === o[s][e].sharp) this.aroundTRBL({ x: s, y: e }, (t => "Sharp1" === o[t.x][t.y].sharp)).map((o => { if (o && !this.disableNeighborLink({ x: s, y: e }, o, t, i)) return !1; })); else if ("Sharp2" === o[s][e].sharp) { const testIsDirectToSharp1 = t => { const i = this.directionPositionReducer(t), n = { x: s, y: e }; let r = this.clonePosition(n); for (;;) { if (r = i(r), this.checkPositionEqual(n, r)) return !0; const t = o[r.x][r.y].sharp; if ("Sharp1" === t) return !0; if ("Sharp2" !== t) return !1; } }; if (testIsDirectToSharp1("up") && testIsDirectToSharp1("down")) { if (!this.lockPointUpStatus(t, i, { x: s, y: e }, !1)) return !1; } else if (testIsDirectToSharp1("left") && testIsDirectToSharp1("right") && !this.lockPointLeftStatus(t, i, { x: s, y: e }, !1)) return !1; } return !0; }, this.updateCases = t => { const i = t.blockState; for (let o of this.graph.keys()) for (let s of this.graph[0].keys()) if (!i[o][s].locked) { const e = { x: o, y: s }, n = this.generateSet(t, this.clonePosition(e)), r = this.aroundTRBL(e).filter((t => t && !i[t.x][t.y].locked)); for (let i of r) if (n.exportPoints.has(`${i.x}_${i.y}`)) { let o = new Set; if (!this.disableNeighborLink(e, i, t, o)) return !1; if (o.size > 0) return !!this.clearUpdateQueue(t, o) && 1; } if (1 == n.exportPoints.size) { const [i, o] = [ ...n.exportPoints.values() ][0].split("_"); let s = { x: +i, y: +o }; if (this.checkPositionEqual(e, s)) for (let i of r) { if (void 0 !== this.getDirectionLink(t, e, i)) continue; const o = this.generateSet(t, this.clonePosition(i)), s = `${this.floorEntry.x}_${this.floorEntry.y}`; if (1 === n.exportPoints.size && 1 === o.exportPoints.size && !n.lockedPoints.has(s) && !o.lockedPoints.has(s)) { const positionRest = (t, i) => { const o = t.blockState[i.x][i.y], s = { Sharp1: 1, Sharp2: 2, Sharp3: 2, Sharp4: 3 }, e = this.topPosition(i), n = this.bottomPosition(i), r = this.leftPosition(i), h = this.rightPosition(i); let a = 0; return o.status.up && t.blockState[e.x][e.y].locked && a++, o.status.down && t.blockState[n.x][n.y].locked && a++, o.status.left && t.blockState[r.x][r.y].locked && a++, o.status.right && t.blockState[h.x][h.y].locked && a++, s[o.sharp] - a; }; if (1 === positionRest(t, e) && 1 === positionRest(t, i)) { let o = new Set; if (!this.disableNeighborLink(e, i, t, o)) return !1; if (o.size > 0) return !!this.clearUpdateQueue(t, o) && 1; } } } } } return 2; }, this.findNext = t => { for (let i of this.graph.keys()) for (let o of this.graph[0].keys()) if (!t.blockState[i][o].locked) return { x: i, y: o }; return !1; }, this.getCases = (t, i) => { const {x: o, y: s} = i, e = t.blockState[o][s]; if (e.locked) return []; const {sharp: n, status: {up: r, down: h, left: a, right: l}} = e, c = [ r, a, h, l ].filter((t => !0 === t)).length, u = [ r, a, h, l ].filter((t => !1 === t)).length; if ("Sharp1" === n) { if (c >= 1) return []; let t = []; return void 0 === r && t.push({ position: this.clonePosition(i), directions: [ "up" ] }), void 0 === h && t.push({ position: this.clonePosition(i), directions: [ "down" ] }), void 0 === a && t.push({ position: this.clonePosition(i), directions: [ "left" ] }), void 0 === l && t.push({ position: this.clonePosition(i), directions: [ "right" ] }), t; } if ("Sharp2" === n) { if (c >= 1 || u >= 1) return []; let t = []; return t.push({ position: this.clonePosition(i), directions: [ "up", "down" ] }), t.push({ position: this.clonePosition(i), directions: [ "left", "right" ] }), t; } if ("Sharp3" === n) { if (c >= 2 || u >= 2) return []; let t = []; return void 0 === r || void 0 === h ? void 0 === a && void 0 === l ? (t.push({ position: this.clonePosition(i), directions: [ "up", "left" ] }), t.push({ position: this.clonePosition(i), directions: [ "down", "left" ] }), t.push({ position: this.clonePosition(i), directions: [ "up", "right" ] }), t.push({ position: this.clonePosition(i), directions: [ "down", "right" ] })) : (void 0 === r && t.push({ position: this.clonePosition(i), directions: [ "up" ] }), void 0 === h && t.push({ position: this.clonePosition(i), directions: [ "down" ] })) : (void 0 === a && t.push({ position: this.clonePosition(i), directions: [ "left" ] }), void 0 === l && t.push({ position: this.clonePosition(i), directions: [ "right" ] })), t; } if ("Sharp4" === n) { if (c >= 3 || u >= 1) return []; let t = []; return void 0 === r && t.push({ position: this.clonePosition(i), directions: [ "up" ] }), void 0 === h && t.push({ position: this.clonePosition(i), directions: [ "down" ] }), void 0 === a && t.push({ position: this.clonePosition(i), directions: [ "left" ] }), void 0 === l && t.push({ position: this.clonePosition(i), directions: [ "right" ] }), t; } return []; }, this.applyCase = (t, i) => { const {position: o, directions: s} = i; let e = new Set; for (let i of s) if (!this.lockPointDirectionStatus(i, t, e, o, !0)) return !1; return !!this.clearUpdateQueue(t, e); }, this.recu = t => { for (;;) { const i = this.updateCases(t); if (!1 === i) return !1; if (2 === i) { if (this.verificate(t)) return t; break; } 0; } const i = this.findNext(t); if (!i) return !!this.verificate(t) && t; { const o = this.getCases(t, i); for (let i of o) { const o = { blockState: this.cloneState(t.blockState), currentRecuIndex: t.currentRecuIndex + 1 }; if (this.applyCase(o, i)) { const t = this.recu(o); if (t) return t; } } } return !1; }, this.solve = () => { const {blockState: t} = this.initState(this.graph); let i = { blockState: t, currentRecuIndex: 0 }, o = new Set; return this.isCrossMap || this.updateAroudState(i, o), this.updateCutInCases(i, o), this.clearUpdateQueue(i, o), this.verificate(i) || (i = this.recu({ blockState: this.cloneState(t), currentRecuIndex: 0 })), this.answer = i ? this.generaterAnawer(i.blockState) : "failed"; }, this.generaterAnawer = t => { const calcStatusNumber = t => { const {up: i, down: o, left: s, right: e} = t; let n = 0; return e && (n |= 1), i && (n |= 2), s && (n |= 4), o && (n |= 8), n; }, getBlockAnawerStatus = (t, i) => { const next = t => 8 * +!!(4 & t) + 4 * +!!(2 & t) + 2 * +!!(1 & t) + 1 * +!!(8 & t); let o = 0, s = t; for (;s != i; ) if (s = next(s), o++, o > 3) throw new Error(`next cnt limit excceded: ${t},${i},${o}>3`); return o % 4; }; let i = ""; for (let o of this.graph.keys()) for (let s of this.graph[0].keys()) { const e = getBlockAnawerStatus(this.graph[o][s], calcStatusNumber(t[o][s].status)); i += "Sharp2" == this.getBlockType(this.graph[o][s]) ? { 0: 0, 1: 1, 2: 0, 3: 1 }[e] : e; } return i += ":", i += Array(this.graph.length * this.graph[0].length).fill(1).join(""), i; }, this.generateSet = (t, i) => { const o = t.blockState; let s = new Set, e = new Set; const formatPoint = t => `${t.x}_${t.y}`, formatEdge = (t, i) => { const [o, s] = t.x < i.x ? [ t, i ] : t.x > i.x ? [ i, t ] : t.y < i.y ? [ t, i ] : [ i, t ]; return `${formatPoint(o)},${formatPoint(s)}`; }; let n = [ i ]; do { const i = n.shift(), r = formatPoint(i); if (!s.has(r)) { s.add(r); const addPoint = t => { n.push(t); const o = formatEdge(i, t); e.has(o) || e.add(o); }, {status: {up: h, down: a, left: l, right: c}} = o[i.x][i.y]; h && this.isSolvedPosition(t, this.topPosition(i)) && addPoint(this.topPosition(i)), a && this.isSolvedPosition(t, this.bottomPosition(i)) && addPoint(this.bottomPosition(i)), l && this.isSolvedPosition(t, this.leftPosition(i)) && addPoint(this.leftPosition(i)), c && this.isSolvedPosition(t, this.rightPosition(i)) && addPoint(this.rightPosition(i)); } } while (n.length > 0); let r = !1; if (e.size >= s.size) r = !0; else { r = (() => { let t = {}, i = []; for (let i of s.values()) t[i] = i; for (let t of e.values()) { const [o, s] = t.split(","); i.push({ p: o, q: s }); } const find = i => { let o = i; for (;i != t[i]; ) i = t[i]; for (;o != t[o]; ) { let s = o; o = t[o], t[s] = i; } return i; }, union = (i, o) => { const s = find(i); find(o), t[i] = t[o] = s; }; for (let o of i) { const {p: i, q: s} = o; if (t[i] == t[s]) return !0; union(i, s); } return !1; })(); } let h = new Set, a = new Set; for (let t of s.values()) { const [i, s] = t.split("_"); o[+i][+s].locked ? a.add(t) : h.add(t); } for (let t of a) { const [i, s] = t.split("_"), e = +i, n = +s, {status: {up: r, down: a, left: l, right: c}} = o[e][n], u = this.topPosition({ x: e, y: n }), f = this.bottomPosition({ x: e, y: n }), p = this.leftPosition({ x: e, y: n }), d = this.rightPosition({ x: e, y: n }); r && !o[u.x][u.y].locked && h.add(formatPoint(u)), a && !o[f.x][f.y].locked && h.add(formatPoint(f)), l && !o[p.x][p.y].locked && h.add(formatPoint(p)), c && !o[d.x][d.y].locked && h.add(formatPoint(d)); } return { lockedPoints: a, edges: e, hasLoop: r, exportPoints: h }; }, this.graph = t, this.isCrossMap = i, this.floorEntry = { x: Math.floor(t.length / 2), y: Math.floor(t[0].length / 2) }; } appendToUpdateQueue(t, i, o) { for (let {x: s, y: e} of o) { const o = `${s},${e}`; i.has(o) || (this.isSolvedPosition(t, this.clonePosition({ x: s, y: e })) || i.add(o)); } return !0; } clearUpdateQueue(t, i) { try { for (;i.size > 0; ) { const o = [ ...i.keys() ][0]; i.delete(o); const [s, e] = o.split(",").map((t => +t)); if (!this.updatePoint(t, i, this.clonePosition({ x: s, y: e }))) return !1; } } catch (t) { return !1; } return !0; } aroundTRBL(t, i = (() => !0)) { let o = [ void 0, void 0, void 0, void 0 ]; const s = this.topPosition(t); i(this.clonePosition(s)) && (o[0] = s); const e = this.rightPosition(t); i(this.clonePosition(e)) && (o[1] = e); const n = this.bottomPosition(t); i(this.clonePosition(n)) && (o[2] = n); const r = this.leftPosition(t); return i(this.clonePosition(r)) && (o[3] = r), o; } verificate(t) { const i = this.generateSet(t, { x: 0, y: 0 }); if (i.lockedPoints.size !== this.graph.length * this.graph[0].length) return !1; if (i.hasLoop) return !1; for (let i of this.graph.keys()) for (let o of this.graph[0].keys()) { const {blockState: s} = t, e = s[i][o].status, n = this.rightPosition({ x: i, y: o }), r = s[n.x][n.y].status, h = this.bottomPosition({ x: i, y: o }), a = s[h.x][h.y].status; if (e.right !== r.left || e.down !== a.up) return !1; } return !0; } } (() => { const t = `\n\n ${Pipe.toString()} \n onmessage=${(t => { const {tasks: i, isCrossMap: o} = JSON.parse(t.data), s = new Pipe(i, o).solve(); postMessage(s); }).toString()}\n `, i = [ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 ].includes(Game.plSize), o = task, s = Game.task; console.info("task", o, s); const e = new Date; !function registerWorkerWithBlob(t) { const {scriptStr: i, postMessageStr: o, onMessage: s} = t, e = new Blob([ i ], { type: "text/javascript" }), n = URL.createObjectURL(e), r = new Worker(n); r.onmessage = s, r.postMessage(o); }({ scriptStr: t, postMessageStr: JSON.stringify({ tasks: s, isCrossMap: i }), onMessage: t => { const i = new Date, o = t.data; console.info("answer", o), console.info("耗时", i.valueOf() - e.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")); }(o), window.submit = submitAnswer, window.useRobot && submitAnswer(); } }); })()