Skip to content

Commit 018bf90

Browse files
committed
Add Soul Mates
1 parent 8a8c457 commit 018bf90

9 files changed

Lines changed: 331 additions & 54 deletions

File tree

src-ui/js/ui/KeyPopup.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,8 @@ ui.keypopup = {
257257
hasunomura: [10, 0],
258258
bramble: [10, 0],
259259
golemgrad: [10, 0],
260-
topo: [10, 10]
260+
topo: [10, 10],
261+
soulmates: [10, 10]
261262
},
262263

263264
//---------------------------------------------------------------------------

src-ui/list.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,7 @@ <h2 id="title"><span lang="ja">パズルの種類のリスト</span><span lang="
357357
<li data-pid="numrope"></li>
358358
<li data-pid="simplegako"></li>
359359
<li data-pid="japanesesums"></li>
360+
<li data-pid="soulmates"></li>
360361
</ul>
361362
</div>
362363
<div class="lists numbers">

src/pzpr/variety.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,7 @@
433433
snake: [0, 0, "Snake", "Snake"],
434434
snakeegg: [0, 0, "Snake Egg", "Snake Egg"],
435435
snakepit: [0, 0, "Snake Pit", "Snake Pit", "fillomino"],
436+
soulmates: [0, 0, "ソウルメイツ", "Soul Mates"],
436437
starbattle: [0, 0, "スターバトル", "Star Battle"],
437438
squarejam: [0, 0, "Square Jam", "Square Jam"],
438439
statuepark: [0, 0, "Statue Park", "Statue Park"],

src/res/failcode.en.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,7 @@
795795
"nmEndpoint.snakepit": "A circle is not on an endpoint.",
796796
"nmEqOne.snakepit": "There is a snake of length 1.",
797797
"nmGt2.oyakodori": "There are more than two birds in the same nest.",
798+
"nmGt2.soulmates": "A number can reach more than one identical number.",
798799
"nmHatenaNe.crossstitch": "A circled question mark does not have any corners used by a loop.",
799800
"nmIgnored.herugolf": "There is a Hole without a ball.",
800801
"nmIneqNe.minarism": "A inequality sign is not correct.",
@@ -812,6 +813,7 @@
812813
"nmLoop.snakepit": "A snake has no head or tail.",
813814
"nmLt1.lapaz": "A number is not contained in a 1x2 region.",
814815
"nmLt2.oyakodori": "There is a lonely bird.",
816+
"nmLt2.soulmates": "A number cannot reach another identical number.",
815817
"nmMidpoint.snakepit": "A grey cell is not a middle part of a snake.",
816818
"nmMinesNe.mines": "The number of mines in the surrounding cells is not equal to the number.",
817819
"nmMissing.lohkous": "There is no segment of the given length in the room.",

