From 0580b29b0f259482c75c38aaf6ef19182512eb93 Mon Sep 17 00:00:00 2001 From: Lennard Sprong Date: Mon, 24 Nov 2025 20:26:17 +0100 Subject: [PATCH 01/12] Translated using Weblate (English) Currently translated at 100.0% (228 of 228 strings) Translation: pzprjs/Genre History Translate-URL: https://hosted.weblate.org/projects/pzprjs/genre-history/en/ --- src-ui/res/history.en.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-ui/res/history.en.yaml b/src-ui/res/history.en.yaml index 95c00ba00..2fb2fb819 100644 --- a/src-ui/res/history.en.yaml +++ b/src-ui/res/history.en.yaml @@ -32,7 +32,7 @@ aquapelago: "This genre was invented by Walker Anderson." aquarium: "This genre was invented by Inaba Naoki." armyants: "This genre first appeared in Puzzle Communication Nikoli vol. 158." balance: "This genre was invented by Prasanna Seshadri." -cave: "This genre first appeared in Puzzle Communication Nikoli vol. 58 under the name 'Bag'." +cave: "This genre first appeared in Puzzle Communication Nikoli vol. 60 under the name 'Bag'." barns: "This genre first appeared in Puzzle Communication Nikoli vol. 114." bdblock: "This genre first appeared in Puzzle Communication Nikoli vol. 49." bonsan: "This genre first appeared in Puzzle Communication Nikoli vol. 96." From 6572151889ac533ec0c52faccefbf5bfc7acf8ba Mon Sep 17 00:00:00 2001 From: Lennard Sprong Date: Mon, 24 Nov 2025 19:32:30 +0100 Subject: [PATCH 02/12] Translated using Weblate (English) Currently translated at 100.0% (305 of 305 strings) Translation: pzprjs/Genre Rules Translate-URL: https://hosted.weblate.org/projects/pzprjs/rules/en/ --- src-ui/res/rules.en.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-ui/res/rules.en.yaml b/src-ui/res/rules.en.yaml index fd0947b90..f58bf2bde 100644 --- a/src-ui/res/rules.en.yaml +++ b/src-ui/res/rules.en.yaml @@ -357,4 +357,4 @@ sansaroad: "Shade some empty cells on the board to form a single unshaded region sendai: "Draw lines over the dotted lines to divide each larger region (prefecture) into smaller regions (cities). Cities are allowed to be the same size as its prefecture.\n1. Every city must be orthogonally adjacent to at least one other city with the same shape and orientation.\n2. A number indicates the amount of cities in a prefecture. A prefecture without a number may have any amount of cities.\n3. There can be no dead-end lines." keywest: "Place a number between 0 and 4 in every island, and draw orthogonal bridges connecting them.\n1. Orthogonally adjacent islands cannot have the same number.\n2. Numbers indicate how many bridges are connected to this island.\n3. All islands with a number above 0 must be connected into a single network." energywalk: "Draw lines to form a single network that goes through every numbered cell.\n1. The network cannot have dead ends.\n2. Every visited energy cell must split off in all possible directions.\n3. The network cannot branch off or cross on unshaded cells.\n4. The network must not connect an energy cell to itself via only unshaded cells.\n5. A number indicates how many cells make up the continuous unshaded section of the network that the number is on." -morningwalk: "Draw a loop that goes through every numbered cell.\n1. The loop cannot branch off or cross itself.\n2. A number indicates how many cells make up the continuous white section of the loop that the number is on.\n3. The lengths of two successive different coloured sections of the loop must not be equal." +morningwalk: "Draw a loop that goes through every numbered cell.\n1. The loop cannot branch off or cross itself.\n2. A number indicates how many cells make up the continuous white section of the loop that the number is on.\n3. The lengths of two successive differently coloured sections of the loop must not be equal." From 82f8b4cad312210a41fc6ed1ed53fe415cd8052e Mon Sep 17 00:00:00 2001 From: Lennard Sprong Date: Mon, 24 Nov 2025 20:26:29 +0100 Subject: [PATCH 03/12] Translated using Weblate (Japanese) Currently translated at 99.1% (226 of 228 strings) Translation: pzprjs/Genre History Translate-URL: https://hosted.weblate.org/projects/pzprjs/genre-history/ja/ --- src-ui/res/history.ja.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-ui/res/history.ja.yaml b/src-ui/res/history.ja.yaml index aaae7ad87..9843146ab 100644 --- a/src-ui/res/history.ja.yaml +++ b/src-ui/res/history.ja.yaml @@ -102,7 +102,7 @@ box: パズル通信ニコリ vol.24より wblink: パズル通信ニコリ vol.45より yajikazu: パズル通信ニコリ vol.74より simplegako: わんど氏発案 -cave: パズル通信ニコリ vol.58より(当時のパズル名は「バッグ」) +cave: パズル通信ニコリ vol.60より(当時のパズル名は「バッグ」) tontonbeya: パズル通信ニコリ vol.180より oyakodori: 齋藤スバル氏発案 sashikazune: パズル通信ニコリ vol.161より From acf9c4b548831e8c4c8826871479ef380701ec88 Mon Sep 17 00:00:00 2001 From: Lennard Sprong Date: Mon, 24 Nov 2025 19:35:07 +0100 Subject: [PATCH 04/12] Translated using Weblate (Japanese) Currently translated at 96.7% (295 of 305 strings) Translation: pzprjs/Genre Rules Translate-URL: https://hosted.weblate.org/projects/pzprjs/rules/ja/ --- src-ui/res/rules.ja.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src-ui/res/rules.ja.yaml b/src-ui/res/rules.ja.yaml index 49632c871..8cb25707e 100644 --- a/src-ui/res/rules.ja.yaml +++ b/src-ui/res/rules.ja.yaml @@ -314,3 +314,5 @@ tetrochaink: "1. 盤面のいくつかのマスを黒くぬりましょう。\n2 sansaroad: "盤面のいくつかのマスを黒くぬりましょう。\n1. 小さな丸は、その丸が接している周囲のマスのうち、黒マスと白マスのどちらの数が多いかを表しています。黒丸なら黒マスの方が多く、白丸なら白マスの方が多いということです。灰色の丸は白マスと黒マスが同数であることを表しています。\n2. ▽のマスはすべて白マスになります。また、▽のマスにタテヨコに隣り合うマスのうち、ちょうど3マスだけが白マスになります。▽のマス以外の白マスでは、タテヨコに隣り合うマスのうち、ちょうど2マスだけが白マスになります。\n3. すべての白マスはタテヨコにひとつながりになっていなければなりません。\n4. 白マスが2×2以上のカタマリになってはいけません。" sendai: "点線上に線を引き、すでに太線で区切られた大部屋(県)を、いくつかの小さな部屋(市)に分けます。線が引かれない(市を1つしか持たない)県があってもかまいません。\n1. すべての市は、形も向きも同じになる他の市と辺(市境)を共有しなければなりません。\n2. 数字は、その県が含む市の数を表します。" keywest: "丸(シマといいます)の中に、0~4の数字のどれかを入れます。\n1. 同じ数字はタテヨコに連続しません。\n2. 数字は、その数字のあるシマからタテヨコに隣接したシマへかける橋の数です。\n3. 橋は1ヵ所に1本しかかけられません。\n4. 0が入るシマ以外のすべてのシマは、橋によって1つにつながっていなければいけません。" +morningwalk: "1. 盤面にタテヨコに線を引き、全体で1つの輪っかを作り、すべての数字のマスを通りましょう。\n2. 輪っかを作る線はマスの中央を通り、盤面の外に出たり、交差したり枝分かれしたり行き止まりがあったりしてはいけません。\n3. 数字は、その数字のマスが含まれる、連続した白マス上の線のマス数を表します。\n4. 輪っかを作る線に沿ってみた時、連続した白マス上の線のマス数は、その前後に隣り合う灰色マス上の線のマス数のいずれとも異なる数でなければなりません。" +energywalk: "1. 盤面のいくつかのマスに線を引いて、全体で1つのネットワークを作りましょう。このネットワークは、すべての数字のマスを通ります。\n2. 線はマスの中央を通るようにタテヨコに引きます。線は盤面の外に出てはいけません。\n3. 黄色のマスを「エネルギーマス」と呼び、それ以外のマスを「通常マス」と呼びます。\n4. 通常マスの上では、ネットワークに枝分かれや行き止まりがあってはいけません。\n5. エネルギーマスの上では、そのマスから進むことができる(盤面の外に出ない)すべての方向に線が1本ずつ伸びていなければなりません。\n6. ネットワークに、通常マスだけを通って、あるエネルギーマスから再び同じエネルギーマスに戻ってくる部分があってはいけません。\n7. 数字は、線が通常マスに入ってからその数字のマスを通り、次にエネルギーマスに入るまでに通る通常マスの数を表します。「?」は任意の数を表します。" From cac820e2d1b6f7cec250cebc8387c149a7fb54c0 Mon Sep 17 00:00:00 2001 From: Lennard Sprong Date: Sun, 30 Nov 2025 21:35:27 +0100 Subject: [PATCH 05/12] Add Key West --- src-ui/changes.html | 2 +- src-ui/js/ui/KeyPopup.js | 3 +- src-ui/list.html | 1 + src/puzzle/Config.js | 5 +- src/pzpr/variety.js | 1 + src/res/failcode.en.json | 5 +- src/variety/keywest.js | 158 +++++++++++++++++++++++++++++++++++++++ test/script/keywest.js | 33 ++++++++ 8 files changed, 203 insertions(+), 5 deletions(-) create mode 100644 src/variety/keywest.js create mode 100644 test/script/keywest.js diff --git a/src-ui/changes.html b/src-ui/changes.html index 58ea5a8eb..19782b449 100644 --- a/src-ui/changes.html +++ b/src-ui/changes.html @@ -33,13 +33,13 @@
Latest types (all types)
diff --git a/src-ui/js/ui/KeyPopup.js b/src-ui/js/ui/KeyPopup.js index 2a4d3185a..08c832a39 100644 --- a/src-ui/js/ui/KeyPopup.js +++ b/src-ui/js/ui/KeyPopup.js @@ -239,7 +239,8 @@ ui.keypopup = { cityspace: [10, 0], diamond: [4, 0], morningwalk: [10, 0], - energywalk: [10, 0] + energywalk: [10, 0], + keywest: [4, 4] }, //--------------------------------------------------------------------------- diff --git a/src-ui/list.html b/src-ui/list.html index fb7fa24a6..acf42fbe4 100644 --- a/src-ui/list.html +++ b/src-ui/list.html @@ -242,6 +242,7 @@

