[go: up one dir, main page]

Skip to footer navigation.

Oatmeal

A digital pillow fort

Broughlike dev log

I’ve been working on the broughlike pretty steadily since my last update. The gameplay loop is pretty much unchanged, but I’ve added a fair bit of polish, and fixed a lot of bugs. I think it is honestly sort of boring to play, but I am excited to have this as the starting point for future projects…can you smell the roguelike!? I can!

The major fixes and improvements that I’ve made since my last update include:

An interesting side-effect of working on a 6x6 grid is that it is about as small as any of my algorithms can support — if I go smaller to 4x4 they get wicked slow or totally crash. If I go bigger they stay about as useful, without any noticeable lag (though I haven’t tested for an upper limit to this).

I’ve also been experimenting with an auto-play algorithm, that, similar to the logic which controls the enemies, would have the player automatically navigate toward the exit, able to move around walls, engage enemies and collect items. So far that isn’t really working, though, so I’ll save that for the code-spelunkers among you dear readers.

Here’s the code I’m using to make sure that the player can always reach the exit, other enemies, and items.

// Checks to see if there's a path between any two points on the level
function isReachable(startX, startY, targetX, targetY) {
    const visited = Array(CONFIG.GRID_SIZE).fill().map(() => Array(CONFIG.GRID_SIZE).fill(false)); // Initialize a 2D array of false values
    function dfs(x, y) {
        if (x < 0 || x >= CONFIG.GRID_SIZE || y < 0 || y >= CONFIG.GRID_SIZE) return false; // Are the coordinates in bounds?
        if (visited[x][y]) return false;                                                    // Have we already visited this cell?
        if (walls.some(wall => wall.x === x && wall.y === y)) return false;                 // Is there a wall here?
        visited[x][y] = true;                                                               // Mark this cell as visited
        if (x === targetX && y === targetY) return true;                                    // Have we reached the target?
        return dfs(x + 1, y) || dfs(x - 1, y) || dfs(x, y + 1) || dfs(x, y - 1);            // Recursively check neighbors
    }
    return dfs(startX, startY);
}

Beyond little bits of game development here and there I’ve switched projects at work, been reading a bunch and generally enjoying the fall-flavored actives that happen this time of year…which is a little bit to say I’ve been playing ostrich.”

Broughlike

The Roguelike Celebration happened this weekend. Every year I think about participating, and every year I let it slip me by. In honor of it, though, this weekend I made a Broughlike…which I’ve creatively named Eli’s Broughlike.”

It runs in the browser. It should work on most anything with a keyboard, or with a touchscreen — the about page has some more details about how to play, though I’d suggest trying to play 1 or 2 rounds at least without reading the instructions. I think a subtle confusion about what is happening is a key part to the broughlike genre.

The enemy logic isn’t very sophisticated, and is likely where I’ll spend more time if I do return to this. I learned a lot about flood fill making this, which was rad.

If you play, I hope you have fun!

Sleepy garden beds

This afternoon I put the garden to sleep for the fall; in the past we’ve had some fall and winter vegetables going, but this year that didn’t happen, so, I emptied out the rain barrels, cleaned them out, trundled them to a place where they wouldn’t get blown around by any winds, mulched some of the beds, weeded and generally plotzed around like a garden goblin.

I’ve fallen into the habit of making a big thing of rice over the weekend — I always intend to do something with this rice, but instead I use it for serving upon serving of steamed veg in some sort of sauce or for making fried rice with. Neither is bad, to be totally honest. Perhaps better than what I would plan on, even. I’d also like to say that I’m pretty darn tootin’ good at making fried rice.

Since my last post, I feel like I’ve been trying to make up for spending so much time slogging through that Brandon Sanderson book. I’ve read a handful of really enjoyable books. Mercedes Lackey is perhaps the antithesis of Brandon Sanderson, so was a good starting point and The Amazing Adventures of Kavalier & Clay by Michael Chabon was delicious. I hadn’t read it before…but most delicious (and not only because of all the descriptions of food) has been The Light from Uncommon Stars by Ryka Aoki.

I’ve been noodling on a technical-ish blog post, or maybe wiki page about using the browser’s console to automate boring tasks, like having to manually click delete” 800 times in a terrible web application…I guess keep a look out for that one!? Similarly, I’ve started to scheme and plan for this year’s December Adventure.

