From 70a17e0c83433e768282786d3e97b6a9cd03da1a Mon Sep 17 00:00:00 2001 From: Lennard Sprong Date: Tue, 31 Dec 2024 12:20:51 +0100 Subject: [PATCH 01/16] kusabi, mintonette: Add line coloring --- src/variety/firefly.js | 4 ++-- src/variety/kusabi.js | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/variety/firefly.js b/src/variety/firefly.js index b6acfb888..beeb81301 100644 --- a/src/variety/firefly.js +++ b/src/variety/firefly.js @@ -78,6 +78,8 @@ Graphic: { hideHatena: true, + irowake: true, + gridcolor_type: "LIGHT", numbercolor_func: "fixed", @@ -108,8 +110,6 @@ }, "Graphic@firefly": { - irowake: true, - drawFireflyDots: function() { var g = this.vinc("cell_firefly", "auto"); diff --git a/src/variety/kusabi.js b/src/variety/kusabi.js index 422f2255e..8dd3c3328 100644 --- a/src/variety/kusabi.js +++ b/src/variety/kusabi.js @@ -41,6 +41,7 @@ //--------------------------------------------------------- // 画像表示系 Graphic: { + irowake: true, hideHatena: true, gridcolor_type: "LIGHT", From 7ae9da48e7549102adbe76db45bcb31274771930 Mon Sep 17 00:00:00 2001 From: Lennard Sprong Date: Tue, 31 Dec 2024 13:12:45 +0100 Subject: [PATCH 02/16] shugaku: Change trial color --- src/variety/shugaku.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/variety/shugaku.js b/src/variety/shugaku.js index 512a053a1..4ee3e5ce1 100644 --- a/src/variety/shugaku.js +++ b/src/variety/shugaku.js @@ -349,6 +349,7 @@ bcolor: "rgb(208, 208, 208)", targetbgcolor: "rgb(255, 192, 192)" /* 入力中の布団の色 */, undefcolor: "silver" /* 未確定マスの色 */, + trialcolor: "rgb(80, 0, 80)", circleratio: [0.47, 0.42], From 7ebe9724ad05df8b03a784d52f28d1c7de5a1c99 Mon Sep 17 00:00:00 2001 From: Lennard Sprong Date: Tue, 31 Dec 2024 19:52:54 +0100 Subject: [PATCH 03/16] bosanowa: Add unit tests for display modes --- src/variety/bosanowa.js | 21 --------------------- test/variety/bosanowa_test.js | 19 +++++++++++++++++++ 2 files changed, 19 insertions(+), 21 deletions(-) create mode 100644 test/variety/bosanowa_test.js diff --git a/src/variety/bosanowa.js b/src/variety/bosanowa.js index ff57f9fd9..56394abfc 100644 --- a/src/variety/bosanowa.js +++ b/src/variety/bosanowa.js @@ -309,27 +309,6 @@ return this.getBoardRows() + 2 * this.margin + (disptype === 2 ? 2 : 0); }, - drawErrorCells_bosanowa: function() { - var g = this.vinc("cell_back", "crispEdges", true); - - g.fillStyle = this.errbcolor1; - var clist = this.range.cells; - for (var i = 0; i < clist.length; i++) { - var cell = clist[i]; - g.vid = "c_fullerr_" + cell.id; - if (cell.error === 1) { - g.fillRectCenter( - cell.bx * this.bw, - cell.by * this.bh, - this.bw, - this.bh - ); - } else { - g.vhide(); - } - } - }, - getCircleStrokeColor: function(cell) { if (cell.isValid() && !cell.isNum()) { return cell.error === 1 ? this.errcolor1 : this.quescolor; diff --git a/test/variety/bosanowa_test.js b/test/variety/bosanowa_test.js new file mode 100644 index 000000000..b442221a8 --- /dev/null +++ b/test/variety/bosanowa_test.js @@ -0,0 +1,19 @@ +// test/variety/bosanowa_test.js + +var assert = require("assert"); + +var pzpr = require("../../"); + +var puzzle = new pzpr.Puzzle(); + +describe("Variety:bosanowa", function() { + it("loads styles", function() { + puzzle.open("bosanowa/h/6/5/jo9037g2n2n3g4j3i"); + puzzle.toBuffer("svg", 0, 30); + assert.equal(puzzle.getConfig("disptype_bosanowa"), 2); + + puzzle.open("bosanowa/t/6/5/jo9037g2n2n3g4j3i"); + puzzle.toBuffer("svg", 0, 30); + assert.equal(puzzle.getConfig("disptype_bosanowa"), 3); + }); +}); From ab249fb2ff9de84ce32de9f72bcbec1be2958362 Mon Sep 17 00:00:00 2001 From: Lennard Sprong Date: Mon, 13 Jan 2025 07:48:40 +0100 Subject: [PATCH 04/16] Add Inaba's Island --- src-ui/changes.html | 2 +- src-ui/img/island.png | Bin 0 -> 133 bytes src-ui/js/ui/KeyPopup.js | 1 + src-ui/js/ui/Misc.js | 15 ++++--- src-ui/list.html | 1 + src/pzpr/variety.js | 3 +- src/res/failcode.en.json | 2 +- src/variety/kurotto.js | 95 ++++++++++++++++++++++++++++++++++++--- test/script/island.js | 47 +++++++++++++++++++ 9 files changed, 150 insertions(+), 16 deletions(-) create mode 100644 src-ui/img/island.png create mode 100644 test/script/island.js diff --git a/src-ui/changes.html b/src-ui/changes.html index 91032eec5..15fbbf3db 100644 --- a/src-ui/changes.html +++ b/src-ui/changes.html @@ -33,13 +33,13 @@
Latest types (all types)
diff --git a/src-ui/img/island.png b/src-ui/img/island.png new file mode 100644 index 0000000000000000000000000000000000000000..6ac2f275b6ab4f13d8c3eaf1a8bea50e2c7ec502 GIT binary patch literal 133 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJV{wqX6T`Z5GB1G~wg8_HSK}G8 z3eWAiHhパズルの種類のリスト
  • +
  • diff --git a/src/pzpr/variety.js b/src/pzpr/variety.js index 9eac5407e..6a9b6f7c9 100755 --- a/src/pzpr/variety.js +++ b/src/pzpr/variety.js @@ -212,6 +212,7 @@ ], interbd: [0, 0, "International Borders", "International Borders"], invlitso: [0, 0, "Inverse LITSO", "Inverse LITSO", "lits"], + island: [0, 0, "アイランド", "Inaba's Island", "kurotto"], juosan: [0, 0, "縦横さん", "Juosan"], kaero: [1, 0, "お家に帰ろう", "Return Home"], kaidan: [0, 0, "かいだんしばり", "Stairwell"], @@ -374,7 +375,7 @@ scrin: [0, 0, "スクリン", "Scrin"], shakashaka: [0, 1, "シャカシャカ", "Shakashaka"], shikaku: [0, 1, "四角に切れ", "Shikaku", "shikaku"], - shimaguni: [1, 0, "島国", "Islands", "shimaguni"], + shimaguni: [1, 0, "島国", "Shimaguni", "shimaguni"], shugaku: [1, 0, "修学旅行の夜", "School Trip"], shwolf: [0, 0, "ヤギとオオカミ", "Goats and Wolves", "kramma"], simplegako: [0, 0, "シンプルガコ", "Simple Gako"], diff --git a/src/res/failcode.en.json b/src/res/failcode.en.json index 8f5af3e47..10b167687 100644 --- a/src/res/failcode.en.json +++ b/src/res/failcode.en.json @@ -786,7 +786,7 @@ "nmSumOfDiff.bosanowa": "Sum of the differences between the number and adjacent numbers is not equal to the number.", "nmSumRowNe.kakuro": "The sum of the cells is not correct.", "nmSumRowShadeNe.box": "A number is not equal to the sum of the number of shaded cells.", - "nmSumSizeNe.kurotto": "The number is not equal to sum of adjacent masses of shaded cells.", + "nmSumSizeNe.kurotto": "A number doesn't indicate the sum of the size of the adjacent blocks.", "nmSumSizeNe.tasquare": "Sum of the adjacent masses of shaded cells is not equal to the number.", "nmSumViewNe.teri": "The size of the largest possible rectangle is not equal to the number.", "nmSumViewNe.view": "Sum of four-way gaps to another number is not equal to the number.", diff --git a/src/variety/kurotto.js b/src/variety/kurotto.js index 9a018a070..86ca126ed 100644 --- a/src/variety/kurotto.js +++ b/src/variety/kurotto.js @@ -7,7 +7,7 @@ } else { pzpr.classmgr.makeCustom(pidlist, classbase); } -})(["kurotto", "mines"], { +})(["kurotto", "mines", "island"], { //--------------------------------------------------------- // マウス入力系 MouseEvent: { @@ -40,6 +40,22 @@ this.mousereset(); } }, + "MouseEvent@island": { + inputModes: { + edit: ["number", "clear"], + play: ["shade", "unshade", "info-blk"] + }, + dispInfoBlk: function() { + var cell = this.getcell(); + this.mousereset(); + if (cell.isnull || !cell.island) { + return; + } + cell.island.clist.setinfo(1); + this.board.hasinfo = true; + this.puzzle.redraw(); + } + }, //--------------------------------------------------------- // キーボード入力系 @@ -67,7 +83,12 @@ return this.checkComplete(); } }, - "Cell@kurotto": { + "Board@island": { + addExtraInfo: function() { + this.islandgraph = this.addInfoList(this.klass.AreaIslandGraph); + } + }, + "Cell@kurotto,island": { maxnum: function() { var max = this.board.cell.length - 1; return max <= 999 ? max : 999; @@ -131,9 +152,27 @@ } }, - "AreaShadeGraph@kurotto": { + "AreaShadeGraph@kurotto,island": { enabled: true }, + "AreaIslandGraph:AreaShadeGraph@island": { + enabled: true, + coloring: true, + relation: { "cell.qans": "node", "cell.qnum": "node" }, + setComponentRefs: function(obj, component) { + obj.island = component; + }, + getObjNodeList: function(nodeobj) { + return nodeobj.islandnodes; + }, + resetObjNodeList: function(nodeobj) { + nodeobj.islandnodes = []; + }, + + isnodevalid: function(cell) { + return cell.isShade() || cell.isNum(); + } + }, //--------------------------------------------------------- // 画像表示系 @@ -156,7 +195,7 @@ this.common.setRange.call(this, x1, y1, x2, y2); } }, - "Graphic@kurotto": { + "Graphic@kurotto,island": { hideHatena: true, numbercolor_func: "qnum", @@ -183,6 +222,43 @@ return null; } }, + "Graphic@island#1": { + irowakeblk: true, + + getCircleFillColor: function(cell) { + if (!cell.isCmp()) { + return null; + } + var hasinfo = this.board.haserror || this.board.hasinfo; + if (this.puzzle.execConfig("irowakeblk") && !hasinfo) { + var color = cell.island.color; + if (typeof color !== "string") { + return color; + } + + return color.replace("rgb", "rgba").replace(")", ",0.25)"); + } + return this.qcmpcolor; + }, + + getShadedCellColor: function(cell) { + if (cell.qans !== 1) { + return null; + } + var hasinfo = this.board.haserror || this.board.hasinfo; + var info = cell.error || cell.qinfo; + if (info === 1) { + return this.errcolor1; + } else if (info === 2) { + return this.errcolor2; + } else if (cell.trial) { + return this.trialcolor; + } else if (this.puzzle.execConfig("irowakeblk") && !hasinfo) { + return cell.island.color; + } + return this.shadecolor; + } + }, "Graphic@mines": { qcmpcolor: "rgb(127,127,127)", @@ -260,11 +336,18 @@ } } }, - "AnsCheck@kurotto": { - checklist: ["checkShadeCellExist", "checkCellNumber_kurotto"], + "AnsCheck@kurotto,island": { + checklist: [ + "checkShadeCellExist", + "checkCellNumber_kurotto", + "checkConnectShaded_island@island" + ], checkCellNumber_kurotto: function() { this.checkCellNumber("nmSumSizeNe"); + }, + checkConnectShaded_island: function() { + this.checkOneArea(this.board.islandgraph, "csDivide"); } }, "AnsCheck@mines": { diff --git a/test/script/island.js b/test/script/island.js new file mode 100644 index 000000000..1264ba224 --- /dev/null +++ b/test/script/island.js @@ -0,0 +1,47 @@ +/* island.js */ + +ui.debug.addDebugData("island", { + url: "5/5/1g6m2i3h4m", + failcheck: [ + [ + "brNoShade", + "pzprv3/island/5/5/1 . 6 . . /. . . . . /2 . . . 3 /. . 4 . . /. . . . . /. . . . . /. . . . . /. . . . . /. . . . . /. . . . . /" + ], + [ + "nmSumSizeNe", + "pzprv3/island/5/5/1 . 6 . . /. . . . . /2 . . . 3 /. . 4 . . /. . . . . /. # . # # /+ + # . . /. + # # . /# # . + + /. . . . + /" + ], + [ + "csDivide", + "pzprv3/island/5/5/1 . 6 . . /. . . . . /2 . . . 3 /. . 4 . . /. . . . . /. # . # # /+ + # + # /. + # + . /# . . + + /# + # # + /" + ], + [ + null, + "pzprv3/island/5/5/1 . 6 . . /. . . . . /2 . . . 3 /. . 4 . . /. . . . . /. # . # # /+ + # + # /. + # + . /# # . + + /+ + + + + /" + ] + ], + inputs: [ + { input: ["newboard,5,1", "editmode"] }, + { + input: [ + "cursor,3,1", + "key,1", + "cursor,9,1", + "key,1", + "playmode", + "mouse,left,1,1,5,1" + ], + result: "pzprv3/island/1/5/. 1 . . 1 /# . # . . /" + }, + { + input: ["playmode,info-blk", "mouse,left,3,1"], + result: function(puzzle, assert) { + var bd = puzzle.board; + assert.equal(bd.getc(1, 1).qinfo, 1); + assert.equal(bd.getc(3, 1).qinfo, 1); + assert.equal(bd.getc(5, 1).qinfo, 1); + assert.equal(bd.getc(9, 1).qinfo, 0); + } + } + ] +}); From 885d293a67e23a9e81bda2d8137c6a6193ad7e8b Mon Sep 17 00:00:00 2001 From: Lennard Sprong Date: Mon, 13 Jan 2025 08:13:58 +0100 Subject: [PATCH 05/16] island: Change minimum number to 1 --- src/variety/kurotto.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/variety/kurotto.js b/src/variety/kurotto.js index 86ca126ed..ada7296ac 100644 --- a/src/variety/kurotto.js +++ b/src/variety/kurotto.js @@ -83,6 +83,9 @@ return this.checkComplete(); } }, + "Cell@island": { + minnum: 1 + }, "Board@island": { addExtraInfo: function() { this.islandgraph = this.addInfoList(this.klass.AreaIslandGraph); From 6a09471b4ef9a450c2c2e98923a7bf4aee0479a8 Mon Sep 17 00:00:00 2001 From: Lennard Sprong Date: Wed, 15 Jan 2025 13:34:43 +0100 Subject: [PATCH 06/16] slither: Add Full Loop variant --- src-ui/js/ui/MenuConfig.js | 7 +++++++ src-ui/p.html | 6 ++++++ src-ui/res/p.en.json | 1 + src/puzzle/Config.js | 14 ++++++++++++++ src/res/failcode.en.json | 1 + src/variety-common/Answer.js | 12 ++++++++++-- src/variety-common/Graphic.js | 19 +++++++++++++++++++ src/variety/lineofsight.js | 6 ++++++ src/variety/lither.js | 19 ------------------- src/variety/myopia.js | 8 +++++++- src/variety/slither.js | 8 ++++++++ src/variety/vslither.js | 8 +++++++- test/script/slither.js | 10 ++++++++++ 13 files changed, 96 insertions(+), 23 deletions(-) diff --git a/src-ui/js/ui/MenuConfig.js b/src-ui/js/ui/MenuConfig.js index 58f1243e9..42fdbe3a1 100644 --- a/src-ui/js/ui/MenuConfig.js +++ b/src-ui/js/ui/MenuConfig.js @@ -132,6 +132,13 @@ case "bdwalk": idname = "bdwalk_height"; break; + case "slither": + case "tslither": + case "swslither": + case "myopia": + case "lineofsight": + idname = "slither_full"; + break; } if (typeof idname === "string") { diff --git a/src-ui/p.html b/src-ui/p.html index a63ab9eea..e91696181 100644 --- a/src-ui/p.html +++ b/src-ui/p.html @@ -436,6 +436,12 @@

    読み込み中です...

    __heyapin_overlap__
    +
    + +
    +
    + +
    diff --git a/src/puzzle/Config.js b/src/puzzle/Config.js index 851e1fd92..2f6e15260 100644 --- a/src/puzzle/Config.js +++ b/src/puzzle/Config.js @@ -494,7 +494,8 @@ "midloop", "ovotovata", "balance", - "turnaround" + "turnaround", + "turnrun" ].indexOf(pid) >= 0; break; default: diff --git a/src/pzpr/variety.js b/src/pzpr/variety.js index 6a9b6f7c9..ba92b6a7f 100755 --- a/src/pzpr/variety.js +++ b/src/pzpr/variety.js @@ -436,6 +436,7 @@ triplace: [0, 0, "トリプレイス", "Tri-place"], tslither: [0, 0, "Touch Slitherlink", "Touch Slitherlink", "vslither"], turnaround: [0, 0, "ターンアラウンド", "Turnaround"], + turnrun: [0, 0, "Turn and Run", "Turn and Run", "trainstations"], twinarea: [0, 0, "ツインエリア", "Twin Area", "hanare"], usotatami: [0, 0, "ウソタタミ", "Uso-tatami", "fillmat"], usoone: [0, 0, "ウソワン", "Uso-one"], diff --git a/src/res/failcode.en.json b/src/res/failcode.en.json index 7d2f9c89d..854b5166c 100644 --- a/src/res/failcode.en.json +++ b/src/res/failcode.en.json @@ -355,6 +355,7 @@ "ceDirection.compass": "The number of cells in the direction is wrong.", "ceDirection.mukkonn": "A line that exits a cell turns at the wrong position.", "ceDirection.guidearrow": "A cell does not follow the indicated direction towards the goal.", + "ceDirection.turnrun": "A line that exits a cell turns at the wrong position.", "ceEmpty.kaidan": "There is an empty cell.", "ceEmpty.lightshadow": "There is an undetermined cell.", "ceEmpty.shugaku": "There is an empty cell.", @@ -615,6 +616,7 @@ "lnSnLine.lither": "There is only one tree.", "lnStarNe.starbattle": "The number of stars in a line is wrong.", "lnStraightOnIce.firewalk": "A line goes straight through fire.", + "lnStraightOnNum": "A line goes straight through a number.", "lnWrongAngle.kouchoku": "Some segments meet at the wrong angle.", "lookairBC.lookair": "Two squares of the same size can see each other.", "lpNoNum.onsen": "A loop has no numbers.", @@ -807,8 +809,7 @@ "nonCirclePromontory.kurodoko": "A dead end has no circle.", "nqAroundDup.kakuru": "There are same numbers around the pre-numbered cell.", "nqAroundSumNe.kakuru": "A sum of numbers around the pre-numbered cell is incorrect.", - "numNoLine.geradeweg": "A number has no line.", - "numNoLine.turnaround": "A number has no line.", + "numNoLine": "A number has no line.", "objShaded.nurimaze": "An object is shaded.", "pairedLetterNe.kinkonkan": "Beam from a light doesn't reach its pair.", "pairedNumberNe.kinkonkan": "The number of reflections is wrong.", diff --git a/src/res/failcode.ja.json b/src/res/failcode.ja.json index 24af16444..153114936 100644 --- a/src/res/failcode.ja.json +++ b/src/res/failcode.ja.json @@ -588,7 +588,7 @@ "nonCirclePromontory.kurodoko": "丸のないマスが岬になっています。", "nqAroundDup.kakuru": "初めから出ている数字の周りに同じ数字が入っています。", "nqAroundSumNe.kakuru": "初めから出ている数字の周りに入る数の合計が正しくありません。", - "numNoLine.geradeweg": "線の通っていない数字があります。", + "numNoLine": "線の通っていない数字があります。", "objShaded.nurimaze": "オブジェクトが黒マスになっています。", "pairedLetterNe.kinkonkan": "光が同じ文字の場所へ到達しません。", "pairedNumberNe.kinkonkan": "光の反射回数が正しくありません。", diff --git a/src/variety/trainstations.js b/src/variety/trainstations.js index 9fc8b7883..3662cd68b 100644 --- a/src/variety/trainstations.js +++ b/src/variety/trainstations.js @@ -4,7 +4,7 @@ } else { pzpr.classmgr.makeCustom(pidlist, classbase); } -})(["trainstations"], { +})(["trainstations", "turnrun"], { MouseEvent: { inputModes: { edit: ["number", "undef", "empty", "clear", "info-line"], @@ -74,6 +74,29 @@ return this.isEmpty(); } }, + "Cell@turnrun": { + maxnum: function() { + var bd = this.board; + return Math.max(bd.cols, bd.rows) - 1; + }, + getSegmentDir: function(dir) { + var llist = new this.klass.PieceList(); + var pos = this.getaddr().movedir(dir, 1); + while (1) { + var border = pos.getb(); + if (!border || border.isnull) { + break; + } + if (border.isLine()) { + llist.add(border); + } else { + break; + } + pos.movedir(dir, 2); + } + return llist; + } + }, Board: { hasborder: 1, maxFoundNumber: -1, @@ -208,14 +231,17 @@ decodePzpr: function(type) { this.decodeNumber16(); this.decodeEmpty(); + this.puzzle.setConfig("loop_full", this.checkpflag("f")); }, encodePzpr: function(type) { + this.outpflag = this.puzzle.getConfig("loop_full") ? "f" : null; this.encodeNumber16(); this.encodeEmpty(); } }, FileIO: { decodeData: function() { + this.decodeConfigFlag("f", "loop_full"); this.decodeCell(function(cell, ca) { if (ca === "#") { cell.ques = 7; @@ -228,6 +254,7 @@ this.decodeBorderArrowAns(); }, encodeData: function() { + this.encodeConfigFlag("f", "loop_full"); this.encodeCell(function(cell) { if (cell.ques === 7) { return "# "; @@ -245,17 +272,21 @@ AnsCheck: { checklist: [ - "checkNumberRange", + "checkNumberRange@trainstations", "checkBranchLine", "checkCrossOutOfMark", - "checkCurveOnNumber", - "checkNumberConsecutive", + "checkCurveOnNumber@trainstations", + "checkStraightOnNumber@turnrun", + "checkNumberConsecutive@trainstations", "checkNotCrossOnMark", + "checkNoLineNumber@turnrun", "checkDeadendLine+", "checkOneLoop", - "checkNumberFullSequence", - "checkNoLine" + "checkNumberFullSequence@trainstations", + "checkTurnLengths@turnrun", + "checkNoLine@trainstations", + "checkNoLineIfVariant@turnrun" ], checkNumberRange: function() { @@ -280,6 +311,21 @@ return cell.isLineCurve() && cell.qnum !== -1 && cell.qnum !== 0; }, "lnCurveOnNum"); }, + checkStraightOnNumber: function() { + this.checkAllCell(function(cell) { + return cell.isLineStraight() && cell.qnum !== -1 && cell.qnum !== 0; + }, "lnStraightOnNum"); + }, + + checkTurnLengths: function() { + this.checkWalkGeneric(this.turnrun_walkLine, "ceDirection"); + }, + + checkNoLineNumber: function() { + this.checkAllCell(function(cell) { + return cell.isNum() && cell.lcnt === 0; + }, "numNoLine"); + }, checkNumberConsecutive: function() { var max = this.board.getMaxFoundNumber(); @@ -302,6 +348,10 @@ }, checkNumberFullSequence: function() { + this.checkWalkGeneric(this.trainstations_walkLine, "nmNotConseqFull"); + }, + + checkWalkGeneric: function(func, code) { var bd = this.board, paths = bd.linegraph.components, path = paths[0]; @@ -327,7 +377,7 @@ var walks = []; for (var dir = 1; dir <= 4; dir++) { if (start.reldirbd(dir, 1).isLine()) { - walks.push(this.walkLine(start, dir)); + walks.push(func.call(this, start, dir)); } } @@ -339,7 +389,7 @@ return; } - this.failcode.add("nmNotConseqFull"); + this.failcode.add(code); if (this.checkOnly) { return; } @@ -353,7 +403,7 @@ walk.blist.seterr(1); }, - walkLine: function(start, dir) { + trainstations_walkLine: function(start, dir) { var clist = new this.klass.CellList(); var blist = new this.klass.BorderList(); var current = new this.klass.BorderList(); @@ -401,6 +451,49 @@ !addr.getc().isEmpty() ); + return { clist: clist, blist: blist }; + }, + + turnrun_walkLine: function(start, dir) { + var clist = new this.klass.CellList(); + var blist = new this.klass.BorderList(); + var addr = start.getaddr(); + + do { + var cell = addr.getc(); + + var segments = + cell.isLineCurve() && cell.isValidNum() + ? cell.getSegmentDir(dir) + : null; + + if (segments && segments.length !== cell.qnum) { + clist.add(cell); + blist.extend(segments); + } + + addr.movedir(dir, 2); + + var next = addr.getc(); + var adb = next.adjborder; + + if (next.lcnt === 4) { + /* Go straight at a crossing */ + } else if (dir !== 1 && adb.bottom.isLine()) { + dir = 2; + } else if (dir !== 2 && adb.top.isLine()) { + dir = 1; + } else if (dir !== 3 && adb.right.isLine()) { + dir = 4; + } else if (dir !== 4 && adb.left.isLine()) { + dir = 3; + } + } while ( + !addr.equals(start) && + addr.getc().lcnt > 1 && + !addr.getc().isEmpty() + ); + return { clist: clist, blist: blist }; } } diff --git a/test/script/turnrun.js b/test/script/turnrun.js new file mode 100644 index 000000000..d1063e852 --- /dev/null +++ b/test/script/turnrun.js @@ -0,0 +1,54 @@ +/* turnrun.js */ + +ui.debug.addDebugData("turnrun", { + url: "5/5/g2o0h1k4j", + failcheck: [ + [ + "lnBranch", + "pzprv3/turnrun/5/5/. 2 . . . /. . . . . /. 0 . . 1 /. . . . . /4 . . . . /0 0 0 0 /0 0 0 0 /0 0 0 0 /1 0 0 0 /0 0 0 0 /0 0 0 0 0 /0 0 0 0 0 /1 0 0 0 0 /1 0 0 0 0 /" + ], + [ + "lnCrossExMk", + "pzprv3/turnrun/5/5/. 2 . . . /. . . . . /. 0 . . 1 /. . . . . /4 . . . . /0 1 1 0 /0 0 1 1 /0 0 0 0 /0 0 0 0 /0 0 0 0 /0 0 0 1 0 /0 0 0 1 0 /0 0 0 0 0 /0 0 0 0 0 /" + ], + [ + "lnStraightOnNum", + "pzprv3/turnrun/5/5/. 2 . . . /. . . . . /. 0 . . 1 /. . . . . /4 . . . . /1 1 0 0 /1 0 0 1 /1 1 0 0 /0 1 1 0 /1 1 1 1 /1 0 1 0 0 /0 1 1 1 1 /1 1 0 1 1 /1 0 0 0 1 /" + ], + [ + "ceDirection", + "pzprv3/turnrun/5/5/. 2 . . . /. . . . . /. 0 . . 1 /. . . . . /4 . . . . /0 1r 1r 1r /0 0 1 1 /1 1 0 1 /0 1 1 0 /1 1 1 1 /0 1 0 0 1 /0 1 1 0 0 /1 1 0 1 1 /1 0 0 0 1 /" + ], + [ + "lnNotCrossMk", + "pzprv3/turnrun/5/5/. 2 . . . /. . . . . /. 0 . . 1 /. . . . . /4 . . . . /0 1 1 0 /1 0 0 0 /0 0 0 1 /0 0 0 1 /1 1 1 0 /0 1 0 1 0 /1 0 0 1 0 /1 0 0 0 1 /1 0 0 1 0 /" + ], + [ + "lnPlLoop", + "pzprv3/turnrun/5/5/. 2 . . . /. . . . . /. 0 . . 1 /. . . . . /4 . . . . /0 1 1 0 /0 0 1 0 /1 1 0 1 /0 0 0 1 /1 0 0 0 /0 1 0 1 0 /0 1 1 0 0 /1 1 0 1 1 /1 1 0 0 0 /:" + ], + [ + "numNoLine", + "pzprv3/turnrun/5/5/. 2 . . . /. . . . . /. 0 . . 1 /. . . . . /4 . . . . /0 1 1 0 /0 0 1 0 /1 1 0 0 /0 1 1 1 /1 1 1 1 /0 1 0 1 0 /0 1 1 0 0 /1 1 0 0 0 /1 0 0 0 1 /" + ], + [ + "lnDeadEnd", + "pzprv3/turnrun/5/5/. 2 . . . /. . . . . /. 0 . . 1 /. . . . . /4 . . . . /0 1 1 0 /0 0 0 0 /1 1 0 1 /0 1 1 0 /1 1 1 1 /0 1 0 1 0 /0 1 0 0 0 /1 1 0 1 1 /1 0 0 0 1 /" + ], + [ + "ceNoLine", + "pzprv3/turnrun/3/3/f/. . 2 /. . . /. . . /1 1 /1 0 /0 1 /1 0 1 /0 1 1 /", + { skiprules: true } + ], + [ + null, + "pzprv3/turnrun/3/3/f/. . 2 /. . . /# . . /1 1 /1 0 /0 1 /1 0 1 /0 1 1 /", + { skiprules: true } + ], + [ + null, + "pzprv3/turnrun/5/5/. 2 . . . /. . . . . /. 0 . . 1 /. . . . . /4 . . . . /0 1r 1r 0 /0 0 1 0 /1 1 0 1l /0 1 1 0 /1r 1r 1r 1r /0 1 0 1 0 /0 1 1 0 0 /1 1 0 1 1 /1 0 0 0 1 /" + ] + ], + inputs: [] +}); From d89e69baaac396c1837b383297c6f80a31be2c99 Mon Sep 17 00:00:00 2001 From: Lennard Sprong Date: Mon, 20 Jan 2025 15:42:30 +0100 Subject: [PATCH 15/16] Remove blank History keys --- src-ui/res/history.en.yaml | 38 -------------------------------------- 1 file changed, 38 deletions(-) diff --git a/src-ui/res/history.en.yaml b/src-ui/res/history.en.yaml index 3fce0cfff..7ffbec54b 100644 --- a/src-ui/res/history.en.yaml +++ b/src-ui/res/history.en.yaml @@ -25,16 +25,11 @@ heyablock: This genre was invented by Atsumi Hirose in 2013 for the Japan Zukei mirrorbk: This genre first appeared in Puzzle Communication Nikoli vol. 177. takoyaki: This genre was invented by Michael Vedder. akichi: This genre was invented by Prasanna Seshadri. -aho: "" amibo: "This genre first appeared in Puzzle Communication Nikoli vol. 134." -angleloop: "" -anglers: "" aqre: "This genre was invented by Eric Fox." aquapelago: "This genre was invented by Walker Anderson." aquarium: "This genre was invented by Inaba Naoki." -araf: "" armyants: "This genre first appeared in Puzzle Communication Nikoli vol. 158." -ayeheya: "" balance: "This genre was invented by Prasanna Seshadri." cave: "This genre first appeared in Puzzle Communication Nikoli vol. 58 under the name 'Bag'." barns: "This genre first appeared in Puzzle Communication Nikoli vol. 114." @@ -42,26 +37,19 @@ bdblock: "This genre first appeared in Puzzle Communication Nikoli vol. 49." bonsan: "This genre first appeared in Puzzle Communication Nikoli vol. 96." bosanowa: "This genre first appeared in Puzzle Communication Nikoli vol. 119." box: "This genre first appeared in Puzzle Communication Nikoli vol. 24." -skyscrapers: "" castle: "This genre was invented by Palmer Mebane." cbblock: "This genre first appeared in Puzzle Communication Nikoli vol. 131." chainedb: "This genre first appeared in Puzzle Communication Nikoli vol. 173." -chocona: "" coffeemilk: "This genre first appeared in Puzzle Communication Nikoli vol. 156." cojun: "This genre first appeared in Puzzle Communication Nikoli vol. 125." -compass: "" -coral: "" country: "This genre first appeared in Puzzle Communication Nikoli vol. 65." creek: "This genre first appeared in Puzzle Communication Nikoli vol. 110." -curvedata: "" dbchoco: "This genre first appeared in Puzzle Communication Nikoli vol. 163." -detour: "" dominion: "This genre was invented by Inaba Naoki." doppelblock: "This genre was invented by Inaba Naoki." dosufuwa: "This genre first appeared in Puzzle Communication Nikoli vol. 151." dotchi: "This genre first appeared in Puzzle Communication Nikoli vol. 167." doubleback: "This genre was invented by Palmer Mebane." -easyasabc: "" evolmino: "This genre first appeared in Puzzle Communication Nikoli vol. 182." factors: "This genre first appeared in Puzzle Communication Nikoli vol. 92." fillmat: "This genre first appeared in Puzzle Communication Nikoli vol. 42." @@ -69,15 +57,12 @@ fillomino: "This genre first appeared in Puzzle Communication Nikoli vol. 47." firefly: "This genre first appeared in Puzzle Communication Nikoli vol. 91." fivecells: "This genre first appeared in Puzzle Communication Nikoli vol. 133." fourcells: "This genre first appeared in Puzzle Communication Nikoli vol. 132." -geradeweg: "" -goishi: "" gokigen: "This genre first appeared in Puzzle Communication Nikoli vol. 104." haisu: "This genre was invented by William Hu." hakoiri: "This genre first appeared in Puzzle Communication Nikoli vol. 72." hanare: "This genre first appeared in Puzzle Communication Nikoli vol. 134." hashikake: "This genre first appeared in Puzzle Communication Nikoli vol. 31." hebi: "This genre first appeared in Puzzle Communication Nikoli vol. 126." -herugolf: "" heteromino: "This genre was invented by Inaba Naoki." heyabon: "This genre first appeared in Puzzle Communication Nikoli vol. 107." heyawake: "This genre first appeared in Puzzle Communication Nikoli vol. 39." @@ -90,8 +75,6 @@ ichimaga: "This genre first appeared in Puzzle Communication Nikoli vol. 35." ichimagam: "This genre first appeared in Puzzle Communication Nikoli vol. 37." ichimagax: "This genre first appeared in Puzzle Communication Nikoli vol. 36." interbd: "This genre was invented by Palmer Mebane." -juosan: "" -kaero: "" kakuru: "This genre first appeared in Puzzle Communication Nikoli vol. 32." kazunori: "This genre first appeared in Puzzle Communication Nikoli vol. 149." kinkonkan: "This genre first appeared in Puzzle Communication Nikoli vol. 89." @@ -99,7 +82,6 @@ koburin: "This genre first appeared in Puzzle Communication Nikoli vol. 116." kouchoku: "This genre first appeared in Puzzle Communication Nikoli vol. 133." kramma: "This genre first appeared in Puzzle Communication Nikoli vol. 123." kramman: "This genre first appeared in Puzzle Communication Nikoli vol. 124." -kropki: "" kurochute: "This genre first appeared in Puzzle Communication Nikoli vol. 120." kuroclone: "This genre first appeared in Puzzle Communication Nikoli vol. 153." kurodoko: "This genre first appeared in Puzzle Communication Nikoli vol. 34." @@ -113,12 +95,10 @@ loute: "This genre first appeared in Puzzle Communication Nikoli vol. 133." makaro: "This genre first appeared in Puzzle Communication Nikoli vol. 143." mashu: "This genre first appeared in Puzzle Communication Nikoli vol. 90." maxi: "This genre was invented by Inaba Naoki." -meander: "" mejilink: "This genre first appeared in Puzzle Communication Nikoli vol. 126." minarism: "This genre first appeared in Puzzle Communication Nikoli vol. 93." midloop: "This genre first appeared in Puzzle Communication Nikoli vol. 163." mochikoro: "This genre first appeared in Puzzle Communication Nikoli vol. 100." -mochinyoro: "" moonsun: "This genre first appeared in Puzzle Communication Nikoli vol. 154." nagare: "This genre first appeared in Puzzle Communication Nikoli vol. 147." nagenawa: "This genre first appeared in Puzzle Communication Nikoli vol. 123." @@ -131,13 +111,9 @@ nothree: "This genre first appeared in Puzzle Communication Nikoli vol. 158." nuribou: "This genre first appeared in Puzzle Communication Nikoli vol. 68." nurikabe: "This genre first appeared in Puzzle Communication Nikoli vol. 33." nurimaze: "This genre first appeared in Puzzle Communication Nikoli vol. 145." -nurimisaki: "" -nuriuzu: "" onsen: "This genre first appeared in Puzzle Communication Nikoli vol. 155." paintarea: "This genre first appeared in Puzzle Communication Nikoli vol. 69." -parquet: "" pencils: "This genre first appeared in Puzzle Communication Nikoli vol. 158." -pentominous: "" pentopia: "This genre was invented by Bram de Laat." pipelink: "This genre first appeared in Puzzle Communication Nikoli vol. 45." pipelinkr: "This genre first appeared in Puzzle Communication Nikoli vol. 110." @@ -155,44 +131,30 @@ shakashaka: "This genre first appeared in Puzzle Communication Nikoli vol. 123." shikaku: "This genre first appeared in Puzzle Communication Nikoli vol. 27." shimaguni: "This genre first appeared in Puzzle Communication Nikoli vol. 117." shugaku: "This genre first appeared in Puzzle Communication Nikoli vol. 119." -shwolf: "" slalom: "This genre first appeared in Puzzle Communication Nikoli vol. 116." slither: "This genre first appeared in Puzzle Communication Nikoli vol. 26." -snake: "" -snakepit: "" -starbattle: "" statuepark: "This genre was invented by Palmer Mebane." stostone: "This genre first appeared in Puzzle Communication Nikoli vol. 156." sukoro: "This genre first appeared in Puzzle Communication Nikoli vol. 40." sukororoom: "This genre first appeared in Puzzle Communication Nikoli vol. 130." -symmarea: "" -tajmahal: "" tapa: "This genre was invented by Serkan Yürekli." -tapaloop: "" tasquare: "This genre first appeared in Puzzle Communication Nikoli vol. 123." tatamibari: "This genre first appeared in Puzzle Communication Nikoli vol. 107." tateyoko: "This genre first appeared in Puzzle Communication Nikoli vol. 103." tawa: "This genre first appeared in Puzzle Communication Nikoli vol. 127." tentaisho: "This genre first appeared in Puzzle Communication Nikoli vol. 96." -tents: "" tilepaint: "This genre first appeared in Puzzle Communication Nikoli vol. 53." toichika: "This genre first appeared in Puzzle Communication Nikoli vol. 129." toichika2: "This genre first appeared in Puzzle Communication Nikoli vol. 167." -tren: "" triplace: "This genre first appeared in Puzzle Communication Nikoli vol. 43." -tslither: "" usotatami: "This genre first appeared in Puzzle Communication Nikoli vol. 125." usoone: "This genre first appeared in Puzzle Communication Nikoli vol. 151." -view: "" -vslither: "" wagiri: "This genre first appeared in Puzzle Communication Nikoli vol. 129." -walllogic: "" wblink: "This genre first appeared in Puzzle Communication Nikoli vol. 45." yajikazu: "This genre first appeared in Puzzle Communication Nikoli vol. 74." yajilin: "This genre first appeared in Puzzle Communication Nikoli vol. 86." yajilin-regions: "This genre was invented by Inaba Naoki." yajitatami: "This genre first appeared in Puzzle Communication Nikoli vol. 135." -yinyang: "" yosenabe: "This genre first appeared in Puzzle Communication Nikoli vol. 135." slashpack: This genre was invented by Yosuke Imai. remlen: This genre was invented by Palmer Mebane. From 3b023f81513e3ac3d71a44593fa86e8ae48601fa Mon Sep 17 00:00:00 2001 From: Lennard Sprong Date: Mon, 20 Jan 2025 16:24:37 +0100 Subject: [PATCH 16/16] turnrun: Add History and Japanese title --- src-ui/changes.html | 2 +- src-ui/res/history.en.yaml | 1 + src/pzpr/variety.js | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src-ui/changes.html b/src-ui/changes.html index 9190fbcf7..ebe1e7df2 100644 --- a/src-ui/changes.html +++ b/src-ui/changes.html @@ -33,7 +33,7 @@
    Latest types (all types)
      -
    • Turn and Run
    • +
    • Turn and Run トツゲキループ
    • Inaba's Island アイランド
    • Balloon Box 風船箱
    • Mintonette
    • diff --git a/src-ui/res/history.en.yaml b/src-ui/res/history.en.yaml index 7ffbec54b..b4be745f4 100644 --- a/src-ui/res/history.en.yaml +++ b/src-ui/res/history.en.yaml @@ -199,3 +199,4 @@ timebomb: This genre first appeared in Puzzle Communication Nikoli vol. 163. nibunnogo: This genre first appeared in Puzzle Communication Nikoli vol. 164. balloon: This genre first appeared in Puzzle Communication Nikoli vol. 189. tilecity: This genre was invented by _AtomicNeoN_. +turnrun: This genre was invented as "Totsugeki Loop" by Subaru Saito in 2018. A similar ruleset named "Turn and Run" appeared in a Logic Masters Deutschland competition in 2021. diff --git a/src/pzpr/variety.js b/src/pzpr/variety.js index ba92b6a7f..f613e3dfe 100755 --- a/src/pzpr/variety.js +++ b/src/pzpr/variety.js @@ -436,7 +436,7 @@ triplace: [0, 0, "トリプレイス", "Tri-place"], tslither: [0, 0, "Touch Slitherlink", "Touch Slitherlink", "vslither"], turnaround: [0, 0, "ターンアラウンド", "Turnaround"], - turnrun: [0, 0, "Turn and Run", "Turn and Run", "trainstations"], + turnrun: [0, 0, "トツゲキループ", "Turn and Run", "trainstations"], twinarea: [0, 0, "ツインエリア", "Twin Area", "hanare"], usotatami: [0, 0, "ウソタタミ", "Uso-tatami", "fillmat"], usoone: [0, 0, "ウソワン", "Uso-one"],