diff --git a/src-ui/changes.html b/src-ui/changes.html index 44194e07a..ffb395da5 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/kuromenbun.png b/src-ui/img/kuromenbun.png new file mode 100644 index 000000000..3ef3b85f7 Binary files /dev/null and b/src-ui/img/kuromenbun.png differ diff --git a/src-ui/js/ui/KeyPopup.js b/src-ui/js/ui/KeyPopup.js index 95f2c3d18..6945fcc9f 100644 --- a/src-ui/js/ui/KeyPopup.js +++ b/src-ui/js/ui/KeyPopup.js @@ -198,7 +198,8 @@ ui.keypopup = { subomino: [10, 0], lixloop: [130, 0], teri: [10, 0], - portal: [10, 0] + portal: [10, 0], + kuromenbun: [10, 0] }, //--------------------------------------------------------------------------- diff --git a/src-ui/js/ui/Misc.js b/src-ui/js/ui/Misc.js index d55c47286..6719dceba 100755 --- a/src-ui/js/ui/Misc.js +++ b/src-ui/js/ui/Misc.js @@ -146,6 +146,7 @@ function toBGimage(pid) { "kissing", "kropki", "kuroclone", + "kuromenbun", "lollipops", "magnets", "mannequin", diff --git a/src-ui/list.html b/src-ui/list.html index 2cc4e9c61..b698b9b04 100644 --- a/src-ui/list.html +++ b/src-ui/list.html @@ -85,6 +85,7 @@