A photograph of silhouetted trees in front of the northern lights. A large gravestone is barely visible in one corner of the image.

A photograph of silhouetted trees in front of the northern lights.

A photograph of silhouetted trees in front of the northern lights. They’re visible as sheets of pinks and reds.

A blurry photograph of a child in colorful overalls holding a mini golf club high over their head standing in front of a “life size” brachiosaurus.

Dinosaur golf before it shuts for the season.

Guitar driven development

I’ve found myself in possession of a guitar. Actually, the guitar that I had in middle school has come back to me after a decade’s long jaunt with someone else. I don’t really play guitar, but, I figured I should restring it and tune it.

I’m really very bad at tuning, so, rather than get good at that, or use any of the existing tools within reach of the internet to help me with that I made a thing. Tuner is a little web app that does 2 things: using a device’s microphone it listens for a primary frequency and displays what note that is, and it can play some reference tones, starting from middle C.

The most interesting bit of tuner is the code that detects the dominate frequency being input, and then maps that to a note. I script-monkeyed most of this together.

// Detect the dominant frequency and map it to a musical note
// <https://webaudio.github.io/web-audio-api/#the-analysernode-interface>
// <https://en.wikipedia.org/wiki/Autocorrelation>
function detectNote() {
    const freqArray = new Float32Array(analyzer.frequencyBinCount);
    analyzer.getFloatFrequencyData(freqArray);

    let maxAmp = -Infinity;
    let maxIndex = 0;

    for (let i = 0; i < freqArray.length; i++) {
        if (freqArray[i] > maxAmp) {
            maxAmp = freqArray[i];
            maxIndex = i;
        }
    }

    // Nyquist frequency is half the sample rate of a signal
    // <https://en.wikipedia.org/wiki/Nyquist_frequency>
    const nyquist = audioContext.sampleRate / 2;
    const frequency = maxIndex * nyquist / freqArray.length;

    const note = getNoteFromFrequency(frequency);
    noteDisplay.textContent = `Note: ${note}`;
}

// Convert frequency to musical note
function getNoteFromFrequency(frequency) {
    const notes = [
        'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'
    ];
    const A4 = 440;
    const semitoneRatio = Math.pow(2, 1 / 12);
    const noteIndex = Math.round(12 * Math.log2(frequency / A4));
    const note = notes[(noteIndex % 12 + 12) % 12];

    return frequency ? note : 'N/A';
}

Once the guitar was tuned, I figured I may as well make a metronome because while I am really very bad at tuning I’m even worse at keeping time. BP-sand is a metronome — you can enter a beats per minute count and it’ll beep at an appropriate interval. With each beep a grain of sand rains from the top of the screen. The sand piles up into sticky, branching stacks.

Next, I have to relearn how to play the guitar, I guess…

Other than these musically inclined adventures, autumn has come to Maine — we’ve been enjoying fall vibes, and soaking it all in before the Winter Drearies set in. Recent reading includes The Way of Kings by Brandon Sanderson (an author that I’m honestly ready to call it quits on…page turners for sure, but not for me these days) and The Algebraist by Iain M. Banks. Since finishing Arranger I haven’t picked up another video game, but I have fallen back in love with Puzzmo — pile up poker is 10/10. I think this winter me and Caves of Qud are gonna spend some quality time together. I’m really weirdly excited for it’s 1.0 release. I’m especially excited to explore Qud’s tutorial…I’ve played it for years, but haven’t ever really felt that I’ve actually known how to play it.

A photograph of a sleepy cat lounging in a sunny patch on a bed covered in a grey blanket.

Solar powered.

A relatively close up photograph of many yellow and white flowers in a meadow. The sky is blue and the image is processed to have a warmer hue. It is out of focus.

I had time to take a walk before running some errands and getting a flu shot and a Covid booster this morning.

Two kids in shorts and sweaters standing on a lumpy rock looking out over some Rosa Rugosa at the sea.

It is starting to smell like fall!

A playground for sharing scrappy fiddles

I shared some snippets of JavaScript in a recent blog post and was wicked irked that I didn’t have an easy way to share interactive code on my own thing…so…I made a totally static JavaScript playground for running little experiments and sharing scrappy fiddles!

