diff --git a/public/data/puddingmonsters.json b/public/data/puddingmonsters.json index 3325b9c..55d2e12 100644 --- a/public/data/puddingmonsters.json +++ b/public/data/puddingmonsters.json @@ -1,5 +1,5 @@ { - "generatedAt": "2026-06-09T04:18:02.258Z", + "generatedAt": "2026-06-12T06:32:38.434Z", "seed": 1592594996, "count": 40, "levels": [ @@ -7,34 +7,47 @@ "level": 1, "cols": 5, "rows": 5, - "walls": [], - "spikes": [], - "targets": [ + "walls": [ [ - 0, - 0 + 4, + 4 ], [ 1, - 0 + 4 ], [ 2, 0 ] ], - "monsters": [ - [ - 3, - 2 - ], + "spikes": [], + "targets": [ [ 0, - 0 + 3 ], [ 1, - 2 + 3 + ], + [ + 0, + 4 + ] + ], + "monsters": [ + [ + 2, + 1 + ], + [ + 0, + 1 + ], + [ + 0, + 4 ] ], "par": 2 @@ -43,34 +56,47 @@ "level": 2, "cols": 5, "rows": 5, - "walls": [], - "spikes": [], - "targets": [ + "walls": [ [ - 4, - 2 + 2, + 0 ], [ - 4, - 3 + 0, + 1 ], [ 4, 4 ] ], - "monsters": [ + "spikes": [], + "targets": [ [ - 4, - 4 - ], - [ - 4, - 2 + 1, + 0 ], + [ + 1, + 1 + ], + [ + 1, + 2 + ] + ], + "monsters": [ [ 1, 4 + ], + [ + 1, + 1 + ], + [ + 0, + 0 ] ], "par": 2 @@ -79,20 +105,33 @@ "level": 3, "cols": 5, "rows": 5, - "walls": [], + "walls": [ + [ + 0, + 2 + ], + [ + 0, + 3 + ], + [ + 3, + 3 + ] + ], "spikes": [], "targets": [ [ - 0, - 0 - ], - [ - 1, - 0 + 2, + 3 ], [ 2, - 0 + 4 + ], + [ + 3, + 4 ] ], "monsters": [ @@ -101,12 +140,12 @@ 1 ], [ - 1, - 2 + 3, + 4 ], [ 0, - 0 + 4 ] ], "par": 2 @@ -115,34 +154,47 @@ "level": 4, "cols": 5, "rows": 5, - "walls": [], - "spikes": [], - "targets": [ + "walls": [ [ 0, - 0 + 4 ], [ - 1, - 0 + 3, + 2 ], [ - 0, - 1 + 4, + 4 ] ], - "monsters": [ + "spikes": [], + "targets": [ [ 0, 3 ], [ - 0, + 1, + 3 + ], + [ + 2, + 3 + ] + ], + "monsters": [ + [ + 4, 1 ], [ - 1, - 0 + 3, + 3 + ], + [ + 0, + 3 ] ], "par": 2 @@ -151,34 +203,47 @@ "level": 5, "cols": 5, "rows": 5, - "walls": [], + "walls": [ + [ + 3, + 0 + ], + [ + 2, + 3 + ], + [ + 1, + 3 + ] + ], "spikes": [], "targets": [ + [ + 0, + 2 + ], [ 1, - 0 + 2 ], [ 2, - 0 - ], - [ - 2, - 1 + 2 ] ], "monsters": [ [ - 1, - 0 + 3, + 2 ], [ 2, - 1 + 0 ], [ - 4, - 4 + 0, + 2 ] ], "par": 2 @@ -187,34 +252,47 @@ "level": 6, "cols": 5, "rows": 5, - "walls": [], + "walls": [ + [ + 3, + 4 + ], + [ + 2, + 3 + ], + [ + 3, + 1 + ] + ], "spikes": [], "targets": [ [ - 0, - 3 + 2, + 0 ], [ - 1, - 3 + 2, + 1 ], [ - 1, - 4 + 2, + 2 ] ], "monsters": [ [ - 1, - 0 - ], - [ - 1, - 2 + 0, + 1 ], [ 0, - 3 + 4 + ], + [ + 2, + 0 ] ], "par": 2 @@ -223,56 +301,82 @@ "level": 7, "cols": 5, "rows": 5, - "walls": [], - "spikes": [], - "targets": [ - [ - 1, - 3 - ], - [ - 0, - 4 - ], - [ - 1, - 4 - ] - ], - "monsters": [ + "walls": [ [ 3, 3 ], [ - 4, - 4 + 0, + 3 ], [ 1, - 3 + 0 ] ], - "par": 2 - }, - { - "level": 8, - "cols": 5, - "rows": 5, - "walls": [], "spikes": [], "targets": [ + [ + 0, + 2 + ], + [ + 1, + 2 + ], + [ + 2, + 2 + ] + ], + "monsters": [ + [ + 2, + 2 + ], [ 3, 0 ], [ - 2, + 0, 1 + ] + ], + "par": 3 + }, + { + "level": 8, + "cols": 5, + "rows": 5, + "walls": [ + [ + 0, + 2 ], [ 3, - 1 + 3 + ], + [ + 1, + 0 + ] + ], + "spikes": [], + "targets": [ + [ + 1, + 2 + ], + [ + 0, + 3 + ], + [ + 1, + 3 ] ], "monsters": [ @@ -282,11 +386,11 @@ ], [ 4, - 3 + 2 ], [ - 2, - 1 + 3, + 4 ] ], "par": 3 @@ -297,41 +401,49 @@ "rows": 6, "walls": [ [ - 3, - 4 + 5, + 5 ], [ - 0, + 2, 1 + ], + [ + 5, + 2 + ], + [ + 2, + 5 ] ], "spikes": [], "targets": [ - [ - 1, - 0 - ], [ 2, - 0 + 4 ], [ 3, - 0 + 4 + ], + [ + 3, + 5 ] ], "monsters": [ - [ - 2, - 5 - ], [ 1, - 3 + 2 ], [ 3, - 1 + 2 + ], + [ + 4, + 5 ] ], "par": 3 @@ -342,41 +454,49 @@ "rows": 6, "walls": [ [ - 4, - 0 + 0, + 4 + ], + [ + 5, + 2 ], [ 1, - 3 + 0 + ], + [ + 2, + 4 ] ], "spikes": [], "targets": [ [ - 5, - 4 + 3, + 1 ], [ 4, - 5 + 1 ], [ - 5, - 5 + 3, + 2 ] ], "monsters": [ [ - 5, - 0 - ], - [ - 0, - 4 + 3, + 5 ], [ 4, - 2 + 1 + ], + [ + 1, + 3 ] ], "par": 3 @@ -387,41 +507,49 @@ "rows": 6, "walls": [ [ - 0, + 5, + 2 + ], + [ + 1, 3 ], [ 4, 5 + ], + [ + 2, + 2 ] ], "spikes": [], "targets": [ - [ - 2, - 0 - ], [ 3, - 0 - ], - [ - 4, - 0 - ] - ], - "monsters": [ - [ - 4, - 4 + 1 ], [ 3, 2 ], [ - 2, - 3 + 4, + 2 + ] + ], + "monsters": [ + [ + 4, + 2 + ], + [ + 3, + 1 + ], + [ + 0, + 4 ] ], "par": 3 @@ -431,42 +559,50 @@ "cols": 6, "rows": 6, "walls": [ + [ + 5, + 2 + ], [ 3, - 3 + 1 + ], + [ + 3, + 4 ], [ 2, - 1 + 5 ] ], "spikes": [], "targets": [ [ - 2, - 4 - ], - [ - 2, - 5 + 4, + 2 ], [ 3, - 5 + 3 + ], + [ + 4, + 3 ] ], "monsters": [ [ - 1, - 1 + 2, + 3 ], [ - 1, - 4 - ], - [ - 3, + 4, 5 + ], + [ + 1, + 2 ] ], "par": 3 @@ -478,40 +614,48 @@ "walls": [ [ 3, - 5 + 1 ], [ - 0, - 0 + 2, + 1 + ], + [ + 1, + 3 + ], + [ + 5, + 2 ] ], "spikes": [], "targets": [ [ 4, - 4 + 1 ], [ 5, - 4 + 1 ], [ - 5, - 5 + 4, + 2 ] ], "monsters": [ [ 2, - 2 + 0 ], [ 5, - 5 + 0 ], [ - 1, - 0 + 2, + 2 ] ], "par": 3 @@ -522,27 +666,35 @@ "rows": 6, "walls": [ [ - 5, - 1 + 1, + 2 ], [ - 0, + 1, + 5 + ], + [ + 4, 4 + ], + [ + 5, + 0 ] ], "spikes": [], "targets": [ [ 2, - 0 + 1 ], [ - 3, - 0 + 2, + 2 ], [ - 4, - 0 + 2, + 3 ] ], "monsters": [ @@ -551,12 +703,12 @@ 5 ], [ - 0, + 4, 3 ], [ 4, - 0 + 1 ] ], "par": 3 @@ -567,16 +719,28 @@ "rows": 6, "walls": [ [ - 5, + 0, + 5 + ], + [ + 4, 3 ], [ - 0, + 4, + 4 + ], + [ + 2, 0 ] ], "spikes": [], "targets": [ + [ + 3, + 0 + ], [ 3, 1 @@ -584,24 +748,20 @@ [ 3, 2 - ], - [ - 4, - 2 ] ], "monsters": [ [ - 2, - 5 + 0, + 3 + ], + [ + 3, + 4 ], [ 4, - 2 - ], - [ - 0, - 4 + 0 ] ], "par": 3 @@ -612,8 +772,16 @@ "rows": 6, "walls": [ [ - 5, - 3 + 4, + 2 + ], + [ + 1, + 2 + ], + [ + 0, + 4 ], [ 2, @@ -624,29 +792,29 @@ "targets": [ [ 0, - 0 + 3 ], [ - 0, - 1 + 1, + 3 ], [ - 0, - 2 + 1, + 4 ] ], "monsters": [ [ - 3, - 5 + 2, + 4 ], [ 0, - 5 + 2 ], [ 4, - 2 + 4 ] ], "par": 4 @@ -657,27 +825,35 @@ "rows": 6, "walls": [ [ - 5, + 4, 3 ], [ - 4, - 5 + 2, + 2 ], [ - 0, - 4 + 4, + 0 + ], + [ + 5, + 2 + ], + [ + 3, + 5 ] ], "spikes": [], "targets": [ [ - 1, - 3 + 2, + 4 ], [ - 1, - 5 + 4, + 4 ], [ 2, @@ -686,20 +862,20 @@ ], "monsters": [ [ - 0, + 2, + 3 + ], + [ + 3, 0 ], [ - 2, + 4, 5 ], [ - 4, - 4 - ], - [ - 4, - 2 + 0, + 5 ] ], "par": 4 @@ -710,7 +886,11 @@ "rows": 6, "walls": [ [ - 0, + 3, + 5 + ], + [ + 4, 2 ], [ @@ -718,32 +898,36 @@ 2 ], [ - 4, + 0, + 4 + ], + [ + 2, 2 ] ], "spikes": [], "targets": [ [ - 2, - 0 + 3, + 2 ], [ - 1, - 1 + 3, + 3 ], [ - 2, - 1 + 4, + 3 ] ], "monsters": [ [ 0, - 1 + 5 ], [ - 2, + 4, 4 ], [ @@ -751,8 +935,8 @@ 2 ], [ - 4, - 5 + 0, + 3 ] ], "par": 4 @@ -764,48 +948,56 @@ "walls": [ [ 5, + 5 + ], + [ + 0, + 1 + ], + [ + 0, 2 ], [ 2, - 5 + 0 ], [ - 2, - 0 + 4, + 3 ] ], "spikes": [], "targets": [ [ - 0, + 1, 0 ], [ - 0, - 2 + 2, + 1 ], [ - 0, - 3 + 2, + 2 ] ], "monsters": [ [ - 1, - 2 + 2, + 4 ], [ - 5, - 3 + 3, + 5 ], [ 1, 0 ], [ - 4, - 2 + 5, + 1 ] ], "par": 4 @@ -816,49 +1008,57 @@ "rows": 6, "walls": [ [ - 0, - 5 + 5, + 2 ], [ 4, - 3 + 1 + ], + [ + 3, + 4 ], [ 5, - 1 + 5 + ], + [ + 2, + 0 ] ], "spikes": [], "targets": [ [ - 3, - 4 + 2, + 1 ], [ - 4, - 5 + 2, + 2 ], [ - 5, - 5 + 2, + 3 ] ], "monsters": [ + [ + 2, + 1 + ], [ 0, - 2 + 5 ], [ 4, - 4 - ], - [ - 5, 3 ], [ - 3, - 3 + 1, + 2 ] ], "par": 4 @@ -870,48 +1070,56 @@ "walls": [ [ 3, - 2 + 3 ], [ 0, - 1 + 3 ], [ - 1, - 0 + 0, + 5 + ], + [ + 3, + 2 + ], + [ + 4, + 3 ] ], "spikes": [], "targets": [ [ - 3, - 0 - ], - [ - 5, + 4, 0 ], [ 5, 1 + ], + [ + 4, + 2 ] ], "monsters": [ [ - 1, - 5 - ], - [ - 4, - 0 + 5, + 1 ], [ 2, 0 ], [ - 5, - 3 + 0, + 1 + ], + [ + 4, + 2 ] ], "par": 4 @@ -922,49 +1130,57 @@ "rows": 6, "walls": [ [ - 3, - 5 - ], - [ - 3, - 2 + 5, + 4 ], [ 4, - 4 + 3 + ], + [ + 2, + 1 + ], + [ + 4, + 5 + ], + [ + 0, + 0 ] ], "spikes": [], "targets": [ + [ + 2, + 2 + ], [ 2, 3 ], - [ - 2, - 4 - ], - [ - 2, - 5 - ] - ], - "monsters": [ - [ - 5, - 0 - ], [ 3, 3 - ], - [ - 0, - 3 - ], + ] + ], + "monsters": [ [ 0, 5 + ], + [ + 3, + 5 + ], + [ + 2, + 2 + ], + [ + 4, + 0 ] ], "par": 4 @@ -974,9 +1190,17 @@ "cols": 6, "rows": 6, "walls": [ + [ + 5, + 1 + ], + [ + 0, + 4 + ], [ 1, - 0 + 4 ], [ 2, @@ -984,96 +1208,104 @@ ], [ 3, - 4 + 0 ] ], "spikes": [], "targets": [ - [ - 3, - 0 - ], [ 3, 1 ], + [ + 2, + 3 + ], [ 3, - 2 + 3 ] ], "monsters": [ - [ - 0, - 5 - ], [ 5, 3 ], [ - 3, + 1, 2 ], [ - 2, + 3, 1 + ], + [ + 5, + 5 ] ], - "par": 4 + "par": 5 }, { "level": 24, "cols": 6, "rows": 6, "walls": [ - [ - 1, - 3 - ], - [ - 4, - 2 - ], - [ - 2, - 4 - ] - ], - "spikes": [], - "targets": [ - [ - 5, - 0 - ], [ 5, 1 ], - [ - 5, - 2 - ] - ], - "monsters": [ [ 5, 5 ], [ - 5, + 3, + 0 + ], + [ + 4, + 0 + ], + [ + 4, 3 + ] + ], + "spikes": [], + "targets": [ + [ + 2, + 0 + ], + [ + 2, + 1 + ], + [ + 3, + 1 + ] + ], + "monsters": [ + [ + 2, + 5 + ], + [ + 4, + 1 ], [ 1, 4 ], [ - 1, - 1 + 0, + 0 ] ], - "par": 5 + "par": 6 }, { "level": 25, @@ -1081,31 +1313,39 @@ "rows": 7, "walls": [ [ - 0, - 3 + 3, + 2 ], [ 5, + 0 + ], + [ + 2, 4 ], [ 0, - 4 + 5 ], [ - 2, + 5, 2 + ], + [ + 5, + 6 ] ], "spikes": [ [ - 3, + 0, 3 ] ], "targets": [ [ - 4, + 3, 0 ], [ @@ -1119,20 +1359,20 @@ ], "monsters": [ [ - 4, + 2, 5 ], [ - 6, - 3 + 4, + 4 ], [ 3, - 1 + 0 ], [ 0, - 6 + 0 ] ], "par": 5 @@ -1143,46 +1383,40 @@ "rows": 7, "walls": [ [ - 4, - 0 - ], - [ - 0, - 2 - ], - [ - 2, + 1, 3 ], [ - 3, + 5, + 6 + ], + [ + 2, + 2 + ], + [ + 4, + 1 + ], + [ + 2, + 5 + ], + [ + 6, 1 ] ], "spikes": [ [ - 3, - 4 + 6, + 6 ] ], "targets": [ [ - 1, - 4 - ], - [ - 0, - 6 - ], - [ - 1, - 6 - ] - ], - "monsters": [ - [ - 0, - 4 + 3, + 0 ], [ 3, @@ -1190,11 +1424,25 @@ ], [ 4, - 1 + 2 + ] + ], + "monsters": [ + [ + 5, + 4 ], [ 1, - 5 + 0 + ], + [ + 1, + 2 + ], + [ + 4, + 2 ] ], "par": 5 @@ -1205,58 +1453,66 @@ "rows": 7, "walls": [ [ - 1, - 4 + 0, + 2 ], [ - 0, + 1, + 1 + ], + [ + 3, 6 ], [ - 5, - 3 + 6, + 0 ], [ - 2, + 3, 2 + ], + [ + 0, + 3 ] ], "spikes": [ [ - 4, - 1 + 1, + 4 ] ], "targets": [ - [ - 3, - 0 - ], - [ - 5, - 0 - ], - [ - 6, - 0 - ] - ], - "monsters": [ - [ - 6, - 2 - ], - [ - 3, - 6 - ], [ 2, 4 ], + [ + 2, + 5 + ], + [ + 2, + 6 + ] + ], + "monsters": [ [ 4, - 0 + 1 + ], + [ + 2, + 6 + ], + [ + 0, + 5 + ], + [ + 6, + 1 ] ], "par": 5 @@ -1267,58 +1523,66 @@ "rows": 7, "walls": [ [ - 1, - 2 - ], - [ - 2, - 5 - ], - [ - 1, + 3, 1 ], [ - 2, - 4 - ] - ], - "spikes": [ - [ - 2, - 6 - ] - ], - "targets": [ - [ - 5, - 3 - ], - [ - 5, + 1, 4 ], [ 6, + 3 + ], + [ + 3, + 2 + ], + [ + 4, + 0 + ], + [ + 5, + 3 + ] + ], + "spikes": [ + [ + 1, + 2 + ] + ], + "targets": [ + [ + 3, + 3 + ], + [ + 3, 4 + ], + [ + 3, + 5 ] ], "monsters": [ [ 4, - 6 + 5 ], [ - 0, + 2, 4 ], [ - 1, - 6 + 5, + 0 ], [ 6, - 6 + 1 ] ], "par": 5 @@ -1329,58 +1593,66 @@ "rows": 7, "walls": [ [ - 4, - 2 + 5, + 5 ], [ - 2, - 4 + 3, + 0 + ], + [ + 4, + 0 ], [ 5, - 3 + 6 ], [ 1, - 0 + 2 + ], + [ + 3, + 2 ] ], "spikes": [ [ - 0, - 0 + 3, + 5 ] ], "targets": [ [ - 1, + 4, 3 ], [ - 3, - 3 + 4, + 4 ], [ - 3, + 5, 4 ] ], "monsters": [ [ - 4, - 1 + 1, + 5 ], [ - 5, + 3, + 4 + ], + [ + 6, 0 ], [ 5, 4 - ], - [ - 0, - 3 ] ], "par": 5 @@ -1391,57 +1663,65 @@ "rows": 7, "walls": [ [ - 1, - 1 - ], - [ - 3, + 5, 5 ], [ - 2, - 4 + 0, + 6 ], [ - 4, - 4 - ] - ], - "spikes": [ + 6, + 6 + ], [ 0, 3 - ] - ], - "targets": [ + ], [ - 6, - 1 + 2, + 2 ], [ 5, 3 + ] + ], + "spikes": [ + [ + 5, + 2 + ] + ], + "targets": [ + [ + 3, + 0 ], [ - 6, + 3, + 2 + ], + [ + 3, 3 ] ], "monsters": [ - [ - 6, - 3 - ], [ 3, - 1 + 0 ], [ 4, - 3 + 1 ], [ - 0, + 5, + 6 + ], + [ + 1, 4 ] ], @@ -1453,61 +1733,69 @@ "rows": 7, "walls": [ [ - 1, - 4 - ], - [ - 4, + 3, 0 ], [ - 6, - 6 + 3, + 1 ], [ - 1, - 6 + 5, + 4 + ], + [ + 6, + 5 + ], + [ + 2, + 3 + ], + [ + 0, + 0 ] ], "spikes": [ [ - 3, - 5 + 4, + 1 ] ], "targets": [ [ 3, - 0 + 3 ], [ - 2, - 1 + 5, + 3 ], [ - 3, - 1 + 4, + 4 ] ], "monsters": [ - [ - 1, - 5 - ], [ 5, - 1 - ], - [ - 4, - 5 + 2 ], [ 1, 1 + ], + [ + 2, + 4 + ], + [ + 0, + 3 ] ], - "par": 5 + "par": 6 }, { "level": 32, @@ -1515,61 +1803,69 @@ "rows": 7, "walls": [ [ - 3, + 1, + 6 + ], + [ + 4, 2 ], [ - 2, - 6 + 0, + 5 ], [ - 5, + 0, + 2 + ], + [ + 6, 4 ], [ - 5, - 6 + 0, + 1 ] ], "spikes": [ [ - 4, - 3 + 3, + 0 ] ], "targets": [ + [ + 1, + 2 + ], [ 2, - 0 + 3 ], [ - 4, - 0 - ], - [ - 3, - 1 + 1, + 4 ] ], "monsters": [ [ 6, - 2 - ], - [ - 4, - 2 - ], - [ - 4, 5 ], [ - 2, + 5, + 6 + ], + [ + 6, 0 + ], + [ + 2, + 2 ] ], - "par": 5 + "par": 7 }, { "level": 33, @@ -1577,70 +1873,78 @@ "rows": 7, "walls": [ [ - 4, + 1, + 6 + ], + [ + 5, 3 ], [ 1, - 5 - ], - [ - 6, - 1 + 2 ], [ 0, - 5 + 6 + ], + [ + 4, + 0 + ], + [ + 0, + 4 ], [ 2, - 0 + 1 ] ], "spikes": [ [ 3, - 5 + 1 ], [ - 3, - 6 + 2, + 0 ] ], "targets": [ [ - 3, + 2, 2 ], [ - 3, - 4 + 4, + 2 ], [ - 4, - 5 + 2, + 3 ] ], "monsters": [ - [ - 2, - 5 - ], - [ - 4, - 1 - ], [ 6, 0 ], [ 0, - 0 + 2 ], [ 4, 5 + ], + [ + 2, + 2 + ], + [ + 2, + 6 ] ], "par": 7 @@ -1650,59 +1954,49 @@ "cols": 7, "rows": 7, "walls": [ - [ - 0, - 3 - ], - [ - 5, - 0 - ], - [ - 4, - 4 - ], [ 1, 2 ], [ - 6, - 5 - ] - ], - "spikes": [ - [ - 6, - 3 - ], - [ - 3, - 4 - ] - ], - "targets": [ - [ - 5, + 4, 2 ], [ - 4, - 3 + 3, + 5 ], [ - 5, + 1, + 0 + ], + [ + 4, + 0 + ], + [ + 0, + 2 + ], + [ + 6, 4 ] ], - "monsters": [ + "spikes": [ [ 3, 6 ], [ - 5, - 5 + 1, + 6 + ] + ], + "targets": [ + [ + 3, + 2 ], [ 3, @@ -1710,10 +2004,28 @@ ], [ 5, - 2 + 3 + ] + ], + "monsters": [ + [ + 5, + 1 ], [ - 0, + 4, + 6 + ], + [ + 3, + 0 + ], + [ + 6, + 0 + ], + [ + 2, 2 ] ], @@ -1724,25 +2036,33 @@ "cols": 7, "rows": 7, "walls": [ - [ - 6, - 1 - ], [ 5, - 6 + 2 ], [ 5, 3 ], [ - 0, - 2 + 2, + 3 + ], + [ + 4, + 6 + ], + [ + 5, + 1 ], [ 3, - 0 + 1 + ], + [ + 1, + 5 ] ], "spikes": [ @@ -1751,44 +2071,44 @@ 1 ], [ - 1, - 5 + 2, + 6 ] ], "targets": [ [ - 2, + 4, 0 ], [ - 1, - 2 + 4, + 1 ], [ - 3, + 4, 2 ] ], "monsters": [ [ - 5, - 2 - ], - [ - 4, + 6, 6 ], [ - 0, - 1 + 2, + 5 ], [ 3, - 2 + 3 ], [ - 6, - 6 + 4, + 1 + ], + [ + 5, + 0 ] ], "par": 7 @@ -1798,30 +2118,38 @@ "cols": 7, "rows": 7, "walls": [ + [ + 6, + 4 + ], + [ + 4, + 0 + ], + [ + 6, + 6 + ], + [ + 1, + 6 + ], + [ + 0, + 1 + ], [ 5, 3 ], [ 2, - 1 - ], - [ - 2, - 2 - ], - [ - 2, - 3 - ], - [ - 1, - 4 + 0 ] ], "spikes": [ [ - 6, + 3, 0 ], [ @@ -1831,38 +2159,38 @@ ], "targets": [ [ - 1, - 0 + 5, + 4 ], [ - 3, - 0 + 5, + 5 ], [ - 1, - 1 + 5, + 6 ] ], "monsters": [ + [ + 2, + 3 + ], [ 2, 6 ], [ 6, - 5 - ], - [ - 4, - 3 - ], - [ - 5, 2 ], [ - 2, - 0 + 5, + 1 + ], + [ + 5, + 4 ] ], "par": 7 @@ -1872,73 +2200,81 @@ "cols": 7, "rows": 7, "walls": [ + [ + 0, + 5 + ], [ 1, 6 ], [ - 1, - 4 + 5, + 6 ], [ 0, - 1 - ], - [ - 1, 2 ], [ - 3, + 1, 3 + ], + [ + 6, + 5 + ], + [ + 2, + 4 ] ], "spikes": [ [ - 2, - 6 + 1, + 1 ], [ - 2, - 3 + 6, + 6 ] ], "targets": [ - [ - 0, - 4 - ], - [ - 0, - 5 - ], - [ - 2, - 5 - ] - ], - "monsters": [ [ 5, 2 ], [ - 3, - 1 - ], - [ - 1, - 0 - ], - [ - 0, - 4 + 4, + 3 ], [ 5, 4 ] ], + "monsters": [ + [ + 4, + 0 + ], + [ + 0, + 3 + ], + [ + 2, + 0 + ], + [ + 5, + 4 + ], + [ + 5, + 2 + ] + ], "par": 8 }, { @@ -1946,71 +2282,79 @@ "cols": 7, "rows": 7, "walls": [ + [ + 5, + 5 + ], + [ + 4, + 1 + ], + [ + 6, + 2 + ], [ 2, - 6 + 4 ], [ 6, - 5 + 3 ], [ - 1, - 5 - ], - [ - 6, + 5, 0 ], [ - 0, - 0 + 2, + 3 ] ], "spikes": [ [ - 4, - 3 + 2, + 6 ], [ - 3, + 5, 1 ] ], "targets": [ [ 0, - 3 + 0 ], [ 1, - 4 + 1 ], [ 2, - 5 + 2 ] ], "monsters": [ + [ + 1, + 2 + ], [ 6, - 6 - ], - [ - 0, - 3 - ], - [ - 5, - 0 - ], - [ - 5, 5 ], [ - 6, - 4 + 4, + 3 + ], + [ + 3, + 2 + ], + [ + 0, + 0 ] ], "par": 8 @@ -2021,8 +2365,24 @@ "rows": 7, "walls": [ [ - 4, - 6 + 0, + 0 + ], + [ + 2, + 2 + ], + [ + 0, + 1 + ], + [ + 6, + 4 + ], + [ + 1, + 5 ], [ 5, @@ -2030,64 +2390,56 @@ ], [ 4, - 1 - ], - [ - 6, 5 - ], - [ - 1, - 1 ] ], "spikes": [ [ - 3, - 5 + 0, + 3 ], [ - 1, + 0, 2 ] ], "targets": [ [ - 0, - 0 + 4, + 4 + ], + [ + 5, + 5 + ], + [ + 5, + 6 + ] + ], + "monsters": [ + [ + 5, + 6 ], [ 2, 0 ], [ - 0, + 6, 1 - ] - ], - "monsters": [ + ], + [ + 6, + 3 + ], [ 1, 6 - ], - [ - 5, - 6 - ], - [ - 3, - 1 - ], - [ - 5, - 0 - ], - [ - 0, - 1 ] ], - "par": 8 + "par": 9 }, { "level": 40, @@ -2096,69 +2448,77 @@ "walls": [ [ 0, - 2 - ], - [ - 2, - 2 - ], - [ - 4, - 0 - ], - [ - 4, - 4 - ], - [ - 4, - 2 - ] - ], - "spikes": [ - [ - 3, - 6 - ], - [ - 6, - 3 - ] - ], - "targets": [ - [ - 3, - 0 - ], - [ - 4, 1 ], [ - 5, - 2 - ] - ], - "monsters": [ - [ - 2, - 5 - ], - [ - 3, - 2 + 1, + 3 ], [ 6, - 2 + 6 ], [ - 1, + 5, + 3 + ], + [ + 3, 2 ], [ 0, 3 + ], + [ + 4, + 6 + ] + ], + "spikes": [ + [ + 1, + 6 + ], + [ + 4, + 0 + ] + ], + "targets": [ + [ + 3, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 3 + ] + ], + "monsters": [ + [ + 5, + 0 + ], + [ + 3, + 4 + ], + [ + 5, + 6 + ], + [ + 3, + 0 + ], + [ + 1, + 5 ] ], "par": 10 diff --git a/public/src/games/puddingmonsters/PuddingMonstersGame.js b/public/src/games/puddingmonsters/PuddingMonstersGame.js index aff97b5..67dc568 100644 --- a/public/src/games/puddingmonsters/PuddingMonstersGame.js +++ b/public/src/games/puddingmonsters/PuddingMonstersGame.js @@ -126,7 +126,7 @@ export default class PuddingMonstersGame extends Phaser.Scene { const title = this.add.text(cx, 84, 'JELL-O MONSTERS', { fontFamily: 'Righteous', fontSize: '64px', color: COLORS.goldHex, }).setOrigin(0.5); - const sub = this.add.text(cx, 140, 'Flick the jellies to slide & stick. Merge them into one — finish on the yellow squares, in par, for ★★★.', { + const sub = this.add.text(cx, 140, 'Flick the jellies — only walls & friends stop a slide, the open edges drop you! Merge into one on the yellow squares, in par, for ★★★.', { fontFamily: '"Julius Sans One"', fontSize: '22px', color: COLORS.mutedHex, }).setOrigin(0.5); this.layer.add([title, sub]); @@ -300,9 +300,13 @@ export default class PuddingMonstersGame extends Phaser.Scene { const boardW = cols * this.cell; const boardH = rows * this.cell; + // Floating table: a thin slab over a drop shadow, not an enclosing rim — + // the edges are open and monsters slide right off them. const frame = this.add.graphics().setDepth(D.frame); + frame.fillStyle(0x000000, 0.4); + frame.fillRoundedRect(this.originX - 8, this.originY + 8, boardW + 16, boardH + 18, 18); frame.fillStyle(FRAME, 1); - frame.fillRoundedRect(this.originX - 18, this.originY - 18, boardW + 36, boardH + 36, 22); + frame.fillRoundedRect(this.originX - 7, this.originY - 7, boardW + 14, boardH + 14, 14); this.layer.add(frame); const floor = this.add.graphics().setDepth(D.floor); @@ -470,7 +474,7 @@ export default class PuddingMonstersGame extends Phaser.Scene { this.undoBtn = undo; this.layer.add([undo, reset, hint, levels]); - const tip = this.add.text(BTN_X, y + BTN_H + 26, 'Drag a monster to slide it\n(or arrow keys). Finish on the\nyellow squares, in par, for ★★★.', { + const tip = this.add.text(BTN_X, y + BTN_H + 26, 'Drag to flick (or arrow keys).\nOnly walls & monsters stop a\nslide — don\'t fall off the edge!\nYellow squares + par = ★★★.', { fontFamily: '"Julius Sans One"', fontSize: '16px', color: COLORS.mutedHex, align: 'center', }).setOrigin(0.5, 0).setDepth(D.ui); this.layer.add(tip); @@ -519,7 +523,7 @@ export default class PuddingMonstersGame extends Phaser.Scene { doFlick(cell, dir) { const idx = blobAt(this.state, cell.x, cell.y); if (idx < 0) return; - const { maxSteps, deathStep } = computeSlide(this.state, idx, dir); + const { maxSteps, deathStep, deathCause } = computeSlide(this.state, idx, dir); if (maxSteps === 0) { this.nudgeInvalid(idx); return; } this.busy = true; @@ -530,24 +534,26 @@ export default class PuddingMonstersGame extends Phaser.Scene { const movingCells = this.state.blobs[idx].cells.map((c) => c.slice()); const [dx, dy] = DIRS[dir]; const steps = deathStep > 0 ? deathStep : maxSteps; + // edge deaths overshoot the rim a little before the blob tips off + const animSteps = deathCause === 'edge' ? deathStep + 1.25 : steps; this.drawAllMonsters(idx); this.animGfx = this.add.graphics().setDepth(D.anim); this.layer.add(this.animGfx); const proxy = { v: 0 }; - const tgtX = dx * steps * this.cell; - const tgtY = dy * steps * this.cell; + const tgtX = dx * animSteps * this.cell; + const tgtY = dy * animSteps * this.cell; const drawAt = (t) => { this.animGfx.clear(); this.drawBlobInto(this.animGfx, movingCells, tgtX * t, tgtY * t); }; drawAt(0); playSound(this, SFX.PIECE_CLICK); this.tweens.add({ targets: proxy, v: 1, - duration: Math.min(420, 110 + steps * 55), + duration: Math.min(420, 110 + animSteps * 55), ease: deathStep > 0 ? 'Quad.easeIn' : 'Back.easeOut', onUpdate: () => drawAt(proxy.v), onComplete: () => { - if (deathStep > 0) this.splatAndFail(idx, dir); + if (deathStep > 0) this.splatAndFail(idx, dir, deathCause); else this.commitMove(idx, dir, movingCells, dx, dy, steps); }, }); @@ -602,17 +608,20 @@ export default class PuddingMonstersGame extends Phaser.Scene { }); } - splatAndFail(idx, dir) { + splatAndFail(idx, dir, cause) { slide(this.state, idx, dir); // marks state 'dead' (positions unchanged) playSound(this, SFX.SCIFI_EXPLODE); const g = this.animGfx; - if (g) { + if (!g) { this.onDead(cause); return; } + const done = () => { g.destroy(); if (this.animGfx === g) this.animGfx = null; this.onDead(cause); }; + if (cause === 'edge') { + // tumble off the table: drop and shrink while fading this.tweens.add({ - targets: g, alpha: 0, duration: 260, ease: 'Quad.easeIn', - onComplete: () => { g.destroy(); if (this.animGfx === g) this.animGfx = null; this.onDead(); }, + targets: g, alpha: 0, y: g.y + this.cell * 0.9, scaleX: 0.85, scaleY: 0.85, + duration: 340, ease: 'Quad.easeIn', onComplete: done, }); } else { - this.onDead(); + this.tweens.add({ targets: g, alpha: 0, duration: 260, ease: 'Quad.easeIn', onComplete: done }); } } @@ -669,7 +678,7 @@ export default class PuddingMonstersGame extends Phaser.Scene { // ── End states ────────────────────────────────────────────────────────────── - onDead() { + onDead(cause) { this.overlayUp = true; const cx = GAME_WIDTH / 2; const cy = GAME_HEIGHT / 2; @@ -683,12 +692,14 @@ export default class PuddingMonstersGame extends Phaser.Scene { panel.strokeRoundedRect(cx - 300, cy - 170, 600, 340, 20); this.layer.add(panel); - const title = this.add.text(cx, cy - 90, 'Splat!', { + const title = this.add.text(cx, cy - 90, cause === 'edge' ? 'Overboard!' : 'Splat!', { fontFamily: 'Righteous', fontSize: '64px', color: COLORS.dangerHex, }).setOrigin(0.5).setDepth(D.overlayUI); - const msg = this.add.text(cx, cy - 14, 'A monster slid onto the spikes. Try again!', { - fontFamily: '"Julius Sans One"', fontSize: '24px', color: COLORS.textHex, - }).setOrigin(0.5).setDepth(D.overlayUI); + const msg = this.add.text(cx, cy - 14, + cause === 'edge' ? 'A monster slid off the open edge. Try again!' + : 'A monster slid onto the spikes. Try again!', { + fontFamily: '"Julius Sans One"', fontSize: '24px', color: COLORS.textHex, + }).setOrigin(0.5).setDepth(D.overlayUI); this.layer.add([title, msg]); const retry = new Button(this, cx - 150, cy + 90, 'Retry', () => this.playLevel(this.level), diff --git a/public/src/games/puddingmonsters/PuddingMonstersLogic.js b/public/src/games/puddingmonsters/PuddingMonstersLogic.js index 81c68b3..6255d19 100644 --- a/public/src/games/puddingmonsters/PuddingMonstersLogic.js +++ b/public/src/games/puddingmonsters/PuddingMonstersLogic.js @@ -3,14 +3,15 @@ // // A level is a grid of cols x rows. Monsters are jelly blobs (each starts as one // cell). Flicking a blob slides it in a direction until any of its cells would -// leave the board, hit a WALL, or hit another blob — classic "ice slide". When a -// blob comes to rest orthogonally adjacent to another blob they STICK into one +// hit a WALL or another blob — classic "ice slide". The board edge does NOT +// stop a slide: the table edges are open, and a blob that reaches one slides +// off and falls (the run fails), faithful to the original game. When a blob +// comes to rest orthogonally adjacent to another blob they STICK into one // rigid blob. The level is solved when every monster has merged into a single // connected blob. // -// SPIKES are deadly: if a blob's slide path crosses a spike it dies (the run -// fails and the level must be restarted). STARS are bonus floor tiles collected -// when a monster covers them. +// SPIKES are equally deadly: if a blob's slide path crosses a spike it dies +// (the run fails and the level must be restarted). // // A blob: { cells: [[x,y,m], ...] } (rigid; moves as a unit). The third // element m is the index of the original monster occupying that cell; it rides @@ -90,8 +91,11 @@ export function targetsCovered(state, targets) { return n; } -// How far blob `idx` can slide in `dir`, and whether that path crosses a spike. -// Pure (does not mutate). { maxSteps, deathStep } — deathStep>0 means fatal. +// How far blob `idx` can slide in `dir`, and whether the slide is fatal. +// Only walls and other monsters stop a slide — the board edge is open, so a +// blob whose path crosses a spike or leaves the board dies. Pure (does not +// mutate). { maxSteps, deathStep, deathCause } — deathStep>0 means fatal, +// deathCause is 'spike' | 'edge' | null. export function computeSlide(state, idx, dir) { const [dx, dy] = DIRS[dir]; const blob = state.blobs[idx]; @@ -101,40 +105,45 @@ export function computeSlide(state, idx, dir) { if (i !== idx) b.cells.forEach(([x, y]) => other.add(key(x, y))); }); + // Slide until a wall or another blob blocks. Out-of-board cells block + // nothing (walls and blobs only exist on the board), so an unblocked blob + // runs clean off the table — the death scan below catches that. let maxSteps = 0; const limit = state.cols + state.rows; for (let s = 1; s <= limit; s++) { - let ok = true; + let blocked = false; for (const [x, y] of blob.cells) { - const nx = x + dx * s, ny = y + dy * s; - if (nx < 0 || ny < 0 || nx >= state.cols || ny >= state.rows) { ok = false; break; } - if (state.walls.has(key(nx, ny)) || other.has(key(nx, ny))) { ok = false; break; } + const k = key(x + dx * s, y + dy * s); + if (state.walls.has(k) || other.has(k)) { blocked = true; break; } } - if (!ok) break; + if (blocked) break; maxSteps = s; } let deathStep = 0; + let deathCause = null; for (let s = 1; s <= maxSteps && deathStep === 0; s++) { for (const [x, y] of blob.cells) { - if (state.spikes.has(key(x + dx * s, y + dy * s))) { deathStep = s; break; } + const nx = x + dx * s, ny = y + dy * s; + if (nx < 0 || ny < 0 || nx >= state.cols || ny >= state.rows) { deathStep = s; deathCause = 'edge'; break; } + if (state.spikes.has(key(nx, ny))) { deathStep = s; deathCause = 'spike'; break; } } } - return { maxSteps, deathStep }; + return { maxSteps, deathStep, deathCause }; } // Flick blob `idx` in `dir`. Mutates state. Returns: -// { moved:false } — couldn't move (no-op) -// { moved:true, dead:true, deathStep } — slid onto a spike (run fails) -// { moved:true, dead:false, merged, steps } — slid and (maybe) merged +// { moved:false } — couldn't move (no-op) +// { moved:true, dead:true, deathStep, deathCause } — hit a spike / fell off +// { moved:true, dead:false, merged, steps } — slid and (maybe) merged export function slide(state, idx, dir) { - const { maxSteps, deathStep } = computeSlide(state, idx, dir); + const { maxSteps, deathStep, deathCause } = computeSlide(state, idx, dir); if (maxSteps === 0) return { moved: false }; const [dx, dy] = DIRS[dir]; if (deathStep > 0) { state.state = 'dead'; - return { moved: true, dead: true, deathStep }; + return { moved: true, dead: true, deathStep, deathCause }; } const blob = state.blobs[idx]; diff --git a/server/scripts/genPuddingMonsters.js b/server/scripts/genPuddingMonsters.js index 4a01f19..9dea7dd 100644 --- a/server/scripts/genPuddingMonsters.js +++ b/server/scripts/genPuddingMonsters.js @@ -1,7 +1,9 @@ // Offline generator for Pudding Monsters levels. // // For each difficulty tier it random-fills a grid with walls, spikes and K -// monsters, runs the BFS solver to (a) reject unsolvable/trivial layouts and +// monsters (the board edge is open — only walls and monsters stop a slide, so +// every tier carries walls), runs the BFS solver to (a) reject +// unsolvable/trivial layouts and // (b) label each survivor with its minimum flick count (par), then marks 3 cells // of that solution's final footprint as yellow target squares and writes ordered // levels to public/data/puddingmonsters.json. Stars at play time require BOTH: @@ -39,14 +41,16 @@ const rng = makeRng(SEED); const randInt = (n) => Math.floor(rng() * n); // Difficulty curve. Ordered tiers ramp grid size, monsters and obstacles; each -// keeps `count` levels whose par falls in [minPar, maxPar]. Spikes appear only -// in later tiers (yield + difficulty). Levels are numbered tier-by-tier. +// keeps `count` levels whose par falls in [minPar, maxPar]. The board edge is +// OPEN (sliding off is fatal), so walls — the only terrain that stops a slide — +// appear from tier 1, like the original game. Spikes arrive in later tiers. +// Levels are numbered tier-by-tier. const TIERS = [ - { count: 8, cols: 5, rows: 5, monsters: 3, walls: 0, spikes: 0, minPar: 2, maxPar: 3 }, - { count: 8, cols: 6, rows: 6, monsters: 3, walls: 2, spikes: 0, minPar: 3, maxPar: 5 }, - { count: 8, cols: 6, rows: 6, monsters: 4, walls: 3, spikes: 0, minPar: 4, maxPar: 7 }, - { count: 8, cols: 7, rows: 7, monsters: 4, walls: 4, spikes: 1, minPar: 5, maxPar: 9 }, - { count: 8, cols: 7, rows: 7, monsters: 5, walls: 5, spikes: 2, minPar: 7, maxPar: 14 }, + { count: 8, cols: 5, rows: 5, monsters: 3, walls: 3, spikes: 0, minPar: 2, maxPar: 3 }, + { count: 8, cols: 6, rows: 6, monsters: 3, walls: 4, spikes: 0, minPar: 3, maxPar: 5 }, + { count: 8, cols: 6, rows: 6, monsters: 4, walls: 5, spikes: 0, minPar: 4, maxPar: 7 }, + { count: 8, cols: 7, rows: 7, monsters: 4, walls: 6, spikes: 1, minPar: 5, maxPar: 9 }, + { count: 8, cols: 7, rows: 7, monsters: 5, walls: 7, spikes: 2, minPar: 7, maxPar: 14 }, ]; const MAX_ATTEMPTS = 6000000; const MAX_SECONDS = 200;