パズルの種類のリスト
  • +
  • diff --git a/src/puzzle/Config.js b/src/puzzle/Config.js index 3a8a2fb4e..753b534cb 100644 --- a/src/puzzle/Config.js +++ b/src/puzzle/Config.js @@ -458,7 +458,10 @@ break; case "forceallcell": exec = - pid === "fillomino" || pid === "symmarea" || pid === "snakepit"; + pid === "fillomino" || + pid === "symmarea" || + pid === "snakepit" || + pid === "keywest"; break; case "dontpassallcell": exec = pid === "arukone"; diff --git a/src/pzpr/variety.js b/src/pzpr/variety.js index 3bb981235..51093f627 100755 --- a/src/pzpr/variety.js +++ b/src/pzpr/variety.js @@ -231,6 +231,7 @@ kakuro: [0, 1, "カックロ", "Kakuro"], kakuru: [0, 0, "カックル", "Kakuru"], kazunori: [0, 0, "かずのりのへや", "Kazunori Room"], + keywest: [0, 0, "キーウエスト", "Key West"], kinkonkan: [1, 0, "キンコンカン", "Kin-Kon-Kan"], kissing: [ 0, diff --git a/src/res/failcode.en.json b/src/res/failcode.en.json index 5ed856653..9d22c0a96 100644 --- a/src/res/failcode.en.json +++ b/src/res/failcode.en.json @@ -766,10 +766,11 @@ "nmIgnored.herugolf": "There is a Hole without a ball.", "nmIneqNe.minarism": "A inequality sign is not correct.", "nmInside": "A wolf is inside the loop.", - "nmLineGt.hashikake": "The number of connecting bridges to a number is not correct.", + "nmLineAdjacent.keywest": "Two adjacent islands have the same number of connecting bridges.", + "nmLineGt.hashikake": "The number of bridges connecting to a number is not correct.", "nmLineGt.kaidan": "The number of blocks adjacent to a number is not correct.", "nmLineGt1.amibo": "Multiple lines connect to a white circle.", - "nmLineLt.hashikake": "The number of connecting bridges to a number is not correct.", + "nmLineLt.hashikake": "The number of bridges connecting to a number is not correct.", "nmLineLt.kaidan": "The number of blocks adjacent to a number is not correct.", "nmLineLt2.kouchoku": "A clue doesn't have two segments.", "nmLineNe.ichimaga": "The number is not equal to the number of lines out of the circle.", diff --git a/src/variety/keywest.js b/src/variety/keywest.js new file mode 100644 index 000000000..0875e34ed --- /dev/null +++ b/src/variety/keywest.js @@ -0,0 +1,158 @@ +(function(pidlist, classbase) { + if (typeof module === "object" && module.exports) { + module.exports = [pidlist, classbase]; + } else { + pzpr.classmgr.makeCustom(pidlist, classbase); + } +})(["keywest"], { + MouseEvent: { + inputModes: { + edit: ["number", "line", "peke", "clear"], + play: ["number", "line", "peke", "clear"] + }, + + mouseinput_clear: function() { + this.inputFixedNumber(-1); + }, + + mouseinput_auto: function() { + if (this.btn === "left") { + this.inputLine(); + } else { + this.inputpeke(); + } + if (this.mouseend && this.notInputted()) { + this.inputqnum(); + } + } + }, + + KeyEvent: { + enablemake: true, + enableplay: true + }, + + Cell: { + disInputHatena: true, + minnum: 0, + maxnum: 4 + }, + + Board: { + cols: 7, + rows: 7, + + hasborder: 1 + }, + + LineGraph: { + enabled: true, + makeClist: true + }, + + Graphic: { + gridcolor_type: "THIN", + irowake: true, + + circleratio: [0.37, 0.33], + + lwratio: 8, + + paint: function() { + this.drawPekes(); + this.drawLines(); + + this.drawCircledNumbers(); + this.drawAnsNumbers(); + + this.drawCursor(); + }, + + drawAnsNumbers: function() { + this.vinc("cell_ans_number", "auto"); + this.drawNumbers_com( + this.getAnsNumberText, + this.getAnsNumberColor, + "cell_ans_text_", + { ratio: 0.65 } + ); + }, + + getQuesNumberColor: function(cell) { + return cell.error === 1 ? this.errcolor1 : this.quescolor; + }, + getCircleStrokeColor: function(cell) { + // Also shows connectivity error + return cell.error ? this.errcolor1 : this.quescolor; + }, + getCircleFillColor: function(cell) { + return cell.error === 1 ? this.errbcolor1 : "white"; + } + }, + + Encode: { + decodePzpr: function(type) { + this.decode4Cell(); + }, + encodePzpr: function(type) { + this.encode4Cell(); + } + }, + //--------------------------------------------------------- + FileIO: { + decodeData: function() { + this.decodeCellQnum(); + this.decodeBorderLine(); + this.decodeCellAnumsub(); + }, + encodeData: function() { + this.encodeCellQnum(); + this.encodeBorderLine(); + this.encodeCellAnumsub(); + } + }, + + //--------------------------------------------------------- + // 正解判定処理実行部 + AnsCheck: { + checklist: [ + "checkAdjacentDiffNumber", + "checkCellNumberNotOver", + "checkCellNumberNotLess", + "checkConnectAllNumber", + "checkAdjacentDiffWithoutNumber", + "checkNoNumCell_keywest+" + ], + + checkCellNumberNotOver: function() { + this.checkAllCell(function(cell) { + return cell.isValidNum() && cell.getNum() < cell.lcnt; + }, "nmLineGt"); + }, + checkCellNumberNotLess: function() { + this.checkAllCell(function(cell) { + return cell.isValidNum() && cell.getNum() > cell.lcnt; + }, "nmLineLt"); + }, + checkAdjacentDiffWithoutNumber: function() { + this.checkSideCell(function(cell1, cell2) { + if (cell1.isValidNum() && cell2.isValidNum()) { + return false; + } + + return cell1.lcnt === cell2.lcnt; + }, "nmLineAdjacent"); + }, + checkNoNumCell_keywest: function() { + if (this.puzzle.getConfig("forceallcell")) { + this.checkAllCell(function(cell) { + return cell.noNum(); + }, "ceNoNum"); + } + } + }, + FailCode: { + nmLineGt: "nmLineGt.hashikake", + nmLineLt: "nmLineLt.hashikake" + } +}); diff --git a/test/script/keywest.js b/test/script/keywest.js new file mode 100644 index 000000000..1a8ed8194 --- /dev/null +++ b/test/script/keywest.js @@ -0,0 +1,33 @@ +ui.debug.addDebugData("keywest", { + url: "5/5/gb7ej1008ega", + failcheck: [ + [ + "nmLineAdjacent", + "pzprv3/keywest/5/5/. 1 . . 2 /. 4 . . . /. . . 1 0 /0 3 . 4 . /. . 0 . . /0 0 1 1 /1 1 1 0 /0 0 0 0 /0 1 1 1 /0 0 0 0 /0 1 1 1 1 /1 1 0 0 0 /0 1 0 1 0 /0 1 0 1 0 /. . . . . /. . . . . /. . . . . /. . . . . /. . . . . /" + ], + [ + "nmAdjacent", + "pzprv3/keywest/5/5/. 1 . . 2 /. 4 . . . /. . . 1 0 /0 3 . 4 . /. . 0 . . /0 0 1 1 /1 1 1 0 /0 0 0 0 /0 1 1 1 /1 0 0 0 /1 1 1 1 1 /1 1 0 0 0 /0 1 0 1 0 /0 1 0 1 0 /1 . 2 3 . /3 . 3 2 1 /1 2 0 . . /. . 2 . 1 /1 2 . 1 0 /" + ], + [ + "nmLineLt", + "pzprv3/keywest/5/5/. 1 . . 2 /. 4 . . . /. . . 1 0 /0 3 . 4 . /. . 0 . . /0 0 1 1 /1 1 1 1 /0 0 0 0 /0 1 1 1 /1 0 0 0 /0 1 1 1 0 /1 1 0 1 0 /0 1 0 0 0 /0 1 0 1 0 /0 . 2 3 . /2 . 3 4 1 /1 2 0 . . /. . 2 . 1 /1 2 . 1 0 /" + ], + [ + "nmLineGt", + "pzprv3/keywest/5/5/. 1 . . 2 /. 4 . . . /. . . 1 0 /0 3 . 4 . /. . 0 . . /0 0 1 1 /1 1 1 0 /0 0 0 0 /0 1 1 1 /1 0 0 0 /1 1 1 1 1 /1 1 0 0 0 /0 1 0 1 0 /0 1 0 1 0 /0 . 2 3 . /3 . 3 2 1 /1 2 0 . . /. . 2 . 1 /1 2 . 1 0 /" + ], + [ + "ceNoNum", + "pzprv3/keywest/5/5/. 1 . . 2 /. 4 . . . /. . . 1 0 /0 3 . 4 . /. . 0 . . /0 0 1 1 /1 1 1 0 /0 0 0 0 /0 1 1 1 /1 0 0 0 /0 1 1 1 1 /1 1 0 0 0 /0 1 0 1 0 /0 1 0 1 0 /. . 2 3 . /. . 3 2 1 /. . . . . /. . 2 . 1 /1 2 . 1 0 /" + ], + [ + "lcDivided", + "pzprv3/keywest/5/5/. 1 . . 2 /. 4 . . . /. . . 1 0 /0 3 . 4 . /. . 0 . . /0 0 0 1 /1 1 0 0 /0 0 0 0 /0 1 1 1 /1 0 0 0 /0 1 0 0 1 /1 1 0 0 0 /0 1 0 1 0 /0 1 0 1 0 /0 . 0 1 . /2 . 1 0 1 /1 2 0 . . /. . 2 . 1 /1 2 . 1 0 /" + ], + [ + null, + "pzprv3/keywest/5/5/. 1 . . 2 /. 4 . . . /. . . 1 0 /0 3 . 4 . /. . 0 . . /0 0 1 1 /1 1 1 0 /0 0 0 0 /0 1 1 1 /1 0 0 0 /0 1 1 1 1 /1 1 0 0 0 /0 1 0 1 0 /0 1 0 1 0 /0 . 2 3 . /2 . 3 2 1 /1 2 0 . . /. . 2 . 1 /1 2 . 1 0 /" + ] + ] +}); From 7dff3b2af0221ca31377e75323a3f81f919b2475 Mon Sep 17 00:00:00 2001 From: Lennard Sprong Date: Mon, 8 Dec 2025 18:28:35 +0100 Subject: [PATCH 06/12] Translated using Weblate (Japanese) Currently translated at 100.0% (228 of 228 strings) Translation: pzprjs/Genre History Translate-URL: https://hosted.weblate.org/projects/pzprjs/genre-history/ja/ --- src-ui/res/history.ja.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src-ui/res/history.ja.yaml b/src-ui/res/history.ja.yaml index 9843146ab..4ddd86640 100644 --- a/src-ui/res/history.ja.yaml +++ b/src-ui/res/history.ja.yaml @@ -224,3 +224,5 @@ tetrochaink: パズル通信ニコリ vol.192より sansaroad: パズル通信ニコリ vol.185より sendai: パズル通信ニコリ vol.104より keywest: パズル通信ニコリ vol.53より +morningwalk: Anurag Sahay氏発案 +energywalk: Parachor氏発案 From e590ff337bf35c52037512dd33a33a7821f30cf4 Mon Sep 17 00:00:00 2001 From: Lennard Sprong Date: Sun, 14 Dec 2025 11:39:16 +0100 Subject: [PATCH 07/12] Translated using Weblate (English) Currently translated at 100.0% (309 of 309 strings) Translation: pzprjs/Genre Rules Translate-URL: https://hosted.weblate.org/projects/pzprjs/rules/en/ --- src-ui/res/rules.en.yaml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src-ui/res/rules.en.yaml b/src-ui/res/rules.en.yaml index f58bf2bde..1cca32125 100644 --- a/src-ui/res/rules.en.yaml +++ b/src-ui/res/rules.en.yaml @@ -358,3 +358,29 @@ sendai: "Draw lines over the dotted lines to divide each larger region (prefectu keywest: "Place a number between 0 and 4 in every island, and draw orthogonal bridges connecting them.\n1. Orthogonally adjacent islands cannot have the same number.\n2. Numbers indicate how many bridges are connected to this island.\n3. All islands with a number above 0 must be connected into a single network." energywalk: "Draw lines to form a single network that goes through every numbered cell.\n1. The network cannot have dead ends.\n2. Every visited energy cell must split off in all possible directions.\n3. The network cannot branch off or cross on unshaded cells.\n4. The network must not connect an energy cell to itself via only unshaded cells.\n5. A number indicates how many cells make up the continuous unshaded section of the network that the number is on." morningwalk: "Draw a loop that goes through every numbered cell.\n1. The loop cannot branch off or cross itself.\n2. A number indicates how many cells make up the continuous white section of the loop that the number is on.\n3. The lengths of two successive differently coloured sections of the loop must not be equal." +cornerch: |- + Shade some cells on the board to form regions of unshaded cells. + 1. A number indicates the size of the region that contains it.. + 2. Clues represent the number of cells in the region they belong to. A region can contain one or more identical numbers, or none at all. + 3. If the number of cells in an unshaded group is even, the area must be rectangular. If the number of cells is odd, the area must not be rectangular. + 4. All unshaded areas must be connected diagonally. +oasis: |- + Shade some cells on the board. + 1. Shaded cells cannot be horizontally or vertically adjacent. + 2. Numbers cannot be shaded. + 3. The unshaded cells cannot form a 2x2 square. + 4. A circled number indicates how many other circles it could reach by traveling only through empty unshaded cells. + 5. All unshaded cells on the board form an orthogonally connected area. +circuitwalk: |- + Draw one or more loops so every number is visited once. + 1. A loop cannot branch off or overlap itself or another loop. + 2. Every visited shaded cell must consist of exactly two straight line segments belonging to different loops crossing over each other. + 3. Crossings cannot appear outside of a shaded cell. + 4. A number indicates how many cells make up the continuous white section of the loop that the number is on. + 5. All loops must form a single connected network. +edamame: |- + Draw several rectangles of length 1xN (at least 3), then place circles inside them. + 1. Rectangles cannot overlap numbers. + 2. Every rectangle contains exactly three circles: One on each endpoint, and another somewhere in the middle. + 3. A number indicates the amount of circles in the 4 orthogonally adjacent cells. Circles cannot appear outside of rectangles. + 4. All cells not used by rectangles form an orthogonally contiguous area. From dc89e477a205fb557ede19872fd83f60aea98884 Mon Sep 17 00:00:00 2001 From: Lennard Sprong Date: Sun, 14 Dec 2025 11:41:54 +0100 Subject: [PATCH 08/12] Translated using Weblate (English) Currently translated at 100.0% (230 of 230 strings) Translation: pzprjs/Genre History Translate-URL: https://hosted.weblate.org/projects/pzprjs/genre-history/en/ --- src-ui/res/history.en.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src-ui/res/history.en.yaml b/src-ui/res/history.en.yaml index 2fb2fb819..38de1522a 100644 --- a/src-ui/res/history.en.yaml +++ b/src-ui/res/history.en.yaml @@ -226,3 +226,5 @@ sendai: This genre first appeared in Puzzle Communication Nikoli vol. 104. keywest: This genre first appeared in Puzzle Communication Nikoli vol. 53. morningwalk: This genre was invented by Anurag Sahay. energywalk: This genre was invented by Parachor. +circuitwalk: This genre was invented by Parachor. +cornerch: This genre first appeared in Puzzle Communication Nikoli vol. 191. From 248ea08e506914e2ce6ff9aa08e23a61b1369c37 Mon Sep 17 00:00:00 2001 From: Lennard Sprong Date: Sun, 14 Dec 2025 11:42:04 +0100 Subject: [PATCH 09/12] Translated using Weblate (Japanese) Currently translated at 100.0% (230 of 230 strings) Translation: pzprjs/Genre History Translate-URL: https://hosted.weblate.org/projects/pzprjs/genre-history/ja/ --- src-ui/res/history.ja.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src-ui/res/history.ja.yaml b/src-ui/res/history.ja.yaml index 4ddd86640..6e4725a9b 100644 --- a/src-ui/res/history.ja.yaml +++ b/src-ui/res/history.ja.yaml @@ -226,3 +226,5 @@ sendai: パズル通信ニコリ vol.104より keywest: パズル通信ニコリ vol.53より morningwalk: Anurag Sahay氏発案 energywalk: Parachor氏発案 +circuitwalk: Parachor氏発案 +cornerch: パズル通信ニコリ vol.191より From 02ff721feb5b26ae27c2e9fc23f5498b53cd3a69 Mon Sep 17 00:00:00 2001 From: Lennard Sprong Date: Sun, 14 Dec 2025 11:43:30 +0100 Subject: [PATCH 10/12] Translated using Weblate (Japanese) Currently translated at 95.7% (296 of 309 strings) Translation: pzprjs/Genre Rules Translate-URL: https://hosted.weblate.org/projects/pzprjs/rules/ja/ --- src-ui/res/rules.ja.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src-ui/res/rules.ja.yaml b/src-ui/res/rules.ja.yaml index 8cb25707e..ac8ecfc35 100644 --- a/src-ui/res/rules.ja.yaml +++ b/src-ui/res/rules.ja.yaml @@ -316,3 +316,9 @@ sendai: "点線上に線を引き、すでに太線で区切られた大部屋 keywest: "丸(シマといいます)の中に、0~4の数字のどれかを入れます。\n1. 同じ数字はタテヨコに連続しません。\n2. 数字は、その数字のあるシマからタテヨコに隣接したシマへかける橋の数です。\n3. 橋は1ヵ所に1本しかかけられません。\n4. 0が入るシマ以外のすべてのシマは、橋によって1つにつながっていなければいけません。" morningwalk: "1. 盤面にタテヨコに線を引き、全体で1つの輪っかを作り、すべての数字のマスを通りましょう。\n2. 輪っかを作る線はマスの中央を通り、盤面の外に出たり、交差したり枝分かれしたり行き止まりがあったりしてはいけません。\n3. 数字は、その数字のマスが含まれる、連続した白マス上の線のマス数を表します。\n4. 輪っかを作る線に沿ってみた時、連続した白マス上の線のマス数は、その前後に隣り合う灰色マス上の線のマス数のいずれとも異なる数でなければなりません。" energywalk: "1. 盤面のいくつかのマスに線を引いて、全体で1つのネットワークを作りましょう。このネットワークは、すべての数字のマスを通ります。\n2. 線はマスの中央を通るようにタテヨコに引きます。線は盤面の外に出てはいけません。\n3. 黄色のマスを「エネルギーマス」と呼び、それ以外のマスを「通常マス」と呼びます。\n4. 通常マスの上では、ネットワークに枝分かれや行き止まりがあってはいけません。\n5. エネルギーマスの上では、そのマスから進むことができる(盤面の外に出ない)すべての方向に線が1本ずつ伸びていなければなりません。\n6. ネットワークに、通常マスだけを通って、あるエネルギーマスから再び同じエネルギーマスに戻ってくる部分があってはいけません。\n7. 数字は、線が通常マスに入ってからその数字のマスを通り、次にエネルギーマスに入るまでに通る通常マスの数を表します。「?」は任意の数を表します。" +cornerch: |- + 1. 盤面のいくつかのマスを黒くぬりまし ょう。 + 2. 盤面の数字は、その数字が含まれる、 黒マスによって分断されたところ (シマ と呼びます)のマスの数を表します。 + 3. 1つのシマに、いくつ数字が入っても かまいません。また、数字が入っている マスを黒くぬってはいけません。 + 4. マスの数が偶数のシマは必ず長方形 (正方形も含みます)になります。また、 マスの数が奇数のシマは必ず長方形 (正 方形も含みます) 以外の形になります。 + 5. すべてのシマは頂点を通じてひとつな がりになっていなければなりません。 From 3be700edce3c49a372f8f34976169315a76a8911 Mon Sep 17 00:00:00 2001 From: Lennard Sprong Date: Mon, 15 Dec 2025 09:28:10 +0100 Subject: [PATCH 11/12] Add Sendai-Miyagi --- src-ui/changes.html | 2 +- src-ui/img/sendai.png | Bin 0 -> 231 bytes src-ui/js/ui/KeyPopup.js | 1 + src-ui/js/ui/Misc.js | 1 + src-ui/list.html | 1 + src/pzpr/variety.js | 1 + src/res/failcode.en.json | 2 + src/variety/sendai.js | 182 +++++++++++++++++++++++++++++++++++++++ test/script/sendai.js | 25 ++++++ 9 files changed, 214 insertions(+), 1 deletion(-) create mode 100644 src-ui/img/sendai.png create mode 100644 src/variety/sendai.js create mode 100644 test/script/sendai.js diff --git a/src-ui/changes.html b/src-ui/changes.html index 19782b449..6b3eb76cc 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/sendai.png b/src-ui/img/sendai.png new file mode 100644 index 0000000000000000000000000000000000000000..f24ddb8f469688f0673fada2077639772729f7a6 GIT binary patch literal 231 zcmeAS@N?(olHy`uVBq!ia0vp^20*O9!3-pmxw!R!6k~CayA#8@b22Z19L@lr5Z9?k z&+9~`ZP<6{ZiCJtpkR-ui(`mI@7oYZz5_t5|Ng&lH5WRlwBe0~p4;W9%ubihGyZ)& zJ#|OqKe>%lUsc(df9!XXtrwR1;rZeL`@&Z7bzaPc zeiu*Is`v+6cW8et-_HDq{os#%0ttGPo^IftQ+&gEK{8+6{&S(N3C}h-Y$%>Bd}q$` dmh1m7vHnwJGq%z=aTn+=22WQ%mvv4FO#nS=TnPXG literal 0 HcmV?d00001 diff --git a/src-ui/js/ui/KeyPopup.js b/src-ui/js/ui/KeyPopup.js index 08c832a39..18bda71ee 100644 --- a/src-ui/js/ui/KeyPopup.js +++ b/src-ui/js/ui/KeyPopup.js @@ -238,6 +238,7 @@ ui.keypopup = { numcity: [10, 10], cityspace: [10, 0], diamond: [4, 0], + sendai: [10, 0], morningwalk: [10, 0], energywalk: [10, 0], keywest: [4, 4] diff --git a/src-ui/js/ui/Misc.js b/src-ui/js/ui/Misc.js index 72e08b865..622ffa83d 100755 --- a/src-ui/js/ui/Misc.js +++ b/src-ui/js/ui/Misc.js @@ -209,6 +209,7 @@ function toBGimage(pid) { "sashikazune", "satogaeri", "scrin", + "sendai", "simplegako", "simpleloop", "skyscrapers", diff --git a/src-ui/list.html b/src-ui/list.html index acf42fbe4..dd7f47fb9 100644 --- a/src-ui/list.html +++ b/src-ui/list.html @@ -297,6 +297,7 @@

    パズルの種類のリスト
  • +
  • diff --git a/src/pzpr/variety.js b/src/pzpr/variety.js index 51093f627..35cfb1870 100755 --- a/src/pzpr/variety.js +++ b/src/pzpr/variety.js @@ -396,6 +396,7 @@ { alias: "sato", kanpen: "satogaeri" } ], scrin: [0, 0, "スクリン", "Scrin"], + sendai: [0, 0, "宮城県仙台市", "Sendai-Miyagi"], shakashaka: [0, 1, "シャカシャカ", "Shakashaka"], shikaku: [0, 1, "四角に切れ", "Shikaku", "shikaku"], shimaguni: [1, 0, "島国", "Shimaguni", "shimaguni"], diff --git a/src/res/failcode.en.json b/src/res/failcode.en.json index 9d22c0a96..942e96864 100644 --- a/src/res/failcode.en.json +++ b/src/res/failcode.en.json @@ -97,6 +97,7 @@ "bkCircleNe.familyphoto": "A number does not indicate the amount of circles in the area.", "bkCircleNe.fracdiv": "A number does not indicate the ratio of circles and cells in the area.", "bkClue.toichika2": "A number does not match the clue for the country.", + "bkCountNe.sendai": "A number does not indicate the amount of cities in the outlined prefecture.", "bkDifferentLetters.nikoji": "Two areas with different letters have the same shape.", "bkDifferentLines.pmemory": "Two regions of the same shape have different lines.", "bkDifferentOrientation.nikoji": "Two areas with equal letters have different orientation.", @@ -147,6 +148,7 @@ "bkMUPassedGt2.moonsun": "A line passes the marks of the sun for two rooms in a row.", "bkNoChain.chainedb": "A block is not diagonally adjacent to another block.", "bkNoChain.mrtile": "A block is not diagonally adjacent to another identical block.", + "bkNoChain.sendai": "A city is not adjacent to another city with the same shape and orientation.", "bkNoColor.interbd": "A country has no color.", "bkNoLevel.aquarium": "A region has different water surface levels.", "bkNoLine.country": "A line doesn't pass a country.", diff --git a/src/variety/sendai.js b/src/variety/sendai.js new file mode 100644 index 000000000..72056ad36 --- /dev/null +++ b/src/variety/sendai.js @@ -0,0 +1,182 @@ +// +// sendai.js +// +(function(pidlist, classbase) { + if (typeof module === "object" && module.exports) { + module.exports = [pidlist, classbase]; + } else { + pzpr.classmgr.makeCustom(pidlist, classbase); + } +})(["sendai"], { + MouseEvent: { + inputModes: { + edit: ["border", "number"], + play: ["border", "subline"] + }, + autoedit_func: "areanum", + autoplay_func: "border" + }, + + KeyEvent: { + enablemake: true + }, + + Cell: { + maxnum: function() { + return this.room ? this.room.clist.length : 1; + } + }, + Board: { + hasborder: 1, + addExtraInfo: function() { + this.blockgraph = this.addInfoList(this.klass.AreaBlockGraph); + } + }, + + AreaRoomGraph: { + countprop: "l2cnt", + enabled: true, + hastop: true, + isedgevalidbylinkobj: function(border) { + return !border.ques; + } + }, + "AreaBlockGraph:AreaRoomGraph": { + countprop: "lcnt", + enabled: true, + getComponentRefs: function(obj) { + return obj.block; + }, + setComponentRefs: function(obj, component) { + obj.block = component; + }, + getObjNodeList: function(nodeobj) { + return nodeobj.blocknodes; + }, + resetObjNodeList: function(nodeobj) { + nodeobj.blocknodes = []; + }, + isedgevalidbylinkobj: function(border) { + return !border.isBorder(); + } + }, + + Graphic: { + gridcolor_type: "DLIGHT", + + paint: function() { + this.drawBGCells(); + this.drawDashedGrid(); + + this.drawQuesNumbers(); + + this.addlw = -1; + this.drawQansBorders(); + this.addlw = 0; + this.drawQuesBorders(); + this.drawBorderQsubs(); + + this.drawChassis(); + this.drawTarget(); + } + }, + + Encode: { + decodePzpr: function(type) { + this.decodeBorder(); + this.decodeRoomNumber16(); + }, + encodePzpr: function(type) { + this.encodeBorder(); + this.encodeRoomNumber16(); + } + }, + FileIO: { + decodeData: function() { + this.decodeCellQnum(); + this.decodeBorder(function(border, ca) { + var value = +ca; + if (value & 4) { + border.qsub = 1; + value &= ~4; + } + if (value === 1) { + border.ques = 1; + } else if (value === 2) { + border.qans = 1; + } + }); + }, + encodeData: function() { + this.encodeCellQnum(); + this.encodeBorder(function(border) { + var value = border.ques ? 1 : border.qans ? 2 : 0; + if (border.qsub) { + value |= 4; + } + return value + " "; + }); + } + }, + + AnsCheck: { + checklist: ["checkAdjacentExist", "checkRoomCount", "checkBorderDeadend+"], + checkRoomCount: function() { + var rooms = this.board.roommgr.components; + var blocks = this.board.blockgraph.components; + for (var r = 0; r < rooms.length; r++) { + rooms[r].blocks = 0; + } + for (var r = 0; r < blocks.length; r++) { + blocks[r].clist[0].room.blocks++; + } + for (var r = 0; r < rooms.length; r++) { + var room = rooms[r]; + if (room.top.isValidNum() && room.top.qnum !== room.blocks) { + this.failcode.add("bkCountNe"); + if (this.checkOnly) { + break; + } + room.clist.seterr(1); + } + } + }, + checkAdjacentExist: function() { + var bd = this.board; + + var blocks = bd.blockgraph.components; + for (var r = 0; r < blocks.length; r++) { + blocks[r].valid = false; + } + + for (var b = 0; b < bd.border.length; b++) { + var border = bd.border[b]; + if (!border.isBorder()) { + continue; + } + + var ca = border.sidecell[0], + cb = border.sidecell[1]; + + if ( + ca.block !== cb.block && + ca.block.clist.getBlockShapes().id === + cb.block.clist.getBlockShapes().id + ) { + ca.block.valid = true; + cb.block.valid = true; + } + } + + for (var r = 0; r < blocks.length; r++) { + if (!blocks[r].valid) { + this.failcode.add("bkNoChain"); + if (this.checkOnly) { + break; + } + blocks[r].clist.seterr(1); + } + } + } + } +}); diff --git a/test/script/sendai.js b/test/script/sendai.js new file mode 100644 index 000000000..afd92562f --- /dev/null +++ b/test/script/sendai.js @@ -0,0 +1,25 @@ +/* sendai.js */ + +ui.debug.addDebugData("sendai", { + url: "4/4/8qg5583g1g", + failcheck: [ + [ + "bkNoChain", + "pzprv3/sendai/4/4/3 . . . /. . . . /1 . . . /. . . . /0 1 0 /0 2 1 /1 2 1 /0 1 0 /2 2 1 2 /1 0 0 1 /0 1 0 1 /" + ], + [ + "bkCountNe", + "pzprv3/sendai/4/4/3 . . . /. . . . /1 . . . /. . . . /2 1 2 /2 2 1 /1 2 1 /0 1 0 /0 2 1 2 /1 0 2 1 /0 1 0 1 /" + ], + [ + "bdDeadEnd", + "pzprv3/sendai/4/2/. . /. . /. . /. . /0 /0 /0 /0 /2 0 /2 2 /2 0 /", + { skiprules: true } + ], + [ + null, + "pzprv3/sendai/4/4/3 . . . /. . . . /1 . . . /. . . . /0 1 2 /2 0 1 /1 2 1 /0 1 0 /0 2 1 2 /1 0 2 1 /0 1 0 1 /" + ] + ], + inputs: [] +}); From aa7ddce47b27c14f8294a400c61714e40f34b786 Mon Sep 17 00:00:00 2001 From: Lennard Sprong Date: Mon, 15 Dec 2025 13:28:50 +0100 Subject: [PATCH 12/12] sendai: Adjust example puzzle --- test/script/sendai.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/script/sendai.js b/test/script/sendai.js index afd92562f..39cb33aff 100644 --- a/test/script/sendai.js +++ b/test/script/sendai.js @@ -1,15 +1,15 @@ /* sendai.js */ ui.debug.addDebugData("sendai", { - url: "4/4/8qg5583g1g", + url: "4/4/8qg51g3g1", failcheck: [ [ "bkNoChain", - "pzprv3/sendai/4/4/3 . . . /. . . . /1 . . . /. . . . /0 1 0 /0 2 1 /1 2 1 /0 1 0 /2 2 1 2 /1 0 0 1 /0 1 0 1 /" + "pzprv3/sendai/4/4/3 . . . /. . . . /1 . . . /. . . . /0 1 2 /2 2 1 /1 0 1 /0 1 2 /0 2 1 0 /1 0 2 2 /0 1 1 0 /" ], [ "bkCountNe", - "pzprv3/sendai/4/4/3 . . . /. . . . /1 . . . /. . . . /2 1 2 /2 2 1 /1 2 1 /0 1 0 /0 2 1 2 /1 0 2 1 /0 1 0 1 /" + "pzprv3/sendai/4/4/3 . . . /. . . . /1 . . . /. . . . /2 1 2 /0 2 1 /1 0 1 /0 1 0 /0 2 1 2 /1 2 2 2 /0 1 1 2 /" ], [ "bdDeadEnd", @@ -18,7 +18,7 @@ ui.debug.addDebugData("sendai", { ], [ null, - "pzprv3/sendai/4/4/3 . . . /. . . . /1 . . . /. . . . /0 1 2 /2 0 1 /1 2 1 /0 1 0 /0 2 1 2 /1 0 2 1 /0 1 0 1 /" + "pzprv3/sendai/4/4/3 . . . /. . . . /1 . . . /. . . . /2 1 2 /0 2 1 /1 0 1 /0 1 0 /0 2 1 2 /1 2 0 2 /0 1 1 0 /" ] ], inputs: []