It is pretty simple — it allows folks to enter and run JavaScript, includes a console so you can easily log things out without folks needing to open developer tools, and lets you share your code in two different ways. First, you can append the contents of the code editor (just a textarea) as a hash to the URL. If you share a URL that includes hashed contents those will automatically load when anyone visits that hash. Because that makes some wickedly long URLs, though, I also added the capability to download the entire website (a single html page) plus the contents of the textarea — this way you can either directly host your own version or share the entire html file with someone else.

I’m really pleased with how this turned out, and I’m excited to use this little playground a lot more. The piece I’m most pleased with is how I handle console.log; I didn’t want anyone to have to write a custom logging function, I wanted folks to be able to reliably use whatever the browser offers, so, I hijacked the in-built console.log function that we all know and love and then return it to you after the code has been run!

To learn about the playground’s secrets run the help function from the editor.


After the fact updates!

I updated the playground a bit since the initial post!

It now includes a few pre-made functions that make creating things a bit easier, including clear and mount functions. For more info, check out the newly improved help command. Here is a demo that shows off how to use the mount function

If you use this playground for anything, I’d love to see what you make!

Constants, variable assignment, and pointers

After reading my last post, a friend asked an interesting question that I thought would also be fun to write about!

They noted that in the reshape function I declared the variable result as a constant. They asked if this was a mistake, because I was resigning the value iteratively, shouldn’t it be declared using let?

What is happening there is that the constant is being declared as an array, so the reference to the array is constant…meaning that you can’t resign that variable to a new array or object. You can, however, fiddle with the contents of that array. Here, const makes the reference immutable, but the contents of the referenced array can be modified.

This is different than, say, C++, where if you declare an array as a constant with the keyword const, you cannot modify the contents.

const int arr[] = {1, 2, 3};
arr[0] = 5;  // Womp, womp, compilation error!

In C++, the const keyword sorta goes deeper,” and applies to the contents of the array, too, not only the reference, so the entire kit-and-caboodle is immutable. To achieve something like what I did in JavaScript in C++ with const you’d want a pointer to an array. This will allow you to modify the contents of the array, but keep the pointer immutable.

int arr[] = {1, 2, 3};
int* const p = arr;
p[0] = 5;  // Success! No compilation error! 

In this example, the pointer, p, is a constant, and immutable, but the contents it points to can change. Sorta like having a box bolted to the floor — you can’t move or change the box, but you can put all kinds of different stuff into the box as long as they fit!

Reshape, in JavaScript and APL

In APL the rho, , called reshape is used to both construct arrays of a given shape (dimensionality), and to reconfigure arrays into new shapes.

Sometimes I wish I had reshape in JavaScript…so I wrote it!

Here are two functions that, when combined, a la Captain Planet, can stand in for APLs reshape in JavaScript.

Ravel is the simpler of the two, it takes an array of any dimension and returns a new, one-dimensional array of the same data. Sorta like flatten from functional libraries like Ramda.js.

function ravel(array) {
  if (!Array.isArray(array)) return [array];
  return array.reduce((acc, val) => acc.concat(ravel(val)), []);
}

Reshape takes a vector describing the desired shape of a new array and an array to reformulate into that shape. The function will produce a new array with the specified shape and fill it by cycling through the elements of the input array. I think that this mimics APLs reshape, allowing you to reshape arrays with fill and cycling behavior.

function reshape(shape, array) {
  const totalSize = shape.reduce((acc, val) => acc * val, 1);
  const ravelledArray = ravel(array);
  const filledArray = [];

  for (let i = 0; i < totalSize; i++) {
    filledArray.push(ravelledArray[i % ravelledArray.length]);
  }

  function constructArray(shape, data, offset = 0) {
    if (shape.length === 1) {
      return data.slice(offset, offset + shape[0]);
    }

    const size = shape[0];
    const subShape = shape.slice(1);
    const subArraySize = subShape.reduce((acc, val) => acc * val, 1);

    const result = [];
    for (let i = 0; i < size; i++) {
      result.push(constructArray(subShape, data, offset + i * subArraySize));
    }
    
    return result;
  }

  return constructArray(shape, filledArray);
}

Here are some side-by-side tests of my JS and the same maneuvers in APL.

First, we reshape an array into an array of two nested arrays:

const array = [1, 2, 3, 4, 5];
const shape = [2, 3];
const reshapedArray = reshape(shape, array);
console.log(reshapedArray);
// [[1, 2, 3], [4, 5, 1]]

