+
diff --git a/src/pzpr/variety.js b/src/pzpr/variety.js
index e8c9a2c51..3addcffcf 100755
--- a/src/pzpr/variety.js
+++ b/src/pzpr/variety.js
@@ -278,6 +278,7 @@
lixloop: [0, 0, "LIX Loop", "LIX Loop", "yajilin"],
lohkous: [0, 0, "Lohkous", "Lohkous"],
lollipops: [0, 0, "ペロペロキャンディ", "Lollipops"],
+ longest: [0, 0, "短辺消失", "The Longest"],
lookair: [0, 0, "るっくえあ", "Look-Air"],
loopsp: [1, 0, "環状線スペシャル", "Loop Special", "pipelink"],
loute: [0, 0, "エルート", "L-route"],
diff --git a/src/res/failcode.en.json b/src/res/failcode.en.json
index a2594aacc..21d522de5 100644
--- a/src/res/failcode.en.json
+++ b/src/res/failcode.en.json
@@ -70,9 +70,11 @@
"bdDeadEnd": "There is a dead-end line.",
"bdDoorsGt.oneroom": "Two rooms have more than one door between them.",
"bdEqual.antmill": "A square clue does not overlap two cells of the same color.",
+ "bdGivenUnused.longest": "A given line is not part of the longest border of an adjacent block.",
"bdIgnoreBP.bdblock": "A point has no line.",
"bdIgnoreBP.kramma": "A point has no line.",
"bdNotChassis.shwolf": "A line doesn't connect to the chassis.",
+ "bdNotGiven.longest": "A region's longest border uses non-given lines.",
"bdPassStar.nuriuzu": "A star overlaps a shaded cell.",
"bdPassStar.tentaisho": "A line goes over a star.",
"bdPierceGt1.blindrush": "A network of walls is pierced more than once.",
@@ -195,6 +197,7 @@
"bkNotAllMUPassed.dotchi2": "A line doesn't pass all white pearls.",
"bkNotAllMUPassed.moonsun": "A line doesn't pass all of the marks of the sun.",
"bkNotClosed.balloon": "A balloon is not enclosed by borders.",
+ "bkNotClosed.longest": "A region is not enclosed by borders.",
"bkNotHRect.tatamibari": "A tatami is not a horizontally long rectangle.",
"bkNotLshape.loute": "An area is not L-shaped or its width is not 1.",
"bkNotLshape3.shikaku": "An area whose size is a multiple of three is not L-shaped.",
diff --git a/src/variety-common/Encode.js b/src/variety-common/Encode.js
index 0f725e333..b15a51fcb 100644
--- a/src/variety-common/Encode.js
+++ b/src/variety-common/Encode.js
@@ -671,6 +671,59 @@ pzpr.classmgr.makeCommon({
this.outbstr += cm;
},
+ decodeOuterBorder: function() {
+ var bstr = this.outbstr,
+ bd = this.board,
+ twi = [16, 8, 4, 2, 1];
+ var pos = bstr
+ ? Math.min(((bd.border.length + 4) / 5) | 0, bstr.length)
+ : 0,
+ id = 0;
+ for (var i = 0; i < pos; i++) {
+ var ca = parseInt(bstr.charAt(i), 32);
+ for (var w = 0; w < 5; w++) {
+ if (!!bd.border[id]) {
+ bd.border[id].ques = ca & twi[w] ? 1 : 0;
+ id++;
+ }
+ }
+ }
+ this.outbstr = bstr.substr(pos);
+ },
+ encodeOuterBorder: function() {
+ var hasoutside = 0,
+ bd = this.board;
+ var bdinside = 2 * bd.cols * bd.rows - bd.cols - bd.rows;
+ for (var id = bdinside; id < bd.border.length && !hasoutside; id++) {
+ if (bd.border[id].isLineNG()) {
+ hasoutside = true;
+ }
+ }
+ var num = 0,
+ pass = 0,
+ cm = "",
+ twi = [16, 8, 4, 2, 1];
+ for (
+ var id = 0, max = !hasoutside ? bdinside : bd.border.length;
+ id < max;
+ id++
+ ) {
+ if (bd.border[id].isLineNG()) {
+ pass += twi[num];
+ }
+ num++;
+ if (num === 5) {
+ cm += pass.toString(32);
+ num = 0;
+ pass = 0;
+ }
+ }
+ if (num > 0) {
+ cm += pass.toString(32);
+ }
+ this.outbstr += cm;
+ },
+
//---------------------------------------------------------------------------
// enc.decodeCrossMark() 黒点をデコードする
// enc.encodeCrossMark() 黒点をエンコードする
diff --git a/src/variety/cbblock.js b/src/variety/cbblock.js
index e61887e75..86d7d86ea 100644
--- a/src/variety/cbblock.js
+++ b/src/variety/cbblock.js
@@ -463,52 +463,10 @@
// URLエンコード/デコード処理
"Encode@cbblock": {
decodePzpr: function(type) {
- this.decodeCBBlock();
+ this.decodeOuterBorder();
},
encodePzpr: function(type) {
- this.encodeCBBlock();
- },
-
- decodeCBBlock: function() {
- var bstr = this.outbstr,
- bd = this.board,
- twi = [16, 8, 4, 2, 1];
- var pos = bstr
- ? Math.min(((bd.border.length + 4) / 5) | 0, bstr.length)
- : 0,
- id = 0;
- for (var i = 0; i < pos; i++) {
- var ca = parseInt(bstr.charAt(i), 32);
- for (var w = 0; w < 5; w++) {
- if (!!bd.border[id]) {
- bd.border[id].ques = ca & twi[w] ? 1 : 0;
- id++;
- }
- }
- }
- this.outbstr = bstr.substr(pos);
- },
- encodeCBBlock: function() {
- var num = 0,
- pass = 0,
- cm = "",
- bd = this.board,
- twi = [16, 8, 4, 2, 1];
- for (var id = 0, max = bd.border.length; id < max; id++) {
- if (bd.border[id].isGround()) {
- pass += twi[num];
- }
- num++;
- if (num === 5) {
- cm += pass.toString(32);
- num = 0;
- pass = 0;
- }
- }
- if (num > 0) {
- cm += pass.toString(32);
- }
- this.outbstr += cm;
+ this.encodeOuterBorder();
}
},
diff --git a/src/variety/longest.js b/src/variety/longest.js
new file mode 100644
index 000000000..5bf6f6e1e
--- /dev/null
+++ b/src/variety/longest.js
@@ -0,0 +1,321 @@
+(function(pidlist, classbase) {
+ if (typeof module === "object" && module.exports) {
+ module.exports = [pidlist, classbase];
+ } else {
+ pzpr.classmgr.makeCustom(pidlist, classbase);
+ }
+})(["longest"], {
+ //---------------------------------------------------------
+ // マウス入力系
+ MouseEvent: {
+ inputModes: {
+ edit: [],
+ play: ["border", "subline"]
+ },
+ mouseinput_auto: function() {
+ if (this.puzzle.playmode) {
+ if (this.mousestart || this.mousemove) {
+ if (this.btn === "left" && this.isBorderMode()) {
+ this.inputborder();
+ } else {
+ this.inputQsubLine();
+ }
+ }
+ } else if (this.puzzle.editmode) {
+ if (this.mousestart || this.mousemove) {
+ this.inputborder();
+ }
+ }
+ }
+ },
+
+ Border: {
+ enableLineNG: true,
+
+ isLineNG: function() {
+ return this.ques === 1;
+ },
+
+ rebuildRooms: function() {
+ var room1 = this.sidecell[0].room,
+ room2 = this.sidecell[1].room;
+ if (room1) {
+ this.board.roommgr.setExtraData(room1);
+ }
+ if (room2 && room1 !== room2) {
+ this.board.roommgr.setExtraData(room2);
+ }
+ },
+
+ posthook: {
+ ques: function() {
+ this.rebuildRooms();
+ },
+ qans: function() {
+ this.rebuildRooms();
+ }
+ }
+ },
+ Board: {
+ cols: 5,
+ rows: 5,
+
+ hasborder: 2
+ },
+ AreaRoomGraph: {
+ enabled: true,
+ setExtraData: function(component) {
+ this.common.setExtraData.call(this, component);
+ component.closed = true;
+
+ var rect = component.clist.getRectSize();
+
+ var long = 0;
+ component.longest = new Set();
+
+ var current = new this.klass.BorderList();
+
+ for (var dir = 0; dir <= 1; dir++) {
+ var mina = dir ? rect.x1 : rect.y1,
+ minb = dir ? rect.y1 : rect.x1,
+ maxa = dir ? rect.x2 : rect.y2,
+ maxb = dir ? rect.y2 : rect.x2;
+
+ for (var a = mina - 1; a <= maxa + 1; a += 2) {
+ for (var b = minb; b <= maxb + 2; b += 2) {
+ var border = dir ? this.board.getb(a, b) : this.board.getb(b, a);
+ var relevant =
+ !border.isnull &&
+ (border.sidecell[0].room === component ||
+ border.sidecell[1].room === component);
+
+ if (border.isBorder() && relevant) {
+ current.add(border);
+ } else if (current.length > 0) {
+ if (current.length > long) {
+ component.longest.clear();
+ long = current.length;
+ }
+ if (current.length === long) {
+ current.each(function(c) {
+ component.longest.add(c);
+ });
+ }
+
+ current = new this.klass.BorderList();
+ }
+
+ if (relevant && component.closed) {
+ if (!border.isBorder() && !border.inside) {
+ component.closed = false;
+ } else if (border.isBorder() && border.inside) {
+ for (var i = 0; i < 2; i++) {
+ var cross = border.sidecross[i];
+ if (
+ cross.lcnt === 1 &&
+ cross.bx !== this.board.minbx &&
+ cross.by !== this.board.minby &&
+ cross.bx !== this.board.maxbx &&
+ cross.by !== this.board.maxby
+ ) {
+ component.closed = false;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+
+ Graphic: {
+ gridcolor_type: "LIGHT",
+ margin: 0.5,
+ errcolor1: "rgb(255, 80, 80)",
+ errcolor2: "rgb(192, 0, 0)",
+
+ paint: function() {
+ this.drawBGCells();
+ this.drawDashedGrid(false);
+
+ this.addlw = 0;
+ this.drawQansBorders();
+ this.addlw = 2;
+ this.drawQuesBorders();
+
+ this.drawLines();
+
+ this.drawBaseMarks();
+
+ this.drawBorderQsubs();
+ },
+ getBorderColor_ques: function(border) {
+ if (border.isLineNG()) {
+ return border.error === 1 ? this.errcolor2 : "black";
+ }
+
+ return null;
+ }
+ },
+
+ Encode: {
+ decodePzpr: function(type) {
+ this.decodeOuterBorder();
+ },
+ encodePzpr: function(type) {
+ this.encodeOuterBorder();
+ }
+ },
+ //---------------------------------------------------------
+ FileIO: {
+ decodeData: function() {
+ this.decodeBorder(function(border, ca) {
+ if (ca === "2") {
+ border.ques = 0;
+ border.qans = 1;
+ } else if (ca === "-1") {
+ border.ques = 0;
+ border.qsub = 1;
+ } else if (ca === "1") {
+ border.ques = 1;
+ } else {
+ border.ques = 0;
+ }
+ });
+ },
+ encodeData: function() {
+ this.encodeBorder(function(border) {
+ if (border.qans === 1) {
+ return "2 ";
+ } else if (border.ques === 1) {
+ return "1 ";
+ } else if (border.qsub === 1) {
+ return "-1 ";
+ } else {
+ return "0 ";
+ }
+ });
+ }
+ },
+
+ AnsCheck: {
+ checklist: [
+ "checkLongestIsAnswer",
+ "checkLongestUnused+",
+ "checkOutsideBorder+",
+ "checkBorderDeadend"
+ ],
+
+ checkLongestIsAnswer: function() {
+ var result = true;
+ var rooms = this.board.roommgr.components;
+ for (var r = 0; r < rooms.length; r++) {
+ var room = rooms[r];
+ var current = true;
+ if (!room.closed) {
+ continue;
+ }
+
+ var longest = Array.from(room.longest);
+ for (var i = 0; i < longest.length; i++) {
+ if (longest[i].qans === 1) {
+ current = false;
+ longest[i].seterr(1);
+ }
+ if (!current && this.checkOnly) {
+ break;
+ }
+ }
+
+ if (!current) {
+ result = false;
+ if (this.checkOnly) {
+ break;
+ }
+ room.clist.seterr(1);
+ }
+ }
+ if (!result) {
+ this.failcode.add("bdNotGiven");
+ this.board.border.setnoerr();
+ }
+ },
+
+ checkLongestUnused: function() {
+ var result = true,
+ borders = this.board.border;
+ for (var id = 0; id < borders.length; id++) {
+ var border = borders[id];
+ if (!border.ques) {
+ continue;
+ }
+ var room1 = border.sidecell[0].room,
+ room2 = border.sidecell[1].room;
+ if (room1 && (!room1.closed || room1.longest.has(border))) {
+ continue;
+ }
+ if (
+ room2 &&
+ room1 !== room2 &&
+ (!room2.closed || room2.longest.has(border))
+ ) {
+ continue;
+ }
+
+ result = false;
+ if (this.checkOnly) {
+ break;
+ }
+ border.seterr(1);
+ }
+ if (!result) {
+ this.failcode.add("bdGivenUnused");
+ borders.setnoerr();
+ }
+ },
+
+ checkOutsideBorder: function() {
+ var borders = this.board.border;
+ for (var id = 0; id < borders.length; id++) {
+ var border = borders[id];
+ if (border.inside || border.isBorder()) {
+ continue;
+ }
+
+ this.failcode.add("bkNotClosed");
+ if (this.checkOnly) {
+ break;
+ }
+ new this.klass.CellList(border.sidecell).seterr(1);
+ }
+ },
+
+ checkBorderDeadend: function() {
+ var result = true,
+ bd = this.board;
+ var crosses = bd.crossinside(
+ bd.minbx + 2,
+ bd.minby + 2,
+ bd.maxbx - 2,
+ bd.maxby - 2
+ );
+ for (var c = 0; c < crosses.length; c++) {
+ var cross = crosses[c];
+ if (cross.lcnt !== 1) {
+ continue;
+ }
+
+ result = false;
+ if (this.checkOnly) {
+ break;
+ }
+ cross.setCrossBorderError();
+ }
+ if (!result) {
+ this.failcode.add("bdDeadEnd");
+ bd.border.setnoerr();
+ }
+ }
+ }
+});
diff --git a/src/variety/mejilink.js b/src/variety/mejilink.js
index 77c7c177c..c59d3d57d 100644
--- a/src/variety/mejilink.js
+++ b/src/variety/mejilink.js
@@ -151,63 +151,10 @@
// URLエンコード/デコード処理
Encode: {
decodePzpr: function(type) {
- this.decodeMejilink();
+ this.decodeOuterBorder();
},
encodePzpr: function(type) {
- this.encodeMejilink();
- },
-
- decodeMejilink: function() {
- var bstr = this.outbstr,
- bd = this.board,
- twi = [16, 8, 4, 2, 1];
- var pos = bstr
- ? Math.min(((bd.border.length + 4) / 5) | 0, bstr.length)
- : 0,
- id = 0;
- for (var i = 0; i < pos; i++) {
- var ca = parseInt(bstr.charAt(i), 32);
- for (var w = 0; w < 5; w++) {
- if (!!bd.border[id]) {
- bd.border[id].ques = ca & twi[w] ? 1 : 0;
- id++;
- }
- }
- }
- this.outbstr = bstr.substr(pos);
- },
- encodeMejilink: function() {
- var count = 0,
- bd = this.board;
- var bdinside = 2 * bd.cols * bd.rows - bd.cols - bd.rows;
- for (var id = bdinside; id < bd.border.length; id++) {
- if (bd.border[id].isGround()) {
- count++;
- }
- }
- var num = 0,
- pass = 0,
- cm = "",
- twi = [16, 8, 4, 2, 1];
- for (
- var id = 0, max = count === 0 ? bdinside : bd.border.length;
- id < max;
- id++
- ) {
- if (bd.border[id].isGround()) {
- pass += twi[num];
- }
- num++;
- if (num === 5) {
- cm += pass.toString(32);
- num = 0;
- pass = 0;
- }
- }
- if (num > 0) {
- cm += pass.toString(32);
- }
- this.outbstr += cm;
+ this.encodeOuterBorder();
}
},
//---------------------------------------------------------
diff --git a/test/script/longest.js b/test/script/longest.js
new file mode 100644
index 000000000..42beb045c
--- /dev/null
+++ b/test/script/longest.js
@@ -0,0 +1,28 @@
+/* longest.js */
+
+ui.debug.addDebugData("longest", {
+ url: "4/4/94001hrn",
+ failcheck: [
+ [
+ "bdNotGiven",
+ "pzprv3/longest/4/4/2 0 1 2 2 /1 2 1 2 1 /1 0 1 2 1 /1 2 0 0 1 /1 1 2 2 /2 0 0 2 /0 2 0 0 /0 2 2 0 /2 1 1 1 /"
+ ],
+ [
+ "bkNotClosed",
+ "pzprv3/longest/4/4/0 0 1 0 0 /1 2 1 0 1 /1 0 1 0 1 /1 2 0 0 1 /1 1 0 0 /2 0 0 0 /0 2 0 0 /0 2 0 0 /2 1 1 1 /"
+ ],
+ [
+ "bdGivenUnused",
+ "pzprv3/longest/4/4/2 0 1 0 2 /1 2 1 0 1 /1 0 1 2 1 /1 2 0 0 1 /1 1 2 2 /2 0 0 0 /0 2 0 2 /0 2 2 0 /2 1 1 1 /"
+ ],
+ [
+ "bdDeadEnd",
+ "pzprv3/longest/4/4/2 0 1 0 2 /1 0 1 0 1 /1 0 1 0 1 /1 0 0 0 1 /1 1 2 2 /0 0 0 0 /0 0 0 0 /0 0 0 0 /2 1 1 1 /"
+ ],
+ [
+ null,
+ "pzprv3/longest/4/4/2 0 1 0 2 /1 2 1 2 1 /1 0 1 2 1 /1 2 0 0 1 /1 1 2 2 /2 0 0 2 /0 2 0 0 /0 2 2 0 /2 1 1 1 /"
+ ]
+ ],
+ inputs: []
+});