src/variety-common/Graphic.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1524,7 +1524,7 @@ pzpr.classmgr.makeCommon({
15241524
//---------------------------------------------------------------------------
15251525
// pc.drawMBs() Cell上の○,×をCanvasに書き込む
15261526
//---------------------------------------------------------------------------
1527-
drawMBs: function() {
1527+
drawMBs: function(withcross) {
15281528
var g = this.vinc("cell_mb", "auto", true);
15291529
g.lineWidth = 1;
15301530

@@ -1548,7 +1548,7 @@ pzpr.classmgr.makeCommon({
15481548
}
15491549

15501550
g.vid = "c_MB2_" + cell.id;
1551-
if (cell.qsub === 2) {
1551+
if (withcross !== false && cell.qsub === 2) {
15521552
g.strokeCross(px, py, rsize);
15531553
} else {
15541554
g.vhide();

src/variety/kaidan.js

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -659,7 +659,7 @@
659659
this.drawCrosses();
660660
} else {
661661
this.drawDotCells();
662-
this.drawMBs();
662+
this.drawMBs(false);
663663
}
664664
if (this.pid === "edamame") {
665665
this.drawDotCells();
@@ -839,28 +839,6 @@
839839
return this.errcolor1;
840840
}
841841
return cell.qcmp ? this.qcmpcolor : this.quescolor;
842-
},
843-
drawMBs: function() {
844-
var g = this.vinc("cell_mb", "auto", true);
845-
g.lineWidth = 1;
846-
847-
var rsize = this.cw * 0.35;
848-
var clist = this.range.cells;
849-
for (var i = 0; i < clist.length; i++) {
850-
var cell = clist[i],
851-
px,
852-
py;
853-
854-
g.vid = "c_MB1_" + cell.id;
855-
if (cell.qsub === 1) {
856-
px = cell.bx * this.bw;
857-
py = cell.by * this.bh;
858-
g.strokeStyle = !cell.trial ? this.mbcolor : "rgb(192, 192, 192)";
859-
g.strokeCircle(px, py, rsize);
860-
} else {
861-
g.vhide();
862-
}
863-
}
864842
}
865843
},
866844
"Graphic@wittgen#2": {

src/variety/soulmates.js

Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
(function(pidlist, classbase) {
2+
if (typeof module === "object" && module.exports) {
3+
module.exports = [pidlist, classbase];
4+
} else {
5+
pzpr.classmgr.makeCustom(pidlist, classbase);
6+
}
7+
})(["soulmates"], {
8+
MouseEvent: {
9+
inputModes: {
10+
edit: ["number", "clear"],
11+
play: ["number", "numexist", "objblank", "clear"]
12+
},
13+
autoedit_func: "qnum",
14+
autoplay_func: "qnum",
15+
16+
inputDot: function() {
17+
this.inputFixedQsub(2);
18+
}
19+
},
20+
21+
KeyEvent: {
22+
enablemake: true,
23+
enableplay: true,
24+
25+
keyinput: function(ca) {
26+
this.key_view(ca);
27+
},
28+
key_view: function(ca) {
29+
if (this.puzzle.playmode) {
30+
if (ca === "q" || ca === "a" || ca === "z") {
31+
ca = "s1";
32+
} else if (ca === "w" || ca === "s" || ca === "x") {
33+
ca = "s2";
34+
} else if (ca === "e" || ca === "d" || ca === "c" || ca === "-") {
35+
ca = " ";
36+
}
37+
}
38+
this.key_inputqnum(ca);
39+
}
40+
},
41+
42+
Cell: {
43+
numberWithMB: true,
44+
enableSubNumberArray: true,
45+
maxnum: function() {
46+
return this.board.cols * this.board.rows - 1;
47+
},
48+
isDot: function() {
49+
return this.qsub === 2;
50+
},
51+
52+
distanceTo: function(end) {
53+
/* Calculate Manhattan distance using Dijkstra's algorithm */
54+
55+
var visited = new Set();
56+
var distances = {};
57+
distances[this.id] = 0;
58+
59+
while (true) {
60+
var current = null;
61+
var minimum = -1;
62+
for (var idx in distances) {
63+
if (visited.has(+idx)) {
64+
continue;
65+
}
66+
67+
var dist = distances[idx];
68+
if (minimum === -1 || dist < minimum) {
69+
current = this.board.cell[+idx];
70+
minimum = dist;
71+
}
72+
}
73+
74+
if (!current || current === end) {
75+
break;
76+
}
77+
78+
for (var dir in current.adjacent) {
79+
var next = current.adjacent[dir];
80+
if (
81+
next.isnull ||
82+
(next !== end && next.isNum()) ||
83+
visited.has(+next.id)
84+
) {
85+
continue;
86+
}
87+
var olddist = distances[next.id];
88+
var newdist = minimum + 1;
89+
if (olddist === undefined || newdist < olddist) {
90+
distances[next.id] = newdist;
91+
}
92+
}
93+
94+
visited.add(current.id);
95+
}
96+
97+
return distances[end.id];
98+
}
99+
},
100+
Board: {
101+
cols: 5,
102+
rows: 5,
103+
104+
addExtraInfo: function() {
105+
this.plaingraph = this.addInfoList(this.klass.PlainGraph);
106+
}
107+
},
108+
109+
"PlainGraph:AreaGraphBase": {
110+
enabled: true,
111+
relation: { "cell.qnum": "node", "cell.anum": "node" },
112+
setExtraData: function(component) {
113+
this.common.setExtraData.call(this, component);
114+
115+
var set = new Set();
116+
117+
component.clist.each(function(cell) {
118+
for (var dir in cell.adjacent) {
119+
var adj = cell.adjacent[dir];
120+
if (adj.isNum()) {
121+
set.add(adj);
122+
}
123+
}
124+
});
125+
component.adjclist = new this.klass.CellList(Array.from(set));
126+
},
127+
128+
isnodevalid: function(cell) {
129+
return !cell.isNum();
130+
},
131+
132+
getComponentRefs: function(obj) {
133+
return obj.plane;
134+
},
135+
setComponentRefs: function(obj, component) {
136+
obj.plane = component;
137+
},
138+
getObjNodeList: function(nodeobj) {
139+
return nodeobj.planenodes;
140+
},
141+
resetObjNodeList: function(nodeobj) {
142+
nodeobj.planenodes = [];
143+
}
144+
},
145+
146+
Graphic: {
147+
paint: function() {
148+
this.drawBGCells();
149+
this.drawTargetSubNumber();
150+
this.drawDotCells();
151+
this.drawGrid();
152+
153+
this.drawSubNumbers();
154+
this.drawMBs(false);
155+
this.drawAnsNumbers();
156+
this.drawQuesNumbers();
157+
158+
this.drawChassis();
159+
160+
this.drawCursor();
161+
}
162+
},
163+
164+
Encode: {
165+
decodePzpr: function(type) {
166+
this.decodeNumber16();
167+
},
168+
encodePzpr: function(type) {
169+
this.encodeNumber16();
170+
}
171+
},
172+
//---------------------------------------------------------
173+
FileIO: {
174+
decodeData: function() {
175+
this.decodeCellQnum();
176+
this.decodeCellAnumsub();
177+
},
178+
encodeData: function() {
179+
this.encodeCellQnum();
180+
this.encodeCellAnumsub();
181+
}
182+
},
183+
184+
AnsCheck: {
185+
checklist: ["checkTripleNumber", "checkSingleNumber", "checkDistances"],
186+
187+
checkDistances: function() {
188+
var checked = new Set();
189+
190+
var reachables = this.getReachables();
191+
for (var i = 0; i < reachables.length; i++) {
192+
var r = reachables[i];
193+
if (r.size !== 2) {
194+
continue;
195+
}
196+
var list = new this.klass.CellList(r);
197+
var start = list[0],
198+
end = list[1];
199+
if (start.id > end.id) {
200+
var tmp = start;
201+
start = end;
202+
end = tmp;
203+
}
204+
205+
if (checked.has(start)) {
206+
continue;
207+
}
208+
checked.add(start);
209+
210+
if (start.distanceTo(end) !== start.getNum()) {
211+
this.failcode.add("nmDistNe");
212+
if (this.checkOnly) {
213+
return;
214+
}
215+
list.seterr(1);
216+
}
217+
}
218+
},
219+
220+
checkTripleNumber: function() {
221+
this.checkSizes(+1, "nmGt2");
222+
},
223+
checkSingleNumber: function() {
224+
this.checkSizes(-1, "nmLt2");
225+
},
226+
227+
checkSizes: function(flag, code) {
228+
var reachables = this.getReachables();
229+
for (var i = 0; i < reachables.length; i++) {
230+
var r = reachables[i];
231+
if (flag > 0 && r.size < 3) {
232+
continue;
233+
}
234+
if (flag < 0 && r.size > 1) {
235+
continue;
236+
}
237+
this.failcode.add(code);
238+
if (this.checkOnly) {
239+
return;
240+
}
241+
new this.klass.CellList(r).seterr(1);
242+
}
243+
},
244+
245+
getReachables: function() {
246+
if (this._info.reachable) {
247+
return this._info.reachable;
248+
}
249+
250+
var info = [];
251+
for (var i = 0; i < this.board.cell.length; i++) {
252+
var cell = this.board.cell[i];
253+
if (!cell.isValidNum()) {
254+
continue;
255+
}
256+
var found = new Set();
257+
found.add(cell);
258+
for (var dir in cell.adjacent) {
259+
var c = cell.adjacent[dir];
260+
if (c.getNum() === cell.getNum()) {
261+
found.add(c);
262+
}
263+
if (c.plane) {
264+
c.plane.adjclist.each(function(obj) {
265+
if (obj.getNum() === cell.getNum()) {
266+
found.add(obj);
267+
}
268+
});
269+
}
270+
}
271+
272+
info.push(found);
273+
}
274+
275+
return (this._info.reachable = info);
276+
}
277+
},
278+
FailCode: {
279+
nmDistNe: "arDistance.toichika2"
280+
}
281+
});

0 commit comments

Comments
 (0)