In APL:

2 3 ⍴ 1 2 3 4 5

Returns

1 2 3
4 5 1

Next a test case where we need to fill in a larger shape with repeating:

const array = [1, 2, 3];
const shape = [3, 3];
const reshapedArray = reshape(shape, array);
console.log(reshapedArray);
// [[1, 2, 3], [1, 2, 3], [1, 2, 3]]

In APL:

3 3 ⍴ 1 2 3

Returns

1 2 3
1 2 3
1 2 3

September summer

I finished reading Robin Sloan’s Moonbound today. It was fun, and light. The blurb likens it to Narnia, and, while a bold claim, I think that was a correct assertion, but more about the intended audience than the book’s subject matter. If a sequel is ever written I’d most certainly give it a look. It seems like a great gift book for a kid between like 8 and 15…or you know, perhaps, anyone who likes fun stories that aren’t scared of being joyful.

The book did do a thing that I don’t often like, which is write a story about the power of a story…it didn’t really lean too hard into that, so I will forgive that…this time.

Next up, following the theme of talking animals…Redwall? Or maybe The Mountain in the Sea, by Ray Nayler! I haven’t read that yet, so don’t really know what it is about, but lets assume a giant talking octopus for the time being!

After some biking about, and playground time with the kids today, I played some more Arranger. This game is becoming the star of this blog, it seems. The game recently started to signal that we’re nearing the final boss!? I may actually finish this game! I’m shocked and excited. Being able to play in bite-sized chunks has a huge breath of fresh air. I rarely have the attention span, nor eyeball powers to play a video game much longer than 45 - 60 minutes in a go.

I’ve also been playing a narrative game in the evenings before bed when I’d typically read a book or stare, listless, at the ceiling. Specifically, I’ve been playing The Ghost and the Golem. I’m still pretty early in, I think, but I’m reminded now of how much I enjoy playing narrative games, and this one seems especially tailored for me. If you have any recommendations for narrative games, I’d be really interested to learn about them.

A new page, still in progress, has appeared on the wiki. This one is being used to catalogue the parts of lil that I find particularly exciting.

Here are some drawings I made with the pixel art thing I mentioned in my last post.

A pixel art portrait, this one of a green guy with green hair and red eyes. The face exudes smugness.

A pixel art portrait of a person with grey skin and a pink nose wearing a black shirt, dark grey pants, and a black hat. They’re set in front of a rather blocky tree, and a purple background.

A pixel art portrait of a large-eyed, pointy-eared green fellow in a dark red shirt.

A pixel art portrait of a purple person with yellow eyes and shoulder-length green hair. They’re in a blue shirt, and in front of a red and light blue checked background.

You, and me, and the HTML5 canvas, pixel art, and quest logs

As we start to round out the summer I haven’t been reading as much, so I don’t have much to report on that front, but I have been keeping busy!

I made yet another pixel art drawing tool, pixel pixel pixel pixel pixel pixel allows folks to draw chonky pixel art creations on pretty much any sized canvas. This was fun to make. I’ve spent so much time with the HTML5 canvas lately that I’m really starting to feel like I get it, which is a fun realization! If you draw anything with pixel I’d love to see what you make!

Having made a fancy little menagerie of web things lately, I’ve been wondering if I can do anything more to unify their aesthetic — I have some subtle and secret rules that I use when designing them to help keep things consistent, but that is more to save me from having to make choices than it is to make them all feel related.

…but also, why would I need them to feel related when they almost all already live at the same URL?

My family recently started to play a game called SCHiM — I’ve been watching, mostly, and I’m really struck by it. A few things I am appreciating about the game:

That sort of quest log stuff has really been on my mind; after noticing how good the quest log is in Arranger in a previous post, being forever struck by how bad the Pokémon games handle this, and picking up Dragon Quest VI on the Gameboy after like…an eon? a millennium? a full epoch? away and being able to slip right back in I decided to make my own quest log…which isn’t in any way a todo list. I’ve been using it at work.

Two young children in swim things are next to each other in the surface. They’re jumping over a small, crashing wave. Above them an overcast sky stretches waaaaaay out to sea. There’s some sea weed out on the drying sand, because the tied is going out.

Home beach. Last day of vacation for me.

« Future Page 1 of 212 Past »