diff --git a/src-ui/changes.html b/src-ui/changes.html index e2e43e28e..4cef4dad9 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 4684106af..15cf5a4d4 100644 --- a/src-ui/js/ui/KeyPopup.js +++ b/src-ui/js/ui/KeyPopup.js @@ -243,7 +243,8 @@ ui.keypopup = { morningwalk: [10, 0], energywalk: [10, 0], cornerch: [10, 0], - keywest: [4, 4] + keywest: [4, 4], + oasis: [10, 0] }, //--------------------------------------------------------------------------- diff --git a/src-ui/list.html b/src-ui/list.html index affe78436..99ca64964 100644 --- a/src-ui/list.html +++ b/src-ui/list.html @@ -123,6 +123,7 @@

パズルの種類のリスト
  • +
  • diff --git a/src/pzpr/variety.js b/src/pzpr/variety.js index 83a60fb62..f959b0196 100755 --- a/src/pzpr/variety.js +++ b/src/pzpr/variety.js @@ -326,6 +326,7 @@ nurimisaki: [0, 0, "ぬりみさき", "Nurimisaki", "kurodoko"], nuritwin: [0, 0, "ぬりツイン", "Nuritwin", "shimaguni"], nuriuzu: [0, 0, "ぬりうず", "Nuri-uzu", "tentaisho"], + oasis: [0, 0, "Oasis", "Oasis"], ovotovata: [0, 0, "Ovotovata", "Ovotovata", "country"], oneroom: [0, 0, "ワンルームワンドア", "One Room One Door", "heyawake"], onsen: [0, 0, "温泉めぐり", "Onsen-meguri", "country"], diff --git a/src/res/failcode.en.json b/src/res/failcode.en.json index 33c7a7a82..9d85f339f 100644 --- a/src/res/failcode.en.json +++ b/src/res/failcode.en.json @@ -815,6 +815,8 @@ "nmNumberNe.sukoro": "The number of numbers placed in four adjacent cells is not equal to the number.", "nmNumberNe.sukororoom": "The number of numbers placed in four adjacent cells is not equal to the number.", "nmNumberNe.view": "The number of numbers placed in four adjacent cells is not equal to the number.", + "nmOasisGt": "A number is larger than the amount of circles that can be reached.", + "nmOasisLt": "A number is smaller than the amount of circles that can be reached.", "nmOrbitNe.orbital": "A number does not indicate the amount of white circles used by the loop.", "nmOrder.numcity": "The size of a district is larger or equal to the size of the previous district.", "nmOutOfBk.oyakodori": "A bird is outside a nest.", diff --git a/src/variety/oasis.js b/src/variety/oasis.js new file mode 100644 index 000000000..93bae064a --- /dev/null +++ b/src/variety/oasis.js @@ -0,0 +1,320 @@ +/* global Set:false */ +(function(pidlist, classbase) { + if (typeof module === "object" && module.exports) { + module.exports = [pidlist, classbase]; + } else { + pzpr.classmgr.makeCustom(pidlist, classbase); + } +})(["oasis"], { + //--------------------------------------------------------- + // マウス入力系 + MouseEvent: { + RBShadeCell: true, + use: true, + inputModes: { + edit: ["number", "clear"], + play: ["shade", "unshade", "peke", "completion"] + }, + mouseinput_auto: function() { + if (this.puzzle.playmode) { + if (this.mousestart) { + this.isDraggingPeke = this.puzzle.key.isALT; + } + if (this.isDraggingPeke) { + this.inputpeke(); + } else if (this.btn === "left" && this.mousestart) { + if (!this.inputqcmp()) { + this.inputcell(); + } + } else if (this.inputData === null || this.inputData < 20) { + this.inputcell(); + } + } else if (this.puzzle.editmode) { + if (this.mousestart) { + this.inputqnum(); + } + } + }, + inputqcmp: function() { + var cell = this.getcell(); + if (cell.isnull || !cell.isNum()) { + return false; + } + + this.inputData = 20; + cell.setQcmp(+!cell.qcmp); + cell.draw(); + + this.mousereset(); + return true; + } + }, + + //--------------------------------------------------------- + // キーボード入力系 + KeyEvent: { + enablemake: true + }, + + //--------------------------------------------------------- + // 盤面管理系 + Cell: { + numberRemainsUnshaded: true, + + maxnum: function() { + return this.board.cols * this.board.rows - 1; + }, + + isCmp: function() { + if (!this.isNum()) { + return false; + } + if (this.qcmp === 1) { + return true; + } + if (!this.isValidNum() || !this.puzzle.execConfig("autocmp")) { + return false; + } + return this.countResult(true) === 0; + }, + + countResult: function(explicit) { + if (!this.isValidNum()) { + return 0; + } + var targets = new Set(); + for (var dir in this.adjacent) { + var c = this.adjacent[dir]; + if (c.isNum()) { + targets.add(c); + } + var group = explicit ? c.autooasis : c.oasis; + if (group) { + group.adjclist.each(function(obj) { + targets.add(obj); + }); + } + } + targets.delete(this); + return targets.size - this.qnum; + }, + redrawConnected: function() { + if (this.autooasis) { + this.autooasis.adjclist.each(function(c) { + c.draw(); + }); + } else { + for (var dir in this.adjacent) { + var c = this.adjacent[dir]; + if (c && c.autooasis) { + c.redrawConnected(); + } else if (c && c.isNum()) { + c.draw(); + } + } + } + }, + + posthook: { + qnum: function() { + var bd = this.board; + var oasis = new Set(); + var autooasis = new Set(); + + for (var dir in this.adjacent) { + var c = this.adjacent[dir]; + if (c && c.oasis) { + oasis.add(c.oasis); + } + if (c && c.autooasis) { + autooasis.add(c.autooasis); + } + } + + oasis.forEach(function(cmp) { + bd.oasisgraph.setExtraData(cmp); + }); + autooasis.forEach(function(cmp) { + bd.autooasisgraph.setExtraData(cmp); + }); + this.redrawConnected(); + }, + qsub: function() { + this.redrawConnected(); + } + } + }, + Board: { + hasborder: 1, + cols: 8, + rows: 8, + + addExtraInfo: function() { + this.oasisgraph = this.addInfoList(this.klass.ImplicitOasisGraph); + this.autooasisgraph = this.addInfoList(this.klass.ExplicitOasisGraph); + } + }, + + AreaUnshadeGraph: { + enabled: true + }, + "ImplicitOasisGraph:AreaGraphBase": { + enabled: true, + relation: { "cell.qans": "node", "cell.qnum": "node", "cell.qsub": "node" }, + setExtraData: function(component) { + this.common.setExtraData.call(this, component); + + var set = new Set(); + + component.clist.each(function(cell) { + for (var dir in cell.adjacent) { + var adj = cell.adjacent[dir]; + if (adj.isNum()) { + set.add(adj); + } + } + }); + component.adjclist = new this.klass.CellList(Array.from(set)); + }, + + isnodevalid: function(cell) { + return cell.isUnshade() && !cell.isNum(); + }, + + getComponentRefs: function(obj) { + return obj.oasis; + }, + setComponentRefs: function(obj, component) { + obj.oasis = component; + }, + getObjNodeList: function(nodeobj) { + return nodeobj.oasisnodes; + }, + resetObjNodeList: function(nodeobj) { + nodeobj.oasisnodes = []; + } + }, + "ExplicitOasisGraph:ImplicitOasisGraph": { + isnodevalid: function(cell) { + return cell.isUnshade() && !cell.isNum() && cell.qsub; + }, + getComponentRefs: function(obj) { + return obj.autooasis; + }, + setComponentRefs: function(obj, component) { + obj.autooasis = component; + }, + getObjNodeList: function(nodeobj) { + return nodeobj.autooasisnodes; + }, + resetObjNodeList: function(nodeobj) { + nodeobj.autooasisnodes = []; + } + }, + + //--------------------------------------------------------- + // 画像表示系 + Graphic: { + qanscolor: "black", + gridcolor_type: "LIGHT", + + autocmp: "number", + hideHatena: true, + + paint: function() { + this.drawBGCells(); + this.drawDotCells(); + this.drawGrid(); + this.drawShadedCells(); + + this.drawCircledNumbers(); + + this.drawChassis(); + + this.drawPekes(); + + this.drawTarget(); + }, + + getCircleFillColor: function(cell) { + if (!cell.isNum()) { + return null; + } + return cell.isCmp() ? this.qcmpcolor : this.bgcolor; + } + }, + + //--------------------------------------------------------- + // URLエンコード/デコード処理 + Encode: { + decodePzpr: function(type) { + this.decodeNumber16(); + }, + encodePzpr: function(type) { + this.encodeNumber16(); + } + }, + //--------------------------------------------------------- + FileIO: { + decodeData: function() { + this.decodeCellQnum(); + this.decodeCellQanssubcmp(); + this.decodeBorderLine(); + }, + encodeData: function() { + this.encodeCellQnum(); + this.encodeCellQanssubcmp(); + this.encodeBorderLineIfPresent(); + }, + + decodeCellQanssubcmp: function() { + this.decodeCell(function(cell, ca) { + if (ca === "+") { + cell.qsub = 1; + } else if (ca === "-") { + cell.qcmp = 1; + } else if (ca === "1") { + cell.qans = 1; + } + }); + }, + encodeCellQanssubcmp: function() { + this.encodeCell(function(cell) { + if (cell.qans === 1) { + return "1 "; + } else if (cell.qsub === 1) { + return "+ "; + } else if (cell.qcmp === 1) { + return "- "; + } else { + return ". "; + } + }); + } + }, + + //--------------------------------------------------------- + // 正解判定処理実行部 + AnsCheck: { + checklist: [ + "checkShadeCellExist", + "checkAdjacentShadeCell", + "checkConnectUnshadeRB", + "checkCellNumberLarge", + "check2x2UnshadeCell++", + "checkCellNumberSmall", + "doneShadingDecided" + ], + + checkCellNumberLarge: function() { + this.checkAllCell(function(cell) { + return cell.countResult(false) < 0; + }, "nmOasisGt"); + }, + checkCellNumberSmall: function() { + this.checkAllCell(function(cell) { + return cell.countResult(false) > 0; + }, "nmOasisLt"); + } + } +}); diff --git a/test/script/oasis.js b/test/script/oasis.js new file mode 100644 index 000000000..076b536a9 --- /dev/null +++ b/test/script/oasis.js @@ -0,0 +1,57 @@ +/* oasis.js */ + +ui.debug.addDebugData("oasis", { + url: "6/6/2.m.l3h2l.m.1", + failcheck: [ + [ + "brNoShade", + "pzprv3/oasis/6/6/2 - . . . . /. . . - . . /. . . . 3 . /. 2 . . . . /. . - . . . /. . . . - 1 /. . . . . . /. . . . . . /. . . . . . /. . . . . . /. . . . . . /. . . . . . /" + ], + [ + "csAdjacent", + "pzprv3/oasis/6/6/2 - . . . . /. . . - . . /. . . . 3 . /. 2 . . . . /. . - . . . /. . . . - 1 /. . . . . . /. . . . . . /. . 1 . . . /. . 1 . . . /. . . . . . /. . . . . . /" + ], + [ + "cuDivideRB", + "pzprv3/oasis/6/6/2 - . . . . /. . . - . . /. . . . 3 . /. 2 . . . . /. . - . . . /. . . . - 1 /. . 1 . . . /. 1 . . 1 . /. . 1 . . 1 /1 . . 1 . . /. 1 . . . 1 /. . 1 . . . /" + ], + [ + "nmOasisGt", + "pzprv3/oasis/6/6/2 - . . . . /. . . - . . /. . . . 3 . /. 2 . . . . /. . - . . . /. . . . - 1 /. . . 1 . 1 /1 . 1 . . . /. . . 1 . 1 /1 . 1 . . . /. . . 1 . 1 /. 1 . . . . /" + ], + [ + "nmOasisLt", + "pzprv3/oasis/6/6/2 - . . . . /. . . - . . /. . . . 3 . /. 2 . . . . /. . - . . . /. . . . - 1 /. . 1 . . . /. 1 . . 1 . /. . . 1 . . /1 . 1 . . 1 /. . . . 1 . /1 . 1 . . . /" + ], + [ + "cu2x2", + "pzprv3/oasis/6/6/2 - . . . . /. . . - . . /. . . . 3 . /. 2 . . . . /. . - . . . /. . . . - 1 /. . . 1 . 1 /. 1 . . . . /. . 1 . . 1 /1 . . 1 . . /. . . . . 1 /. 1 . 1 . . /" + ], + [ + null, + "pzprv3/oasis/6/6/2 - . . . . /. . . - . . /. . . . 3 . /. 2 . . . . /. . - . . . /. . . . - 1 /+ + 1 + + + /+ 1 + + 1 + /+ + 1 + + 1 /1 + + 1 + + /+ 1 + + + 1 /+ + + 1 + + /" + ] + ], + inputs: [ + { + input: [ + "newboard,3,3", + "editmode", + "cursor,3,1", + "key,1", + "playmode", + "mouse,left,3,3", + "mouse,right,5,3" + ], + result: "pzprv3/oasis/3/3/. 1 . /. . . /. . . /. . . /. 1 + /. . . /" + }, + { + input: ["editmode", "cursor,5,5", "key,2", "playmode", "mouse,left,3,1"], + result: "pzprv3/oasis/3/3/. 1 . /. . . /. . 2 /. - . /. 1 + /. . . /" + }, + { + input: ["mouse,left,5,5", "playmode,completion", "mouse,left,3,1"], + result: "pzprv3/oasis/3/3/. 1 . /. . . /. . 2 /. . . /. 1 + /. . - /" + } + ] +});