⚠️⚠️⚠️

Penpa+ has disabled external scripts so these tricks no longer work on the default Penpa+. I now host my own penpa fork, dangerous-penpa-edit that will not be maintained but allows external scripts. Links on this page will send you to my fork, dangerous-penpa-edit.


I recommend checking out my previous article about Penpa+ hacks first.

Link to Maze

I’ve made another couple of cursed discoveries of what one can do with Penpa+. Thw two things I want to talk about in this post are a collaborative r/Place copy in Penpa+ and a Maze game. I don’t want to share the actual link for the Penpa+ Place to avoid griefing the canvas, but here’s a couple pictures of its development.

I wanted to make a post on how I made these two things.

Penpa+ Place

After Snake, the next thing I wanted to experiment with was multiplayer compatibility. I figured out how to load code from other sources which allowed me use Firebase’s Realtime Database, a free cloud-hosted database, which I’ve had experience using before. I read the surface elements the user inputs and add them to the database (there’s no authentication here, which is why I don’t want to make the original link public). I also have a listener which copies down any updates from the database back to the grid. I’ve also figured out how to attach code in Jekyll so you can see the source code here 🙂

Maze

Of course the natural thing to try to port into Penpa+ is Doom. This seemed very intimidating to me, so I wanted to get some practice working with 3d first (no promises on Doom ever happening though). I’ve always wanted to implement a raycaster, so I followed 3DSage’s Raycasting Tutorial. Most of the raycasting code is from this tutorial, with some modifications for Penpa+. Here’s my source code for the maze. I wanted to share some hacks/tips I discovered while creating this.

First, the win message. You don’t need answer checking mode to be enabled, but I keep it on so people aren’t jumpscared by the message & are expecting an end. The code is:

let message = document.getElementById("custom_message").value;
if (message == "" || message.includes("http-equiv=")) {
    message = Identity.solveDefaultMessage;
}
setTimeout(() => {
    Swal.fire({
        title: Identity.solveTitle ? "<h3 class=wish style=color:white>" + Identity.solveTitle + "</h3>" : undefined,
        html: "<h2 class=`wish` style=color:white>" + message + "</h2>",
        background: "url(js/images/new_year.jpg)",
        icon: "success",
        confirmButtonText: Identity.solveOKButtonText,
    })
}, 20);
sw_timer.pause();

I should probably specify that this code just makes the congratulations message appear; I’m not checking against any pre-loaded solution.

Secondly, grid resizing. By default, the minimum ‘display size’ (i.e., the size of each box) of the grid is 12 units and if you try setting it lower, it will give an error message. Even if you programatically change it, it won’t allow you to have a display size of less than 12! That is, unless you override the method that stops you.

Object.defineProperty(UserSettings, "displaysize", {
    set(newValue) {
        this._displaysize = newValue;
        redraw_grid();
    }
});

You can also dynamically resize your grid (the dimensions). I suspect this could lead to some puzzly ideas. See the maze source code for the full implementation.

Lastly, WASD/keyboard events. I added an handler for when keys were pressed/lifted:

window.onkeyup = function (e) {
    pressedKeys[e.keyCode] = false;
}
window.onkeydown = function (e) {
    pressedKeys[e.keyCode] = true;
}

then read from pressedKeys to tell of a key was pressed. A helpful website to get JS keycode info can be found here. For mobile support, I read where shaded cells are in the grid when the user drags on the board and determined which diagonal quadrant of the board the shaded cell was.

It’s still pretty easy to hack into the code and break it, but to disable the easiest way in, I removed the “resize grid” button: document.getElementById("newsize").remove();😉

Ending thoughts

Penpa+ has been really fun to play around with :D

(I think) I pretty much understand how the surface mode grid works, but I still have no idea how the other modes (ex. edges, walls, etc.) work in terms of indices, nor non-square grids. I might play around with these modes more or try to make some creative puzzles taking advantage of this ‘bug’.

I’ve seen a couple of Penpa+ hack creations that are actually puzzly, such as this Statue Park (go fast) by ft029 and Choco Banana (Lights Out) by lemononmars. I hope this inspires people to create some cool things!