パズルの種類のリスト
  • +
  • diff --git a/src/pzpr/variety.js b/src/pzpr/variety.js index 1d58874b8..80519c921 100755 --- a/src/pzpr/variety.js +++ b/src/pzpr/variety.js @@ -228,6 +228,7 @@ kurochute: [0, 1, "クロシュート", "Kurochute"], kuroclone: [0, 0, "クロクローン", "Kuroclone"], kurodoko: [0, 1, "黒どこ(黒マスはどこだ)", "Kurodoko"], + kuromenbun: [0, 0, "クロメンブン", "Kuromenbun"], kurotto: [0, 0, "クロット", "Kurotto"], kusabi: [0, 0, "クサビリンク", "Kusabi"], ladders: [0, 0, "はしごをかけろ", "Ladders"], diff --git a/src/res/failcode.en.json b/src/res/failcode.en.json index b58a72c15..0931fa3a6 100644 --- a/src/res/failcode.en.json +++ b/src/res/failcode.en.json @@ -718,8 +718,10 @@ "nmShadeDiagNe.context": "The number of shaded cells diagonally adjacent to a shaded number is not correct.", "nmShadeGt.interbd": "The number of shaded cells around a number is not correct.", "nmShadeGt.kaidan": "The number of circles around a number is not correct.", + "nmShadeGt.kuromenbun": "The number of shaded cells around an area is not correct.", "nmShadeLt.interbd": "The number of shaded cells around a number is not correct.", "nmShadeLt.kaidan": "The number of circles around a number is not correct.", + "nmShadeLt.kuromenbun": "The number of shaded cells around an area is not correct.", "nmShadeNe.lapaz": "The number of shaded cells in the row or column is not correct.", "nmShadeNe.paintarea": "The number is not equal to the number of shaded cells in the four adjacent cells.", "nmShadeNe.tawa": "The number of shaded cells around a number is not correct.", diff --git a/src/variety/kuromenbun.js b/src/variety/kuromenbun.js new file mode 100644 index 000000000..0bebc5990 --- /dev/null +++ b/src/variety/kuromenbun.js @@ -0,0 +1,239 @@ +/* global Set:false */ +(function(pidlist, classbase) { + if (typeof module === "object" && module.exports) { + module.exports = [pidlist, classbase]; + } else { + pzpr.classmgr.makeCustom(pidlist, classbase); + } +})(["kuromenbun"], { + MouseEvent: { + use: true, + inputModes: { + edit: ["border", "number", "clear", "info-ublk"], + play: ["shade", "unshade", "info-ublk"] + }, + autoedit_func: "areanum", + autoplay_func: "cell", + + dispInfoUblk: function() { + var cell = this.getcell(); + this.mousereset(); + if (cell.isnull || !cell.room) { + return; + } + cell.room.clist.setinfo(1); + cell.room.adjclist.setinfo(2); + this.board.hasinfo = true; + this.puzzle.redraw(); + } + }, + + KeyEvent: { + enablemake: true + }, + + Cell: { + numberRemainsUnshaded: true, + minnum: 0, + maxnum: function() { + return Math.round((this.board.cols * this.board.rows) / 1.5); + } + }, + CellList: { + seterr: function(num) { + if (!this.board.isenableSetError()) { + return; + } + for (var i = 0; i < this.length; i++) { + var old = this[i].error; + this[i].error = old <= 0 ? num : Math.max(old, num); + } + }, + setinfo: function(num) { + for (var i = 0; i < this.length; i++) { + var old = this[i].qinfo; + this[i].qinfo = old <= 0 ? num : Math.max(old, num); + } + } + }, + Board: { + hasborder: 1, + rows: 8, + cols: 8 + }, + + AreaRoomGraph: { + relation: { "border.ques": "separator", "cell.qans": "node" }, + enabled: true, + isnodevalid: function(cell) { + return cell.isUnshade(); + }, + setExtraData: function(component) { + component.clist = new this.klass.CellList(component.getnodeobjs()); + + var set = new Set(); + + component.clist.each(function(cell) { + for (var dir in cell.adjacent) { + var adj = cell.adjacent[dir]; + if (!adj.isnull && adj.room !== component) { + set.add(adj); + } + } + }); + component.adjclist = new this.klass.CellList(Array.from(set)); + } + }, + + Graphic: { + gridcolor_type: "DARK", + + enablebcolor: true, + errbcolor2: "rgb(255, 216, 216)", + errcolor2: "rgb(192, 0, 0)", + + getBGCellColor: function(cell) { + if ((cell.error || cell.qinfo) === 1) { + return cell.qsub === 1 ? this.errbcolor2 : this.errbcolor1; + } + return cell.qsub === 1 ? this.bcolor : null; + }, + + drawDotCells: function() { + var g = this.vinc("cell_dot", "auto", true); + + var dsize = Math.max(this.cw * 0.12, 3); + var clist = this.range.cells; + for (var i = 0; i < clist.length; i++) { + var cell = clist[i]; + + g.vid = "c_dot_" + cell.id; + if ( + !cell.isNum() && + !cell.isShade() && + (cell.error || cell.qinfo) === 2 + ) { + g.fillStyle = this.errcolor2; + g.fillCircle(cell.bx * this.bw, cell.by * this.bh, dsize); + } else { + g.vhide(); + } + } + }, + + drawShadedBorders: function() { + this.vinc("wbd", "crispEdges", true); + var g = this.context; + g.lineWidth = this.gw; + g.strokeStyle = this.bbcolor; + + var blist = this.range.borders; + for (var i = 0; i < blist.length; i++) { + var border = blist[i]; + + g.vid = "b_wbd_" + border.id; + if ( + !border.isBorder() && + border.sidecell[0].isShade() && + border.sidecell[1].isShade() + ) { + if (border.isVert()) { + var px = border.bx * this.bw, + py = border.by * this.bh; + g.strokeLine(px, py - this.bh, px, py + this.bh); + } else { + var px = border.bx * this.bw, + py = border.by * this.bh; + g.strokeLine(px - this.bw, py, px + this.bw, py); + } + } else { + g.vhide(); + } + } + }, + + paint: function() { + this.drawBGCells(); + this.drawShadedCells(); + this.drawGrid(); + + this.drawDotCells(); + this.drawQuesNumbers(); + + this.drawShadedBorders(); + this.drawBorders(); + + this.drawChassis(); + + this.drawBoxBorders(false); + + this.drawTarget(); + } + }, + + Encode: { + decodePzpr: function(type) { + this.decodeBorder(); + this.decodeNumber16(); + }, + encodePzpr: function(type) { + this.encodeBorder(); + this.encodeNumber16(); + } + }, + FileIO: { + decodeData: function() { + this.decodeBorderQues(); + this.decodeCellQnum(); + this.decodeCellAns(); + }, + encodeData: function() { + this.encodeBorderQues(); + this.encodeCellQnum(); + this.encodeCellAns(); + } + }, + + AnsCheck: { + checklist: [ + "checkDoubleNumber", + "checkNumberTooHigh", + "checkNumberTooLow", + "doneShadingDecided" + ], + checkNumberTooLow: function() { + this.checkShadeCount(-1, "nmShadeLt"); + }, + checkNumberTooHigh: function() { + this.checkShadeCount(+1, "nmShadeGt"); + }, + + checkShadeCount: function(factor, code) { + var checkSingleError = !this.puzzle.getConfig("multierr"); + var rooms = this.board.roommgr.components; + for (var r = 0; r < rooms.length; r++) { + var room = rooms[r], + num = room.clist.getQnumCell().getNum(); + if (num < 0) { + continue; + } + + var actual = room.adjclist.filter(function(cell) { + return cell.isShade(); + }).length; + + if ((factor < 0 && actual < num) || (factor > 0 && actual > num)) { + this.failcode.add(code); + if (this.checkOnly) { + return; + } + room.clist.seterr(1); + room.adjclist.seterr(2); + if (checkSingleError) { + return; + } + } + } + } + } +}); diff --git a/test/script/kuromenbun.js b/test/script/kuromenbun.js new file mode 100644 index 000000000..b6a9c7607 --- /dev/null +++ b/test/script/kuromenbun.js @@ -0,0 +1,58 @@ +/* kuromenbun.js */ + +ui.debug.addDebugData("kuromenbun", { + url: "4/4/0q0v282h2j0l5", + failcheck: [ + [ + "bkNumGe2", + "pzprv3/kuromenbun/4/4/0 0 0 /0 0 1 /1 0 1 /0 0 0 /1 1 1 1 /1 0 0 0 /1 0 0 1 /2 . . 2 /. . . . /0 . . . /. . . 5 /. . . . /. # # . /. . . # /. # . . /" + ], + [ + "nmShadeGt", + "pzprv3/kuromenbun/4/4/0 0 0 /0 0 1 /1 0 1 /0 0 0 /1 1 1 1 /1 0 0 0 /1 0 0 1 /2 . . 2 /. . . . /0 . . . /. . . 5 /. # # . /# . . # /. . . . /. # . . /" + ], + [ + "nmShadeLt", + "pzprv3/kuromenbun/4/4/0 0 0 /0 0 1 /1 0 1 /0 0 0 /1 1 1 1 /1 0 0 0 /1 0 0 1 /2 . . 2 /. . . . /0 . . . /. . . 5 /. . # . /. # . # /. . . . /. . . . /" + ], + [ + null, + "pzprv3/kuromenbun/4/4/0 0 0 /0 0 1 /1 0 1 /0 0 0 /1 1 1 1 /1 0 0 0 /1 0 0 1 /2 . . 2 /. . . . /0 . . . /. . . 5 /+ + # + /+ # + # /+ + + # /+ # + + /" + ] + ], + inputs: [ + { + input: [ + "newboard,4,2", + "editmode", + "cursor,1,1", + "mouse,right,1,1", + "mouse,left, 0,2, 6,2, 6,0" + ], + result: + "pzprv3/kuromenbun/2/4/0 0 1 /0 0 0 /1 1 1 0 /5 . . . /. . . . /. . . . /. . . . /" + }, + { + input: [ + "playmode", + "mouse,left,5,3", + "playmode,info-ublk", + "mouse,left,1,3" + ], + result: function(puzzle, assert) { + var bd = puzzle.board; + assert.equal(bd.getc(3, 3).qinfo, 1); + assert.equal(bd.getc(5, 3).qinfo, 2); + assert.equal(bd.getc(7, 3).qinfo, 0); + } + }, + { + input: ["playmode,info-ublk", "mouse,left,5,3"], + result: function(puzzle, assert) { + var bd = puzzle.board; + assert.equal(bd.getc(5, 3).qinfo, 0); + assert.equal(bd.getc(5, 1).qinfo, 0); + } + } + ] +});