puzz.dennisc.net is out
My old puzzle page’s organization didn’t make much sense. It was separated into puzzle genres, like Sudoku, Star Battle, and Suguru. If you know anything about my puzzles, you’ll know that almost every puzzle I’ve published is a Sudoku in some shape or form. So in effect, all of my non-sudoku puzzles get buried, which makes no sense to do.
But at the same time, I still want to be able to filter for Sugurus. And this idea of filters, along with the bloated size of dennisc.net and astronomical build times, were the impetus for me creating a separate puzzle website. I would also frequently find myself copy and pasting the previous puzzle Markdown into the next puzzle, which seemed like a really bad idea when I could just generate the entire puzzle page from a series of configuration files.
So here’s how my new puzzle page works, more or less:
- Statically generate index.html and copy over all stylesheets/images.
- When a query string is sent, server-side generate the website instead. This is because there are so many combinations of query strings and I update the website often enough (i.e. I publish puzzles often enough) that pre-generating them all just doesn’t make sense.
What’s more, anyone can use and adapt my code for their own purposes. The source code is public and it is MIT licensed. It’s not quite the best thing for people inexperienced with computers to host (it wasn’t written to be), but if you have a domain and want to host your own puzzle website on my server feel free to ask.
This was quite the simple project. It took me just over a day’s worth of work from conceptualization to actually finishing, which is nice when I have a billion projects that are just almost done. It did take a bit of work to transfer all my puzzles over, but a couple of quick Rust scripts saved me a lot of work. Disclaimer: all these Rust scripts are shitty code and you should not try to emulate it. Like, there are very obvious mistakes/bad things I’m doing that I just never bothered to fix, since I’d only use these scripts for one very specific migration.
Directory creation/image transferring script
I ran this and then replaced .png
with
.svg
. Since all my images were either PNG or SVG files,
that copied all of them over.
use std::fs;
use std::path::Path;
fn main() -> Result<(), Box<dyn std::error::Error>> {
for file in fs::read_dir("/home/dennisc/dennisc.net/content/puzzles")? {
let path = file.as_ref().unwrap().path();
if path.is_dir() {
let name = path.file_name().unwrap();
fs::create_dir_all(Path::new("/home/dennisc/puzz-dennisc/puzzles").join(name)).unwrap();
fs::copy(path.join("puzzle.png"), format!("/home/dennisc/puzz-dennisc/puzzles/{}/puzzle.png", name.to_str().unwrap()));
}
}
Ok(())
}
Link formatting script
Previously I had .url
files (I made up the extension, it
doesn’t really mean anything) like ctc.url
. I wanted to
collect them all and copy the TOML-formatted [links]
item
instead of doing it by hand.
use std::path::Path;
use std::fs::read_to_string;
fn main() {
let ctc = if Path::new("ctc.url").exists() {
Path::new("ctc.url")
} else {
Path::new("sudokupad.url")
};
let fp = Path::new("f-puzzles.url");
let penpa = Path::new("penpa.url");
let octopuzzles = Path::new("octopuzzles.url");
println!("[links]");
println!("ctc = \"{}\"", read_to_string(ctc).unwrap().trim());
if let Ok(s) = read_to_string(octopuzzles) {
println!("octopuzzles = \"{}\"", s.trim());
}
if let Ok(s) = read_to_string(penpa) {
println!("penpa = \"{}\"", s.trim());
}
if let Ok(s) = read_to_string(fp) {
println!("fpuzzles = \"{}\"", s.trim());
}
}
You’ll notice that this script doesn’t work if I don’t have a CTC url (which I didn’t sometimes), but those were few enough that I didn’t feel like going back and fixing it.